import type { ComposerTranslation } from 'vue-i18n';
import type { ActiveSubscription, UsageData, UsageSummary } from '@/types/subscription';
import type {
    PaygUsageRecord,
    UsageBillingCategory,
    UsageBillingCategoryRaw,
    UsageBillingDashboard,
    UsageBillingLineItem,
    UsageBillingLineItemRaw,
    UsageBillingSummary,
    UsageBillingSummaryRaw,
    UsageRecordRaw,
} from '@/types/usageBilling';
import { formatDate } from '@/utils/formatDate';
import { calculateVatAmount, excludeVat } from '@/utils/vat';

const PAYG_PACKAGE_NAME = 'pay-as-you-go';

const MONTHLY_BILLING_FEATURE_BUSINESS_IDS = new Set([
    'users_limits',
    'cost_centers',
    'addons',
    'chart_of_accounts',
]);

const CATEGORY_BADGE: Record<string, string> = {
    users_and_permissions: 'users',
    sales: 'sales',
    purchase: 'purchase',
    accounting: 'accounting',
    reports: 'reports',
    usage: 'accounting',
    general: 'general',
};

const CATEGORY_LABEL_KEY: Record<string, string> = {
    users_and_permissions: 'users',
    sales: 'sales',
    purchase: 'purchases',
    accounting: 'accounting',
    reports: 'reports',
    usage: 'accounting',
    general: 'general',
};

export interface UsageBillingPageOverrides {
    currentUsage?: number;
    currentUsageWithoutVat?: number;
    maximumThreshold?: number;
    projectedInvoice?: number;
    lastPayment?: number;
    lastPaymentDate?: string;
}

type SubscriptionWithPayg = ActiveSubscription & {
    total_due?: number;
    total_due_without_vat?: number;
    pay_as_you_go_maximum_threshold?: number;
    is_pay_as_you_go?: boolean;
    package?: { name?: string };
    payment_transaction?: { amount?: number; created_at?: string };
};

function categoryLabelKey(category: string): string {
    return CATEGORY_LABEL_KEY[category] ?? category;
}

function categoryBadge(category: string): string {
    return CATEGORY_BADGE[category] ?? category;
}

function featureName(item: UsageBillingLineItemRaw, locale: string): string {
    return locale === 'ar' ? item.name_ar : item.name_en;
}

function isMonthlyBillingFeature(businessId: string): boolean {
    return MONTHLY_BILLING_FEATURE_BUSINESS_IDS.has(businessId);
}

function unitSuffix(item: UsageBillingLineItemRaw): 'monthly' | null {
    return item.is_monthly ? 'monthly' : null;
}

function mapLineItem(item: UsageBillingLineItemRaw, locale: string): UsageBillingLineItem {
    return {
        name: featureName(item, locale),
        quantity: item.quantity,
        unit_price: (item.unit_price === 0) ? 'N/A' : item.unit_price,
        unit_suffix: unitSuffix(item),
        total: item.total,
    };
}

export function mapUsageBillingCategory(
    raw: UsageBillingCategoryRaw,
    t: ComposerTranslation,
    locale: string,
): UsageBillingCategory {
    const labelKey = categoryLabelKey(raw.category);
    const badge = categoryBadge(raw.category);

    return {
        key: raw.category,
        badge,
        label: t(`usage_billing.categories.${labelKey}`),
        description: t(`usage_billing.categories.${labelKey}_description`),
        total: raw.total,
        items: raw.items.map((item) => mapLineItem(item, locale)),
    };
}

export function mapUsageBillingSummary(
    raw: UsageBillingSummaryRaw,
    t: ComposerTranslation,
    locale: string,
): UsageBillingSummary {
    const billingPeriodLabel = `${formatDate(raw.period_start, { locale, format: 'DD MMM' })} – ${formatDate(raw.period_end, { locale, format: 'DD MMM YYYY' })}`;

    const nextBillingLabel = raw.is_threshold_warning
        ? t('usage_billing.summary.threshold_billing_warning')
        : raw.next_billing_date
            ? formatDate(raw.next_billing_date, { locale, fullDate: true })
            : '';

    return {
        ...raw,
        billing_period_label: billingPeriodLabel,
        next_billing_label: nextBillingLabel,
        last_payment_label: t('usage_billing.summary.monthly_billing'),
    };
}

