import React from 'react';
import { get } from 'lodash-es';
import {
    AbacusContractLifecycleStatus,
    AbacusContractLifecycleScheduleRenewalType,
    AbacusContractLifecycleSchedulePeriodType,
} from 'src/apollo/definitions/globalTypes';
import { CONTRACT_TYPES } from 'src/constants';
import {
    formatInterval,
    subtractInterval,
    isDateInPast,
    addInterval,
} from 'src/utils/date-helpers';
import type { GetContractQuery } from 'src/apollo/queries/contract/__generated__/get-contract';

export type AbacusContract = GetContractQuery['abacusContract'];

export const formatTerminationPeriod = (
    contract: AbacusContract,
    shouldCapitalize: boolean = true
) => {
    const terminationNoticeDetail = get(
        contract,
        'lifecycleSchedules[0].terminationNoticeDetail'
    );
    if (terminationNoticeDetail) {
        return formatInterval(
            terminationNoticeDetail.periodInterval,
            terminationNoticeDetail.periodType,
            shouldCapitalize
        );
    }
};

export const formatCollectionPeriod = (
    contract: AbacusContract,
    shouldCapitalize: boolean = true
) => {
    const collectionPeriodDetail = get(
        contract,
        'lifecycleSchedules[0].collectionPeriodDetail'
    );
    if (collectionPeriodDetail) {
        return formatInterval(
            collectionPeriodDetail.periodInterval,
            collectionPeriodDetail.periodType,
            shouldCapitalize
        );
    }
};

const getTerminationNoticeDate = (contract: AbacusContract) => {
    const termEndDate = contract?.lifecycle?.lifecycleTermEnd;
    const terminationNoticeDetail = get(
        contract,
        'lifecycleSchedules[0].terminationNoticeDetail'
    );
    if (termEndDate && terminationNoticeDetail) {
        return subtractInterval(
            termEndDate,
            terminationNoticeDetail.periodInterval,
            terminationNoticeDetail.periodType
        );
    }
};

const getSubsequentPeriodEndDate = (contract: AbacusContract) => {
    const termEndDate = contract?.lifecycle?.lifecycleTermEnd;
    const renewalOffsetDetail = get(
        contract,
        'lifecycleSchedules[1].renewalOffsetDetail'
    );
    if (termEndDate && renewalOffsetDetail) {
        return addInterval(
            termEndDate,
            renewalOffsetDetail.periodInterval,
            renewalOffsetDetail.periodType
        );
    }
};

const getCollectionPeriodEndDate = (contract: AbacusContract) => {
    const termEndDate = contract?.lifecycle?.lifecycleTermEnd;
    const collectionPeriodDetail = get(
        contract,
        'lifecycleSchedules[0].collectionPeriodDetail'
    );
    if (termEndDate && collectionPeriodDetail) {
        return addInterval(
            termEndDate,
            collectionPeriodDetail.periodInterval,
            collectionPeriodDetail.periodType
        );
    }
};

export interface RenewalDescriptionProps {
    contractType: string;
    contract: AbacusContract;
}

