import React, { useEffect, useState } from 'react';
import {
    Alert,
    Button,
    Container,
    Form,
    useToast,
} from '@theorchard/suite-components';
import { isEmpty, trim } from 'lodash-es';
import { useHistory, useParams } from 'react-router-dom';
import { PageHeader } from '../shared/pageHeader';
import { AbacusContractAdvanceStatus } from 'src/apollo/definitions/globalTypes';
import {
    NOT_PAID_ADVANCES_STATUS,
    PAID_ADVANCES_STATUS,
} from 'src/apollo/type-constants/advance';
import {
    useCreateContractAdvance,
    useUpdateContractAdvance,
} from 'src/apollo/mutations/contract-advance';
import { useContractById } from 'src/apollo/queries/contract';
import { useContractAdvanceById } from 'src/apollo/queries/contract-advance';
import { useGetExchangeRates } from 'src/apollo/queries/exchange-rates';
import { useStatementPeriodRecentPeriods } from 'src/apollo/queries/statement-periods';
import { LinkButton } from 'src/components/shared/link-button';
import {
    CONTRACT_ADVANCE_NOT_FOUND,
    DEFAULT_REFERENCE_PAYMENT_TYPE,
    NO_EXCHANGE_RATES_MSG,
    SPECIFIC_ADVANCE,
} from 'src/constants';
import { useContractBreadcrumbs } from 'src/hooks/breadcrumbs/contract';
import {
    getContractDetail,
    updateContractAdvance as updateContractAdvanceUrl,
} from 'src/urls/frontend-royalties';
import { amountFormatter } from 'src/utils/amount-helpers';
import { advanceFormValidation } from 'src/utils/form-validations/advance-form-validation';
import { AccountContractInformation } from './account-contract-information';
import { AdvanceFormFieldsCreation } from './advance-form-fields-creation';
import { AdvanceFormFieldsPayment } from './advance-form-fields-payment';
import type { CallbackFunction } from 'src/types/callback-function';
import type { GenericObject } from 'src/types/generic-object';
import BigNumber from 'bignumber.js';
import './styles.scss';