export function mapUsageBillingCategories(
    categories: UsageBillingCategoryRaw[],
    t: ComposerTranslation,
    locale: string,
): UsageBillingCategory[] {
    return categories.map((category) => mapUsageBillingCategory(category, t, locale));
}

export function isPayAsYouGoSubscription(subscription?: SubscriptionWithPayg | null): boolean {
    if (!subscription) {
        return false;
    }

    return subscription.is_pay_as_you_go === true
        || (subscription.package?.name as string | undefined) === PAYG_PACKAGE_NAME;
}

function getProjectedInvoice(subscription: SubscriptionWithPayg, totalDue: number): number {
    const daysRemaining = subscription.days_remaining ?? 0;
    const daysElapsed = 30 - daysRemaining;

    if (daysElapsed <= 0) {
        return totalDue;
    }

    const dailyAverage = totalDue / daysElapsed;

    return dailyAverage * daysRemaining + totalDue;
}

export function formatUsages(usages: PaygUsageRecord[]): UsageRecordRaw[] {
    const grouped = new Map<number, PaygUsageRecord[]>();

    for (const row of usages) {
        const rows = grouped.get(row.feature_id) ?? [];
        rows.push(row);
        grouped.set(row.feature_id, rows);
    }

    return Array.from(grouped.entries()).map(([featureId, featureUsages]) => {
        const feature = featureUsages[0]?.feature ?? null;
        const quantity = featureUsages.reduce((sum, row) => sum + row.usage, 0);
        const totalCost = Math.round(
            featureUsages.reduce((sum, row) => sum + Number(row.total_cost), 0) * 100,
        ) / 100;
        const pricePerUnit = Number(feature?.price_per_unit ?? 0);
        const pricePerFeature = Number(feature?.price_per_feature ?? 0);
        const unitPrice = Math.round(pricePerUnit * 100) / 100;

        return {
            feature_id: featureId,
            usage: quantity,
            total_cost: totalCost,
            unit_price: unitPrice,
            feature: feature === null ? null : {
                business_id: feature.business_id,
                name_en: feature.name_en,
                name_ar: feature.name_ar,
                category: feature.category,
                price_per_unit: pricePerUnit,
                price_per_feature: pricePerFeature,
            },
        };
    });
}

function buildCategoriesFromUsages(usages: UsageRecordRaw[]): UsageBillingCategoryRaw[] {
    const itemsByCategory: Record<string, UsageBillingLineItemRaw[]> = {};

    for (const record of usages) {
        if (!record.feature) {
            continue;
        }

        const category = record.feature.category || 'accounting';

        itemsByCategory[category] ??= [];
        itemsByCategory[category].push({
            feature_business_id: record.feature.business_id,
            name_en: record.feature.name_en,
            name_ar: record.feature.name_ar,
            quantity: record.usage,
            unit_price: record.unit_price,
            price_per_unit: record.feature.price_per_unit,
            price_per_feature: record.feature.price_per_feature,
            is_monthly: isMonthlyBillingFeature(record.feature.business_id),
            total: record.total_cost,
        });
    }

    return Object.entries(itemsByCategory)
        .map(([category, items]) => ({
            category,
            total: Math.round(items.reduce((sum, item) => sum + item.total, 0) * 100) / 100,
            items,
        }))
        .sort((a, b) => b.total - a.total);
}

function buildCategoriesFromUsageData(usageData: UsageData[]): UsageBillingCategoryRaw[] {
    if (usageData.length === 0) {
        return [];
    }

    const items: UsageBillingLineItemRaw[] = usageData.map((row) => ({
        feature_business_id: row.label,
        name_en: row.label,
        name_ar: row.label,
        quantity: row.usage,
        unit_price: 0,
        price_per_unit: 0,
        price_per_feature: 0,
        is_monthly: isMonthlyBillingFeature(row.label),
        total: 0,
    }));

    return [
        {
            category: 'usage',
            total: 0,
            items,
        },
    ];
}

