import { ClubOfferData, CreateLinkEvent, PurchaseFromCatalogComposer } from '@octane/renderer';
import { FC, useCallback, useMemo, useRef, useState } from 'react';
import {
    CatalogPurchaseState,
    DispatchUiEvent,
    GetConfigurationValue,
    LocalizeText,
    OpenUrl,
    SanitizeHtml,
    SendMessageComposer,
    ShowVipBenefits
} from '../../../../../api';
import vipIconMedium from '../../../../../assets/images/catalog/air/vip-icon-medium.png';
import { Button, LayoutCurrencyIcon, LayoutLoadingSpinnerView } from '../../../../../common';
import { BaseButton } from '../../../../../common/BaseButton';
import { NativeTextTree } from '../../../../../common/native-text/NativeTextTree';
import {
    CatalogEvent,
    CatalogInitGiftEvent,
    CatalogPurchasedEvent,
    CatalogPurchaseFailureEvent,
    CatalogPurchaseNotAllowedEvent,
    CatalogPurchaseSoldOutEvent
} from '../../../../../events';
import {
    useCatalogActions,
    useCatalogData,
    useCatalogSkipPurchaseConfirmation,
    useCatalogUiState,
    useClubOffers,
    useGiftConfiguration,
    useNotification,
    usePurse,
    useUiEvent
} from '../../../../../hooks';
import { CatalogClubPurchaseConfirmView } from './CatalogClubPurchaseConfirmView';
import { CatalogLayoutProps } from './CatalogLayout.types';
import { clubOffersWindowId, getClubMembershipSummary, groupClubOffers, isVipPurchaseLayout } from './clubPurchase.helpers';