export const AdvanceForm: React.FC = () => {
    const { contractId, contractAdvanceId } = useParams<{
        contractId: string;
        contractAdvanceId?: string;
    }>();
    const history = useHistory();
    const [formData, setFormData] = useState<any>({ milestoneReached: 'no' });
    const [account, setAccount] = useState<any>({});
    const [isEditMode, setIsEditMode] = useState<boolean>(false);
    const [errors, setErrors] = useState<any>({});
    const [exchangeRate, setExchangeRate] = useState<number | null>(null);
    const [statementPeriodId, setStatementPeriodId] = useState<
        number | string | null
    >(null);
    const [isSaveButtonDisabled, setIsSaveButtonDisabled] =
        useState<boolean>(false);
    const [isAdvancePaymentConfirmed, setIsAdvancePaymentConfirmed] =
        useState<boolean>(false);
    const [showCurrencyWarning, setShowCurrencyWarning] =
        useState<boolean>(false);
    const { data } = useContractById(contractId);
    const pageTitle = isEditMode ? 'Contract Advance' : 'New Advance';
    const breadcrumbs = useContractBreadcrumbs(pageTitle);

    let advanceData: any = null;
    if (contractAdvanceId) {
        const result = useContractAdvanceById(contractAdvanceId);
        advanceData = result.data;
    }
    const createContractAdvance = useCreateContractAdvance(
        contractId,
        account.accountId,
        isAdvancePaymentConfirmed
    );
    const updateContractAdvance = useUpdateContractAdvance(
        contractAdvanceId,
        account.accountId,
        isAdvancePaymentConfirmed
    );
    const { data: statementPeriods, loading: statementPeriodsLoading } =
        useStatementPeriodRecentPeriods();

    const {
        getExchangeRatesList,
        data: exchangeRatesData,
        loading: exchangeRatesLoading,
    } = useGetExchangeRates();

    const addToast = useToast();

    if (
        !statementPeriodsLoading &&
        statementPeriods &&
        !exchangeRatesLoading &&
        !exchangeRatesData
    ) {
        const currentStatementPeriod =
            statementPeriods?.abacusStatementPeriods?.recentPeriods.find(
                (statementPeriod: any) =>
                    statementPeriod.statementPeriodStatus === 'current'
            );
        if (currentStatementPeriod) {
            setStatementPeriodId(currentStatementPeriod.statementPeriodId);
            getExchangeRatesList(currentStatementPeriod.statementPeriodId);
        }
    }

    const changeHandler = (e: any) => {
        const {
            target: { name },
        } = e;
        let {
            target: { value },
        } = e;
        const deleteFields = [
            'milestoneDate',
            'approvalConfirmation',
            'paymentConfirmation',
            'note',
        ];

        if (name === 'withholdingTaxAmount' && value) {
            const wht = value.replace(/[^0-9.,-]/g, '');
            value = parseFloat(wht) > 0 ? `-${wht}` : wht;
        }

        if (name === 'vatAmount' && value) {
            value = value.replace(/[^0-9.,]/g, '');
        }

        if (name === 'usSourceIncomeRate') {
            const usSourceIncomeRate = value.replace(/[^0-9.,]/g, '');
            value =
                parseFloat(usSourceIncomeRate) > 100 ? 100 : usSourceIncomeRate;
        }

        if (name === 'currencyCode') {
            const payeeCurrencyCode =
                data?.abacusContract?.account?.accountPaymentTerm?.currencyCode;
            if (payeeCurrencyCode && value) {
                setShowCurrencyWarning(payeeCurrencyCode !== value);
            } else {
                setShowCurrencyWarning(false);
            }
        }

        if (name === 'referencePaymentType') {
            delete formData.approvalConfirmation;
            delete formData.paymentConfirmation;
        }

        setErrors({});
        if (!value) {
            delete formData[name];

            if (name === 'approvalConfirmation' && !value) {
                delete formData.paymentConfirmation;
                delete formData.note;
            }
            if (name === 'paymentConfirmation' && !value) delete formData.note;

            setFormData({ ...formData });
            return;
        }

        if (name === 'milestoneReached' && value === 'no')
            deleteFields.forEach(key => delete formData[key]);

        setFormData({ ...formData, [name]: value });
    };

    const getAdvanceStatusQueryParam = (status: string): string => {
        if (NOT_PAID_ADVANCES_STATUS.includes(status)) return 'not-paid';
        if (PAID_ADVANCES_STATUS.includes(status)) return 'paid';
        return 'queued';
    };

    useEffect(() => {
        if (data) {
            const currencyCode =
                data.abacusContract?.account?.accountPaymentTerm?.currencyCode;
            const accountId = data.abacusContract?.account?.accountId;
            if (!contractAdvanceId) setFormData({ ...formData, currencyCode });
            setAccount({ accountId, currencyCode });
        }
    }, [data]);

    useEffect(() => {
        if (advanceData) {
            if (!advanceData?.abacusContractAdvance)
                return setErrors({
                    formSubmission: CONTRACT_ADVANCE_NOT_FOUND,
                });
            const newFormData = {
                ...formData,
                ...advanceData?.abacusContractAdvance,
            };
            newFormData.amount = amountFormatter(
                newFormData.amount,
                newFormData.currencyCode
            );
            newFormData.milestoneReached = newFormData.milestoneDate
                ? 'yes'
                : 'no';
            const isPaymentConfirmed =
                newFormData.advanceStatus === AbacusContractAdvanceStatus.paid;
            const isSaveButtonDisabled =
                isPaymentConfirmed ||
                newFormData.advanceStatus ===
                    AbacusContractAdvanceStatus.pending_payment ||
                newFormData.advanceStatus ===
                    AbacusContractAdvanceStatus.in_review;
            newFormData.approvalConfirmation =
                newFormData.advanceStatus ===
                    AbacusContractAdvanceStatus.approved || isPaymentConfirmed;
            newFormData.paymentConfirmation = isPaymentConfirmed;
            setIsSaveButtonDisabled(isSaveButtonDisabled);
            setFormData({ ...newFormData });
            setIsEditMode(true);
        }
    }, [advanceData]);

    useEffect(() => {
        if (exchangeRatesData) {
            const exchangeRates = exchangeRatesData?.abacusExchangeRates?.items;
            const selectedExchangeRate =
                exchangeRates &&
                exchangeRates.find(
                    exchangeRateData =>
                        exchangeRateData?.fromCurrencyCode ===
                            formData?.currencyCode &&
                        exchangeRateData?.toCurrencyCode ===
                            account?.currencyCode
                );

            const rate: any = selectedExchangeRate && selectedExchangeRate.rate;
            setExchangeRate(parseFloat(rate));
        }
    }, [exchangeRatesData, formData.currencyCode]);

    const defaultReferencePaymentTypeIsSelected =
        formData.referencePaymentType?.referencePaymentTypeId ===
        DEFAULT_REFERENCE_PAYMENT_TYPE.referencePaymentTypeId;

    const advanceEditLink = (
        advanceDescription: string,
        contractAdvanceId: number,
        isEditMode: boolean
    ) => (
        <div className="AdvanceForm-toast-success-msg">
            {isEditMode
                ? `Advance ${advanceDescription} have been updated successfully.`
                : `Advance ${advanceDescription} have been created successfully.`}
            <br />
            <a
                href={updateContractAdvanceUrl(contractId, contractAdvanceId)}
                rel="noreferrer"
            >
                View it here
            </a>
        </div>
    );

    const saveHandler = () => {
        const validationErrors = advanceFormValidation(formData);
        if (!isEmpty(validationErrors)) {
            setErrors(validationErrors);
            return false;
        }
        setErrors({});
        if (
            formData.currencyCode !== account?.currencyCode &&
            !exchangeRate &&
            formData?.paymentConfirmation
        ) {
            setErrors({ paymentConfirmation: NO_EXCHANGE_RATES_MSG });
            return false;
        }
        setIsSaveButtonDisabled(true);
        const {
            advanceDescription,
            amount,
            currencyCode,
            milestone,
            milestoneDescription,
            milestoneReached,
            milestoneDate,
            approvalConfirmation,
            paymentConfirmation,
            note,
            referencePaymentType,
            vatAmount,
            withholdingTaxAmount,
            usSourceIncomeRate,
        } = formData;

        let advanceStatus: AbacusContractAdvanceStatus =
            AbacusContractAdvanceStatus.not_qualified;
        if (milestoneReached === 'yes')
            if (approvalConfirmation && !paymentConfirmation)
                advanceStatus = AbacusContractAdvanceStatus.approved;
            else if (paymentConfirmation)
                advanceStatus = AbacusContractAdvanceStatus.paid;
            else advanceStatus = AbacusContractAdvanceStatus.qualified;

        let variables: any = {
            advanceDescription,
            advanceStatus,
            amount: Number(amount.replace(/,/g, '')),
            currencyCode,
            milestone,
            milestoneDate,
            milestoneDescription,
            note,
            referencePaymentTypeId:
                referencePaymentType?.referencePaymentTypeId,
            vatAmount: Number(vatAmount) === 0 ? 0 : Number(vatAmount) || null,
            withholdingTaxAmount:
                Number(withholdingTaxAmount) === 0
                    ? 0
                    : Number(withholdingTaxAmount) || null,
            usSourceIncomeRate:
                Number(usSourceIncomeRate) === 0
                    ? 0
                    : Number(usSourceIncomeRate) || null,
        };

        if (advanceStatus === AbacusContractAdvanceStatus.paid)
            variables = {
                ...variables,
                accountId: account?.accountId,
                statementPeriodId,
                accountPaymentCurrencyCode: account?.currencyCode,
                exchangeRate,
            };

        let methodToInvoke: CallbackFunction = createContractAdvance;
        if (isEditMode) {
            methodToInvoke = updateContractAdvance;
            variables = { ...variables, contractAdvanceId };
        } else variables = { ...variables, contractId };
        methodToInvoke({ variables })
            .then(({ data }: any) => {
                const newContractAdvanceId =
                    data?.abacusCreateContractAdvance?.contractAdvanceId ??
                    data?.abacusUpdateContractAdvance?.contractAdvanceId;
                const newContractAdvanceStatus =
                    data?.abacusCreateContractAdvance?.advanceStatus ??
                    data?.abacusUpdateContractAdvance?.advanceStatus;

                if (
                    defaultReferencePaymentTypeIsSelected &&
                    formData.milestoneReached === 'yes'
                ) {
                    const contractAdvanceIdQuery = contractAdvanceId
                        ? `&contractAdvanceId=${contractAdvanceId}`
                        : '';
                    const referencePaymentTypeIdQuery =
                        referencePaymentType.referencePaymentTypeId
                            ? `&referencePaymentTypeId=${referencePaymentType.referencePaymentTypeId}`
                            : '';
                    history.push(
                        `/payments/new?selectTab=${SPECIFIC_ADVANCE}&contractId=${contractId}${contractAdvanceIdQuery}${referencePaymentTypeIdQuery}`
                    );
                } else {
                    const queryParam = getAdvanceStatusQueryParam(
                        newContractAdvanceStatus
                    );
                    addToast(
                        advanceEditLink(
                            advanceDescription,
                            newContractAdvanceId,
                            isEditMode
                        ),
                        { variant: 'success' }
                    );

                    history.push(
                        `${getContractDetail(contractId)}?selectContractAdvance=${queryParam}`
                    );
                }
            })
            .catch((err: Error | GenericObject[]) => {
                setIsSaveButtonDisabled(false);
                setErrors({ formSubmission: JSON.stringify(err) });
            });
    };

    useEffect(() => {
        if (formData.paymentConfirmation) setIsAdvancePaymentConfirmed(true);
        else setIsAdvancePaymentConfirmed(false);
    }, [formData.paymentConfirmation]);

    const _amount = Number(trim(formData.amount));
    const _vatAmount = Number(trim(formData.vatAmount));
    const _withholdingTaxAmount = Number(trim(formData.withholdingTaxAmount));
    const amountAfterWithholdingAndVat = BigNumber(_amount)
        .plus(_vatAmount)
        .plus(_withholdingTaxAmount)
        .toNumber();

    const saveButtonText =
        defaultReferencePaymentTypeIsSelected &&
        formData.milestoneReached === 'yes'
            ? 'Save and create'
            : isEditMode
              ? 'Update'
              : 'Create';
    return (
        <div className="AdvanceForm">
            <PageHeader title={pageTitle} breadcrumbs={breadcrumbs} />
            {!isEmpty(errors.formSubmission) && (
                <Alert variant="error" text={errors.formSubmission} />
            )}
            <Container>
                <div className="AdvanceFormFields">
                    <AccountContractInformation
                        contractData={data}
                        advanceData={advanceData}
                    />
                    <Form validated>
                        <AdvanceFormFieldsCreation
                            changeHandler={changeHandler}
                            errors={errors}
                            formData={formData}
                            showCurrencyWarning={showCurrencyWarning}
                        />
                        <AdvanceFormFieldsPayment
                            changeHandler={changeHandler}
                            errors={errors}
                            formData={formData}
                            amountAfterWithholdingAndVat={
                                amountAfterWithholdingAndVat
                            }
                            defaultReferencePaymentTypeIsSelected={
                                defaultReferencePaymentTypeIsSelected
                            }
                        />
                    </Form>
                </div>
            </Container>

            <div className="container AdvanceForm-btn-wrapper">
                <LinkButton
                    buttonSize="md"
                    label="Cancel"
                    target={`${getContractDetail(contractId)}`}
                    variant="default"
                />
                <Button
                    className="save"
                    disabled={!account?.currencyCode || isSaveButtonDisabled}
                    onClick={saveHandler}
                    variant="primary"
                >
                    {saveButtonText}
                </Button>
            </div>
        </div>
    );
};
export default AdvanceForm;