export function buildUsageBillingDashboard(
    usageSummary: UsageSummary,
    subscription: SubscriptionWithPayg | null | undefined,
    companyName: string | null | undefined,
    overrides: UsageBillingPageOverrides = {},
    usages: PaygUsageRecord[] = [],
    vatRate: number,
): UsageBillingDashboard | null {
    if (!isPayAsYouGoSubscription(subscription) || !subscription) {
        return null;
    }

    const periodStart = subscription.start_date;
    const periodEnd = Date.now();
    const periodToDate = new Date().toISOString().slice(0, 10);
    const thresholdLimit = overrides.maximumThreshold
        ?? subscription.pay_as_you_go_maximum_threshold
        ?? 500;
    const currentBalanceInclVat = overrides.currentUsage
        ?? subscription.total_due
        ?? 0;
    const currentBalanceWithoutVat = overrides.currentUsageWithoutVat
        ?? subscription.total_due_without_vat
        ?? excludeVat(currentBalanceInclVat, vatRate);
    const projectedMonthEnd = overrides.projectedInvoice
        ?? getProjectedInvoice(subscription, currentBalanceWithoutVat);
    const thresholdRemaining = Math.max(thresholdLimit - currentBalanceWithoutVat, 0);
    const warningBalance = thresholdLimit * 0.8;
    const isThresholdWarning = currentBalanceWithoutVat >= warningBalance;
    const isCharging = currentBalanceWithoutVat >= thresholdLimit;
    const lastPaymentInclVat = overrides.lastPayment
        ?? Number(subscription.payment_transaction?.amount ?? subscription.paid_amount ?? 0);
    const lastPaymentAmount = excludeVat(lastPaymentInclVat, vatRate);
    const lastPaymentDate = overrides.lastPaymentDate
        ?? subscription.payment_transaction?.created_at?.slice(0, 10)
        ?? periodStart;
    const percentage = thresholdLimit > 0
        ? Math.min(100, Math.round((currentBalanceWithoutVat / thresholdLimit) * 1000) / 10)
        : 0;
    const subtotal = currentBalanceWithoutVat;
    const vatAmount = calculateVatAmount(subtotal, vatRate);
    const total = currentBalanceInclVat;
    const formattedUsages = usages.length > 0 ? formatUsages(usages) : [];
    const categories = formattedUsages.length > 0
        ? buildCategoriesFromUsages(formattedUsages)
        : buildCategoriesFromUsageData(usageSummary.usage_data);

    return {
        is_payg: true,
        is_charging: isCharging,
        summary: {
            current_balance: Math.round(currentBalanceWithoutVat * 100) / 100,
            period_start: periodStart,
            period_end: periodEnd,
            period_to_date: periodToDate,
            next_billing_date: isThresholdWarning
                ? null
                : addDays(periodEnd, 1),
            is_threshold_warning: isThresholdWarning,
            threshold_remaining: Math.round(thresholdRemaining * 100) / 100,
            projected_month_end: Math.round(projectedMonthEnd * 100) / 100,
            last_payment_amount: lastPaymentAmount,
            last_payment_date: lastPaymentDate,
            threshold_limit: thresholdLimit,
        },
        threshold: {
            limit: thresholdLimit,
            current: Math.round(currentBalanceWithoutVat * 100) / 100,
            percentage,
            alert_at_80: Math.round(thresholdLimit * 0.8 * 100) / 100,
            alert_at_95: Math.round(thresholdLimit * 0.95 * 100) / 100,
        },
        account: {
            company_name: companyName ?? subscription.name,
        },
        categories,
        totals: {
            subtotal,
            subtotalWithoutVat: currentBalanceWithoutVat,
            vat_rate: vatRate,
            vat_amount: vatAmount,
            total,
        },
    };
}

function addDays(date: string, days: number): string {
    const next = new Date(date);
    next.setDate(next.getDate() + days);

    return next.toISOString().slice(0, 10);
}

export function mapUsageBillingDashboard(
    dashboard: UsageBillingDashboard,
    t: ComposerTranslation,
    locale: string,
): {
    summary: UsageBillingSummary;
    categories: UsageBillingCategory[];
    threshold: NonNullable<UsageBillingDashboard['threshold']>;
    totals: NonNullable<UsageBillingDashboard['totals']>;
    account: NonNullable<UsageBillingDashboard['account']>;
    is_charging?: boolean;
} | null {
    if (!dashboard.summary || !dashboard.threshold || !dashboard.categories || !dashboard.totals || !dashboard.account) {
        return null;
    }

    return {
        summary: mapUsageBillingSummary(dashboard.summary, t, locale),
        categories: mapUsageBillingCategories(dashboard.categories, t, locale),
        threshold: dashboard.threshold,
        totals: dashboard.totals,
        account: dashboard.account,
        is_charging: dashboard.is_charging,
    };
}
