import { calculateTotalInclVat, calculateVatAmount } from '@/utils/vat';

export type EstimatorControl = 'fixed' | 'stepper' | 'toggle';

/** Fixed SAR buffer added to Exact Total for estimate ranges (pre-VAT). */
export const ESTIMATE_BUFFER_SAR = 20;

export interface EstimatorGroup {
    key: string;
    category: string;
    control: EstimatorControl;
    unit_price: number;
}

export interface EstimatorAddon {
    id: number | null;
    quantity_key: string;
    name_en: string;
    name_ar: string;
    monthly_price: number;
}

export interface EstimatorSchema {
    groups: EstimatorGroup[];
    addons: EstimatorAddon[];
}

export interface RecommendablePackage {
    id: number;
    name: string;
    monthly_price: number;
}

export type EstimatorQuantities = Record<string, number>;

export interface EstimatorLineItem {
    key: string;
    category: string;
    quantity: number;
    amount: number;
    isFixed: boolean;
}

export interface MoneyRange {
    exact: number;
    max: number;
}

export interface EstimatorTotals {
    lineItems: EstimatorLineItem[];
    optionalLineItems: EstimatorLineItem[];
    subtotal: MoneyRange;
    vatAmount: MoneyRange;
    grandTotal: MoneyRange;
}

export function addonQuantityKey(addon: Pick<EstimatorAddon, 'quantity_key'> | number): string {
    if (typeof addon === 'number') {
        return `addon_${addon}`;
    }

    return addon.quantity_key;
}

export function createDefaultQuantities(schema: EstimatorSchema): EstimatorQuantities {
    const quantities: EstimatorQuantities = {};

    for (const group of schema.groups) {
        quantities[group.key] = group.control === 'fixed' ? 1 : 0;
    }

    for (const addon of schema.addons) {
        quantities[addonQuantityKey(addon)] = 0;
    }

    return quantities;
}

export function calculateGroupAmount(group: EstimatorGroup, quantity: number): number {
    return roundMoney(Math.max(0, quantity) * group.unit_price);
}

/**
 * True when the user has entered optional usage or enabled an add-on.
 * Hidden included (fixed) groups alone are not enough to enable Calculate.
 */
export function hasOptionalEstimatorInput(
    schema: EstimatorSchema,
    quantities: EstimatorQuantities,
): boolean {
    for (const group of schema.groups) {
        if (group.control === 'fixed') {
            continue;
        }

        if ((quantities[group.key] ?? 0) > 0) {
            return true;
        }
    }

    for (const addon of schema.addons) {
        if ((quantities[addonQuantityKey(addon)] ?? 0) > 0) {
            return true;
        }
    }

    return false;
}

export function calculateEstimatorTotals(
    schema: EstimatorSchema,
    quantities: EstimatorQuantities,
    vatRate: number,
): EstimatorTotals {
    const lineItems: EstimatorLineItem[] = [];

    for (const group of schema.groups) {
        const quantity = Math.max(0, quantities[group.key] ?? (group.control === 'fixed' ? 1 : 0));
        const amount = calculateGroupAmount(group, quantity);

        if (amount <= 0 && group.control !== 'fixed') {
            continue;
        }

        if (group.control === 'fixed' || quantity > 0) {
            lineItems.push({
                key: group.key,
                category: group.category,
                quantity: group.control === 'fixed' ? 1 : quantity,
                amount,
                isFixed: group.control === 'fixed',
            });
        }
    }

    for (const addon of schema.addons) {
        const key = addonQuantityKey(addon);
        const quantity = quantities[key] ? 1 : 0;

        if (quantity <= 0) {
            continue;
        }

        lineItems.push({
            key,
            category: 'addons',
            quantity: 1,
            amount: roundMoney(Number(addon.monthly_price)),
            isFixed: false,
        });
    }

    const exactSubtotal = roundMoney(lineItems.reduce((sum, item) => sum + item.amount, 0));
    const maxSubtotal = roundMoney(exactSubtotal + ESTIMATE_BUFFER_SAR);

    const subtotal: MoneyRange = {
        exact: exactSubtotal,
        max: maxSubtotal,
    };

    const vatAmount: MoneyRange = {
        exact: calculateVatAmount(exactSubtotal, vatRate),
        max: calculateVatAmount(maxSubtotal, vatRate),
    };

    const grandTotal: MoneyRange = {
        exact: calculateTotalInclVat(exactSubtotal, vatRate),
        max: calculateTotalInclVat(maxSubtotal, vatRate),
    };

    return {
        lineItems,
        optionalLineItems: lineItems.filter((item) => !item.isFixed),
        subtotal,
        vatAmount,
        grandTotal,
    };
}

/**
 * Best fixed plan that is still at or below Exact Total (incl. VAT).
 * Package monthly_price is already VAT-inclusive (same figure users see on Packages).
 * Never recommend a plan priced above the PAYG estimate incl. VAT.
 * Uses Exact Total only — never the buffered upper bound.
 */
export function recommendPackage(
    exactTotalInclVat: number,
    packages: RecommendablePackage[],
): RecommendablePackage | null {
    if (packages.length === 0) {
        return null;
    }

    const affordable = packages
        .filter((pkg) => pkg.monthly_price <= exactTotalInclVat)
        .sort((a, b) => b.monthly_price - a.monthly_price);

    return affordable[0] ?? null;
}

export function formatMoneyRange(range: MoneyRange, format: (amount: number) => string): string {
    if (range.exact === range.max) {
        return format(range.exact);
    }

    // Non-breaking spaces keep the range on one line when space is tight.
    return `${format(range.exact)}\u00A0–\u00A0${format(range.max)}`;
}

function roundMoney(amount: number): number {
    return Math.round(amount * 100) / 100;
}
