import { Check, Loader2, Tag, X } from 'lucide-react';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useBookingLive } from '@/contexts/booking-live';
import promoCode from '@/routes/booking/promo-code';

type ApplyResponse = {
    discountTotal: number;
    discountLabel: string | null;
};

type ErrorResponse = {
    error: string;
};

function getCsrfToken(): string {
    return (
        (document.querySelector('meta[name="csrf-token"]') as HTMLMetaElement | null)
            ?.content ?? ''
    );
}

async function postPromoCode(code: string | null): Promise<ApplyResponse> {
    const response = await fetch(promoCode.store.url(), {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            Accept: 'application/json',
            'X-CSRF-TOKEN': getCsrfToken(),
        },
        credentials: 'same-origin',
        body: JSON.stringify({ code }),
    });

    const payload = (await response.json()) as ApplyResponse | ErrorResponse;

    if (!response.ok) {
        throw new Error((payload as ErrorResponse).error ?? 'Could not apply promo code.');
    }

    return payload as ApplyResponse;
}

type Props = {
    appliedLabel?: string | null;
    onApplied?: (result: ApplyResponse) => void;
};

export function PromoCodeField({ appliedLabel = null, onApplied }: Props) {
    const { setPatch } = useBookingLive();
    const [code, setCode] = useState('');
    const [applied, setApplied] = useState<string | null>(appliedLabel);
    const [error, setError] = useState<string | null>(null);
    const [processing, setProcessing] = useState(false);

    const apply = async () => {
        if (!code.trim() || processing) {
            return;
        }

        setProcessing(true);
        setError(null);

        try {
            const result = await postPromoCode(code.trim());
            setApplied(result.discountLabel ?? code.trim().toUpperCase());
            setPatch({
                discountTotal: result.discountTotal,
                discountLabel: result.discountLabel,
            });
            onApplied?.(result);
        } catch (err) {
            setError(err instanceof Error ? err.message : 'Could not apply promo code.');
        } finally {
            setProcessing(false);
        }
    };

    const remove = async () => {
        setProcessing(true);
        setError(null);

        try {
            const result = await postPromoCode(null);
            setApplied(null);
            setCode('');
            setPatch({
                discountTotal: result.discountTotal,
                discountLabel: null,
            });
            onApplied?.({ discountTotal: result.discountTotal, discountLabel: null });
        } catch (err) {
            setError(err instanceof Error ? err.message : 'Could not remove promo code.');
        } finally {
            setProcessing(false);
        }
    };

    if (applied) {
        return (
            <div className="flex items-center justify-between gap-3 rounded-xl border border-primary/30 bg-primary/5 p-3">
                <div className="flex items-center gap-2 text-sm">
                    <Check className="size-4 text-primary" />
                    <span className="font-medium">{applied}</span>
                    <span className="text-muted-foreground">applied</span>
                </div>
                <Button
                    type="button"
                    variant="ghost"
                    size="sm"
                    disabled={processing}
                    onClick={remove}
                >
                    <X className="size-4" />
                </Button>
            </div>
        );
    }

    return (
        <div className="flex flex-col gap-2">
            <div className="flex items-center gap-2">
                <div className="relative flex-1">
                    <Tag className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
                    <Input
                        value={code}
                        onChange={(event) => setCode(event.target.value.toUpperCase())}
                        onKeyDown={(event) => {
                            if (event.key === 'Enter') {
                                event.preventDefault();
                                void apply();
                            }
                        }}
                        placeholder="Promo code"
                        className="pl-9"
                    />
                </div>
                <Button
                    type="button"
                    variant="outline"
                    disabled={processing || !code.trim()}
                    onClick={() => void apply()}
                >
                    {processing ? <Loader2 className="size-4 animate-spin" /> : 'Apply'}
                </Button>
            </div>
            {error && <p className="text-sm text-destructive">{error}</p>}
        </div>
    );
}