export const RenewalDescription: React.FC<RenewalDescriptionProps> = ({
    contract,
    contractType,
}) => {
    const lifecycle = contract?.lifecycle;
    const currentSchedule = get(contract, 'lifecycleSchedules[0]');
    const terminationNoticeDate = getTerminationNoticeDate(contract);

    if (!lifecycle) return null;

    const currentPeriodStartDate = lifecycle.lifecycleTermStart;
    const currentPeriodEndDate = lifecycle.lifecycleTermEnd;
    const lastRenewed = lifecycle.lastRenewed;
    const terminationPeriod = formatTerminationPeriod(contract, false);
    const nextSchedule = get(contract, 'lifecycleSchedules[1]');
    const nextPeriodStartDate = addInterval(
        currentPeriodEndDate,
        1,
        AbacusContractLifecycleSchedulePeriodType.DAY
    );
    const nextPeriodEndDate = getSubsequentPeriodEndDate(contract);
    const LastRenewed = () => {
        if (!lastRenewed) return null;
        return (
            <p>
                This contract was last renewed on <b>{lastRenewed}</b>.
            </p>
        );
    };

    switch (lifecycle.lifecycleStatus) {
        case AbacusContractLifecycleStatus.TO_BE_TERMINATED:
            if (!currentPeriodEndDate) return null;

            if (contractType === CONTRACT_TYPES.NEIGHBOURING_RIGHTS) {
                const collectionPeriodEndDate =
                    getCollectionPeriodEndDate(contract);
                if (collectionPeriodEndDate) {
                    const dayAfterCurrentPeriodEndDate = addInterval(
                        currentPeriodEndDate,
                        1,
                        AbacusContractLifecycleSchedulePeriodType.DAY
                    );
                    return (
                        <>
                            This contract is set to enter a collection period on{' '}
                            <b>{dayAfterCurrentPeriodEndDate}</b>. The
                            collection period will end on{' '}
                            <b>{collectionPeriodEndDate}</b>.
                        </>
                    );
                } else {
                    return (
                        <>
                            This contract is set to terminate on{' '}
                            <b>{currentPeriodEndDate}</b>. It will not enter a
                            collection period.
                        </>
                    );
                }
            } else {
                return (
                    <>
                        This contract is set to terminate on{' '}
                        <b>{currentPeriodEndDate}</b>.
                    </>
                );
            }
        case AbacusContractLifecycleStatus.ACTIVE:
            if (!currentSchedule) return null;

            switch (currentSchedule.renewalType) {
                case AbacusContractLifecycleScheduleRenewalType.CONTINUOUSLY_ACTIVE:
                    if (!terminationPeriod || !terminationNoticeDate) {
                        return (
                            <>
                                This contract is set to be continuously active.
                                It will not terminate or renew unless a contract
                                admin edits this contract.
                                <LastRenewed />
                            </>
                        );
                    }
                    return (
                        <>
                            This contract is set to be continuously active. It
                            will not terminate or renew unless a contract admin
                            edits this contract.
                            <br />
                            The client can choose to terminate this contract at
                            any point with <b>{terminationPeriod}</b> notice.
                            <LastRenewed />
                        </>
                    );
                case AbacusContractLifecycleScheduleRenewalType.RENEW_PERIODICALLY:
                case AbacusContractLifecycleScheduleRenewalType.RENEW_AFTER_CERTAIN_DATE:
                    if (
                        !terminationNoticeDate ||
                        !currentPeriodEndDate ||
                        !nextSchedule ||
                        !nextPeriodStartDate
                    )
                        return null;

                    switch (nextSchedule.renewalType) {
                        case AbacusContractLifecycleScheduleRenewalType.RENEW_PERIODICALLY:
                            if (!nextPeriodEndDate) return null;

                            if (isDateInPast(terminationNoticeDate)) {
                                return (
                                    <>
                                        This contract is set to renew on{' '}
                                        <b>{currentPeriodEndDate}</b>. When it
                                        renews, the new period start date will
                                        be <b>{nextPeriodStartDate}</b>.<br />
                                        The contract will renew again on{' '}
                                        <b>{nextPeriodEndDate}</b>.<br />
                                        The user is past the day (
                                        <b>{terminationNoticeDate}</b>) that
                                        they should have given notice if they
                                        wanted to terminate this contract.
                                        <LastRenewed />
                                    </>
                                );
                            } else {
                                return (
                                    <>
                                        This contract is set to renew on{' '}
                                        <b>{currentPeriodEndDate}</b>. When it
                                        renews, the new period start date will
                                        be <b>{nextPeriodStartDate}</b>.<br />
                                        The contract will renew again on{' '}
                                        <b>{nextPeriodEndDate}</b>.<br />
                                        The user can choose to cancel the next
                                        renewal at any point up until{' '}
                                        <b>{terminationNoticeDate}</b>.
                                        <LastRenewed />
                                    </>
                                );
                            }
                        case AbacusContractLifecycleScheduleRenewalType.CONTINUOUSLY_ACTIVE:
                            if (isDateInPast(terminationNoticeDate)) {
                                return (
                                    <>
                                        This contract is set to renew on{' '}
                                        <b>{currentPeriodEndDate}</b>. When it
                                        renews, the new period start date will
                                        be <b>{nextPeriodStartDate}</b>.<br />
                                        The contract will be continuously active
                                        from then on.
                                        <br />
                                        The user is past the day (
                                        <b>{terminationNoticeDate}</b>) that
                                        they should have given notice if they
                                        wanted to terminate this contract.
                                        <LastRenewed />
                                    </>
                                );
                            } else {
                                return (
                                    <>
                                        This contract is set to renew on{' '}
                                        <b>{currentPeriodEndDate}</b>. When it
                                        renews, the new period start date will
                                        be <b>{nextPeriodStartDate}</b>.<br />
                                        The contract will be continuously active
                                        from then on. The user can choose to
                                        cancel the next renewal at any point up
                                        until <b>{terminationNoticeDate}</b>.
                                        <LastRenewed />
                                    </>
                                );
                            }
                        default:
                            return null;
                    }
                default:
                    return null;
            }
        case AbacusContractLifecycleStatus.INIT:
            return (
                <>
                    This contract is not yet active and is not included in the
                    accounting run.
                    <br />
                    It will become active and start being included in the run on{' '}
                    <b>{currentPeriodStartDate}</b>.
                </>
            );
        default:
            // Don't display anything for these inactive, terminated, or in collection period statuses
            return null;
    }
};