export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = ({ page = null }) => {
    const [pendingOffer, setPendingOffer] = useState<ClubOfferData | null>(null);
    const [purchaseWillBeGift, setPurchaseWillBeGift] = useState(false);
    const [purchaseState, setPurchaseState] = useState(CatalogPurchaseState.NONE);
    const [catalogSkipPurchaseConfirmation] = useCatalogSkipPurchaseConfirmation();
    const { currentPage = null } = useCatalogData();
    const { resetPlacedOfferData = null } = useCatalogActions();
    const { giftReceiver = null } = useCatalogUiState();
    const { data: giftConfiguration = null } = useGiftConfiguration();
    const { purse = null, getCurrencyAmount = null } = usePurse();
    const { showConfirm = null, simpleAlert = null } = useNotification();
    const isPurchasingRef = useRef(false);
    const pageData = currentPage ?? page;
    const layoutCode = pageData?.layoutCode ?? 'club_buy';
    const isVipPage = isVipPurchaseLayout(layoutCode);
    const { data: offers = null } = useClubOffers(clubOffersWindowId(layoutCode));
    const offerGroups = useMemo(() => groupClubOffers(layoutCode, offers ?? []), [layoutCode, offers]);
    const membership = useMemo(() => getClubMembershipSummary(purse), [purse]);

    const onCatalogEvent = useCallback((event: CatalogEvent) => {
        if (!isPurchasingRef.current) return;

        switch (event.type) {
            case CatalogPurchasedEvent.PURCHASE_SUCCESS:
                isPurchasingRef.current = false;
                setPurchaseWillBeGift(false);
                setPurchaseState(CatalogPurchaseState.NONE);
                setPendingOffer(null);
                return;
            // A refusal of any kind ends the purchase: "not allowed" and "sold out" used to leave it pending forever.
            case CatalogPurchaseFailureEvent.PURCHASE_FAILED:
            case CatalogPurchaseNotAllowedEvent.NOT_ALLOWED:
            case CatalogPurchaseSoldOutEvent.SOLD_OUT:
                isPurchasingRef.current = false;
                setPurchaseWillBeGift(false);
                setPurchaseState(CatalogPurchaseState.NONE);
                setPendingOffer(null);
                return;
        }
    }, []);

    useUiEvent(CatalogPurchasedEvent.PURCHASE_SUCCESS, onCatalogEvent);
    useUiEvent(CatalogPurchaseFailureEvent.PURCHASE_FAILED, onCatalogEvent);
    useUiEvent(CatalogPurchaseNotAllowedEvent.NOT_ALLOWED, onCatalogEvent);
    useUiEvent(CatalogPurchaseSoldOutEvent.SOLD_OUT, onCatalogEvent);

    const getOfferText = useCallback(
        (offer: ClubOfferData) => {
            if (!isVipPage) return LocalizeText('catalog.club.item.header', ['months'], [offer.months.toString()]);

            if (offer.months > 0) {
                return LocalizeText('catalog.vip.item.header.months', ['num_months'], [offer.months.toString()]);
            }

            return LocalizeText('catalog.vip.item.header.days', ['num_days'], [offer.extraDays.toString()]);
        },
        [isVipPage]
    );

    const getPurchaseHeader = useCallback(
        (offer: ClubOfferData) => {
            const extensionOrSubscription = membership.tier === 'vip' ? 'extension.' : 'subscription.';
            const daysOrMonths = offer.months === 0 ? 'days' : 'months';
            const value = offer.months === 0 ? offer.extraDays : offer.months;

            return LocalizeText(`catalog.vip.buy.confirm.${extensionOrSubscription}${daysOrMonths}`, [`num_${daysOrMonths}`], [value.toString()]);
        },
        [membership.tier]
    );

    const getPurchaseValidUntil = useCallback((offer: ClubOfferData) => {
        return LocalizeText(
            'catalog.vip.buy.confirm.end_date',
            ['day', 'month', 'year'],
            [offer.day.toString(), offer.month.toString(), offer.year.toString()]
        );
    }, []);

    const getActivityPointName = useCallback((type: number) => {
        const fallbackLocalization = type === 0 ? 'tooltip.duckets' : type === 5 ? 'tooltip.diamonds' : '';
        const currencyLocalization = GetConfigurationValue<string>(`activitypoint.name.${type}`, fallbackLocalization);

        return currencyLocalization ? LocalizeText(currencyLocalization) : `#${type}`;
    }, []);

    const showInsufficientBalanceAlert = useCallback(
        (offer: ClubOfferData) => {
            if (!getCurrencyAmount) return false;

            if (offer.priceCredits > getCurrencyAmount(-1)) {
                const description = LocalizeText('catalog.alert.notenough.credits.description');
                const title = LocalizeText('catalog.alert.notenough.title');

                if (showConfirm) {
                    const settle = () => resetPlacedOfferData?.();

                    showConfirm(
                        description,
                        () => {
                            settle();

                            const shopUrl = GetConfigurationValue<string>('web.shop.relativeUrl', '');

                            if (shopUrl) OpenUrl(shopUrl);
                        },
                        settle,
                        null,
                        null,
                        title
                    );
                } else {
                    simpleAlert?.(description, null, null, null, title);
                }

                return true;
            }

            if (offer.priceActivityPoints > getCurrencyAmount(offer.priceActivityPointsType)) {
                const currencyLocalization = GetConfigurationValue<string>(
                    `activitypoint.name.${offer.priceActivityPointsType}`,
                    offer.priceActivityPointsType === 0 ? 'tooltip.duckets' : ''
                );

                if (currencyLocalization) {
                    const currencyName = LocalizeText(currencyLocalization);
                    const description = LocalizeText('catalog.alert.notenough.activitypoints.description', ['currencyname'], [currencyName]);
                    const title = LocalizeText('catalog.alert.notenough.activitypoints.title', ['currencyname'], [currencyName]);

                    if (offer.priceActivityPointsType === 0 && showConfirm) {
                        const settle = () => resetPlacedOfferData?.();

                        showConfirm(
                            description,
                            () => {
                                settle();

                                const ducketsUrl = GetConfigurationValue<string>('link.format.duckets', '');

                                if (ducketsUrl) OpenUrl(ducketsUrl);
                            },
                            settle,
                            null,
                            null,
                            title
                        );
                    } else {
                        simpleAlert?.(description, null, null, null, title);
                    }
                } else {
                    simpleAlert?.(
                        LocalizeText('catalog.alert.notenough.activitypoints.description'),
                        null,
                        null,
                        null,
                        LocalizeText(`catalog.alert.notenough.activitypoints.title.${offer.priceActivityPointsType}`)
                    );
                }

                return true;
            }

            return false;
        },
        [getCurrencyAmount, resetPlacedOfferData, showConfirm, simpleAlert]
    );

    const submitPurchase = useCallback(
        (offer: ClubOfferData) => {
            if (!pageData || isPurchasingRef.current) return;

            if (showInsufficientBalanceAlert(offer)) {
                setPendingOffer(null);
                setPurchaseState(CatalogPurchaseState.NONE);

                return;
            }

            isPurchasingRef.current = true;
            setPurchaseWillBeGift(false);
            setPendingOffer(offer);
            setPurchaseState(CatalogPurchaseState.PURCHASE);
            SendMessageComposer(new PurchaseFromCatalogComposer(pageData.pageId, offer.offerId, null, 1));
        },
        [pageData, showInsufficientBalanceAlert]
    );

    const startPurchase = useCallback(
        (offer: ClubOfferData) => {
            if (isPurchasingRef.current) return;

            if (showInsufficientBalanceAlert(offer)) return;

            if (catalogSkipPurchaseConfirmation) {
                submitPurchase(offer);
                return;
            }

            setPurchaseWillBeGift(false);
            setPendingOffer(offer);
            setPurchaseState(CatalogPurchaseState.CONFIRM);
        },
        [catalogSkipPurchaseConfirmation, showInsufficientBalanceAlert, submitPurchase]
    );

    const startGift = useCallback(
        (offer: ClubOfferData) => {
            if (!pageData || isPurchasingRef.current || !offer.giftable) return;
            if (showInsufficientBalanceAlert(offer)) return;

            setPurchaseWillBeGift(true);
            setPendingOffer(offer);
            setPurchaseState(CatalogPurchaseState.CONFIRM);
        },
        [pageData, showInsufficientBalanceAlert]
    );

    const confirmPendingOffer = useCallback(() => {
        if (!pendingOffer) return;

        if (purchaseWillBeGift) {
            if (!pageData) return;

            DispatchUiEvent(new CatalogInitGiftEvent(pageData.pageId, pendingOffer.offerId, '', giftReceiver ?? ''));
            setPurchaseWillBeGift(false);
            setPendingOffer(null);
            setPurchaseState(CatalogPurchaseState.NONE);
            return;
        }

        submitPurchase(pendingOffer);
    }, [giftReceiver, pageData, pendingOffer, purchaseWillBeGift, submitPurchase]);

    const renderPrice = (offer: ClubOfferData) => {
        if (!isVipPage) {
            return (
                <NativeTextTree>
                    <span className="octane-club-offer-prices is-compact-text">
                        {offer.priceCredits > 0 && LocalizeText('catalog.club.price', ['price'], [offer.priceCredits.toString()])}
                        {offer.priceCredits > 0 && offer.priceActivityPoints > 0 && ' + '}
                        {offer.priceActivityPoints > 0 && `${offer.priceActivityPoints} ${getActivityPointName(offer.priceActivityPointsType)}`}
                    </span>
                </NativeTextTree>
            );
        }

        return (
            <NativeTextTree>
                <span className="octane-club-offer-prices">
                    {offer.priceCredits > 0 && (
                        <span className="octane-club-offer-price" data-currency-type="-1">
                            <span>{offer.priceCredits}</span>
                            <LayoutCurrencyIcon type={-1} />
                        </span>
                    )}
                    {offer.priceActivityPoints > 0 && (
                        <>
                            {offer.priceCredits > 0 && <span className="octane-club-price-separator">+</span>}
                            <span className="octane-club-offer-price" data-currency-type={offer.priceActivityPointsType}>
                                <span>{offer.priceActivityPoints}</span>
                                <LayoutCurrencyIcon type={offer.priceActivityPointsType} />
                            </span>
                        </>
                    )}
                </span>
            </NativeTextTree>
        );
    };

    const renderOffer = (offer: ClubOfferData) => {
        const isPending = pendingOffer?.offerId === offer.offerId;

        return (
            <article
                key={offer.offerId}
                className={`octane-club-offer ${isVipPage ? 'is-wide' : 'is-compact'} ${offer.vip ? 'is-vip' : 'is-hc'}`}
                data-offer-id={offer.offerId}
            >
                <header className="octane-club-offer-header">
                    {isVipPage ? (
                        <img alt="" aria-hidden="true" className="octane-club-vip-medium-mark" draggable={false} src={vipIconMedium} />
                    ) : (
                        <span aria-hidden="true" className={`octane-club-compact-mark ${offer.vip ? 'is-vip' : 'is-hc'}`} />
                    )}
                    <strong>{getOfferText(offer)}</strong>
                </header>
                <div className="octane-club-offer-footer">
                    {renderPrice(offer)}
                    <div className="octane-club-offer-actions">
                        {isVipPage && giftConfiguration?.isEnabled && offer.giftable && (
                            <Button classNames={['octane-club-offer-action']} disabled={isPurchasingRef.current} onClick={() => startGift(offer)}>
                                {LocalizeText('catalog.purchase_confirmation.gift')}
                            </Button>
                        )}
                        <Button classNames={['octane-club-offer-action', 'is-buy']} disabled={isPurchasingRef.current} onClick={() => startPurchase(offer)}>
                            {isPending && purchaseState === CatalogPurchaseState.PURCHASE ? (
                                <LayoutLoadingSpinnerView />
                            ) : (
                                LocalizeText('catalog.club.button.buy')
                            )}
                        </Button>
                    </div>
                </div>
            </article>
        );
    };

    const membershipHeaderKey = {
        hc: 'catalog.club.buy.header.hc',
        none: 'catalog.club.buy.header.none',
        vip: 'catalog.club.buy.header.vip'
    }[membership.tier];
    const membershipInfoKey = {
        hc: 'catalog.club.buy.info.hc',
        none: 'catalog.club.buy.info.none',
        vip: 'catalog.club.buy.info.vip'
    }[membership.tier];
    const remainingKey = membership.tier === 'vip' ? 'catalog.club.buy.remaining.vip' : 'catalog.club.buy.remaining.hc';
    const teaserImage = pageData?.localization.getImage(1) ?? '';
    const vipTitleKey = membership.tier === 'vip' ? 'catalog.vip.extend.title' : 'catalog.vip.buy.title';
    const vipInfo =
        membership.tier === 'vip' ? LocalizeText('catalog.vip.extend.info', ['days'], [membership.totalDays.toString()]) : LocalizeText('catalog.vip.buy.info');

    return (
        <NativeTextTree>
            <div className={`octane-club-purchase-layout ${isVipPage ? 'is-vip-page' : 'is-club-page'}`}>
                {isVipPage ? (
                    <>
                        <div className="octane-club-vip-intro">
                            {teaserImage ? <img alt="" className="octane-club-teaser" src={teaserImage} /> : <span className="octane-club-teaser" />}
                            <div className="octane-club-vip-copy">
                                <strong>{LocalizeText(vipTitleKey)}</strong>
                                <span>{vipInfo}</span>
                            </div>
                        </div>
                        <div className="octane-club-vip-offers">{offerGroups.vip.map(renderOffer)}</div>
                        {/* AIR layout_loyalty_vip_buy has vip_link (VipBuyCatalogWidget.initLinks -> showVipBenefits) where layout_vip_buy has the HC Center link. */}
                        {layoutCode === 'loyalty_vip_buy' ? (
                            <BaseButton className="octane-club-vip-benefits-link" onClick={() => ShowVipBenefits()}>
                                {LocalizeText('catalog.vip.buy.link')}
                            </BaseButton>
                        ) : (
                            <div
                                className="octane-club-center-link"
                                dangerouslySetInnerHTML={{ __html: SanitizeHtml(LocalizeText('catalog.vip.buy.hccenter')) }}
                                role="link"
                                tabIndex={0}
                                onClick={(event) => {
                                    event.preventDefault();
                                    CreateLinkEvent('habboUI/open/hccenter');
                                }}
                                onKeyDown={(event) => {
                                    if (event.key !== 'Enter' && event.key !== ' ') return;

                                    event.preventDefault();
                                    CreateLinkEvent('habboUI/open/hccenter');
                                }}
                            />
                        )}
                    </>
                ) : (
                    <>
                        <header className="octane-club-membership-header">
                            <strong>{LocalizeText(membershipHeaderKey)}</strong>
                        </header>
                        <p className="octane-club-membership-info">{LocalizeText(membershipInfoKey)}</p>
                        <div className="octane-club-emblem" aria-hidden="true">
                            <span className="octane-club-vip-emblem" />
                        </div>
                        <div className="octane-club-columns">
                            <section className="octane-club-hc-column">
                                {offerGroups.hc.map(renderOffer)}
                                {membership.tier === 'vip' && (
                                    <div className="octane-club-info-card">
                                        <strong>{LocalizeText('catalog.club.info.header')}</strong>
                                        <span>{LocalizeText('catalog.club.info.content')}</span>
                                    </div>
                                )}
                            </section>
                            <section className="octane-club-vip-column">{offerGroups.vip.map(renderOffer)}</section>
                        </div>
                        {membership.active && (
                            <div className="octane-club-remaining">{LocalizeText(remainingKey, ['days'], [membership.totalDays.toString()])}</div>
                        )}
                        <BaseButton className="octane-club-center-link" type="button" onClick={() => CreateLinkEvent('habboUI/open/hccenter')}>
                            {LocalizeText('catalog.club.buy.link')}
                        </BaseButton>
                    </>
                )}

                {pendingOffer && purchaseState !== CatalogPurchaseState.NONE && purchaseState !== CatalogPurchaseState.PURCHASE && (
                    <CatalogClubPurchaseConfirmView
                        offer={pendingOffer}
                        productText={getPurchaseHeader(pendingOffer)}
                        validUntilText={getPurchaseValidUntil(pendingOffer)}
                        onCancel={() => {
                            setPurchaseWillBeGift(false);
                            setPendingOffer(null);
                            setPurchaseState(CatalogPurchaseState.NONE);
                        }}
                        onConfirm={confirmPendingOffer}
                    />
                )}
            </div>
        </NativeTextTree>
    );
};
