import { type LokaliseTFunction } from 'src/utils/i18n';

import {
  type CollaboratorLoanPlanResponse,
  type CollaboratorPaymentItem,
  type Periodicity,
} from '../types';

import { collaboratorMinorUnitsToCurrency } from './collaboratorAmount';
import {
  formatLongPaymentDateMX,
  formatPaymentDateMX,
  isDueDatePastInMexicoCity,
  parseApiDateString,
} from './formatters';
import { periodicityKey } from './periodicity';

export type ScheduleTone = 'success' | 'brand' | 'muted' | 'error';

export type ScheduleRow = {
  fee: number;
  date: string;
  amount: number | null;
  status: string;
  tone: ScheduleTone;
};

export type MicroloanPlanStatus =
  | 'ON_TRACK'
  | 'OVERDUE'
  | 'PENDING_DISBURSEMENT'
  | 'PAUSED';

export type MicroloansStatusSummaryCardBalance = {
  kind: 'balance';
  label: string;
  balance: number | null;
  caption: string;
};

export type MicroloansStatusSummaryCardPaidRatio = {
  kind: 'paidRatio';
  label: string;
  value: string;
  caption: string;
};

export type MicroloansStatusSummaryCardNextDue = {
  kind: 'nextDue';
  label: string;
  value: string;
  caption: string;
  gridColumnSpan: 2;
};

export type MicroloansStatusSummaryCard =
  | MicroloansStatusSummaryCardBalance
  | MicroloansStatusSummaryCardPaidRatio
  | MicroloansStatusSummaryCardNextDue;

export function formatMicroloanOverdueMessage(
  t: LokaliseTFunction,
  payments: number,
  daysAgo: number,
): string {
  const p1 = payments === 1;
  const d1 = daysAgo === 1;
  if (p1 && d1) {
    return t('status.overdue_1_1');
  }
  if (p1 && !d1) {
    return t('status.overdue_1_n', { days: daysAgo });
  }
  if (!p1 && d1) {
    return t('status.overdue_n_1', { payments });
  }
  return t('status.overdue_n_n', { payments, days: daysAgo });
}

function normStatus(s: string | undefined): string {
  return (s ?? '').toUpperCase().replace(/[\s-]+/g, '_');
}

function isPaidStatus(s: string | undefined): boolean {
  const n = normStatus(s);
  return n === 'PAID' || n === 'PAYED' || n === 'COMPLETED';
}

function isCancelledStatus(s: string | undefined): boolean {
  return normStatus(s) === 'CANCELLED';
}

function isOverdueStatus(s: string | undefined): boolean {
  const n = normStatus(s);
  return (
    n === 'OVERDUE' || n === 'LATE' || n === 'DELINQUENT' || n === 'DEFAULTED'
  );
}

function isOverduePaymentRow(row: CollaboratorPaymentItem): boolean {
  if (isPaidStatus(row.status) || isCancelledStatus(row.status)) {
    return false;
  }
  if (isOverdueStatus(row.status)) {
    return true;
  }
  return isDueDatePastInMexicoCity(parseApiDateString(row.dueDate));
}

function sumPaidInstallmentsMinorFromEndpoint(
  payments: CollaboratorPaymentItem[],
  amountFallbackMinor: number | undefined,
): number {
  let sum = 0;
  for (const p of payments) {
    if (!isPaidStatus(p.status)) {
      continue;
    }
    const minor = paymentMinorFromItem(p);
    if (minor != null) {
      sum += minor;
    } else if (
      amountFallbackMinor != null &&
      Number.isFinite(amountFallbackMinor)
    ) {
      sum += amountFallbackMinor;
    }
  }
  return sum;
}

function majorFromMinorOrNull(minor: number | undefined | null): number | null {
  if (minor == null || Number.isNaN(minor) || !Number.isFinite(minor)) {
    return null;
  }
  return collaboratorMinorUnitsToCurrency(minor);
}

function paymentMinorFromItem(
  item: CollaboratorPaymentItem,
): number | undefined {
  if (item.dueAmount != null && Number.isFinite(item.dueAmount)) {
    return item.dueAmount;
  }
  if (item.amount != null && Number.isFinite(item.amount)) {
    return item.amount;
  }
  return undefined;
}

function buildScheduleRowsFromPaymentItems(
  payments: CollaboratorPaymentItem[],
  amountFallbackMinor: number | undefined,
  t: LokaliseTFunction,
  locale: Locale,
): ScheduleRow[] {
  const sorted = [...payments].sort((a, b) => {
    const na = a.installmentNumber ?? a.sequence ?? 0;
    const nb = b.installmentNumber ?? b.sequence ?? 0;
    return na - nb;
  });

  const firstUnpaidIdx = sorted.findIndex(
    r => !isPaidStatus(r.status) && !isCancelledStatus(r.status),
  );
  const dueIdx = sorted.findIndex(r => normStatus(r.status) === 'DUE');
  const proximaIdx = dueIdx >= 0 ? dueIdx : firstUnpaidIdx;

  return sorted.map((row, idx) => {
    const fee = row.installmentNumber ?? row.sequence ?? idx + 1;
    const due = parseApiDateString(row.dueDate);
    const dateStr = due ? formatPaymentDateMX(due, locale) : '—';
    const amount = majorFromMinorOrNull(
      paymentMinorFromItem(row) ?? amountFallbackMinor,
    );

    if (isPaidStatus(row.status)) {
      return {
        fee,
        date: dateStr,
        amount,
        status: t('status.paid'),
        tone: 'success' as const,
      };
    }
    if (isCancelledStatus(row.status)) {
      return {
        fee,
        date: dateStr,
        amount,
        status: t('status.cancelled'),
        tone: 'muted' as const,
      };
    }
    if (isOverduePaymentRow(row)) {
      return {
        fee,
        date: dateStr,
        amount,
        status: t('status.overdue_label'),
        tone: 'error' as const,
      };
    }
    if (idx === proximaIdx) {
      return {
        fee,
        date: dateStr,
        amount,
        status: t('status.next'),
        tone: 'brand' as const,
      };
    }
    return {
      fee,
      date: dateStr,
      amount,
      status: t('status.pending'),
      tone: 'muted' as const,
    };
  });
}

function resolveScheduleRows(
  plan: CollaboratorLoanPlanResponse,
  payments: CollaboratorPaymentItem[],
  t: LokaliseTFunction,
  locale: Locale,
): ScheduleRow[] {
  if (payments.length === 0) {
    return [];
  }
  return buildScheduleRowsFromPaymentItems(
    payments,
    plan.amountPerCycle,
    t,
    locale,
  );
}

function countPaidRows(rows: ScheduleRow[]): number {
  return rows.filter(r => r.tone === 'success').length;
}

function parseFirstDuePaymentVencimientoDate(
  payments: CollaboratorPaymentItem[],
): Date | null {
  const dueItems = payments.filter(p => normStatus(p.status) === 'DUE');
  if (dueItems.length === 0) {
    return null;
  }
  const sorted = [...dueItems].sort((a, b) => {
    const na = a.installmentNumber ?? a.sequence ?? 0;
    const nb = b.installmentNumber ?? b.sequence ?? 0;
    return na - nb;
  });
  const iso = sorted[0]?.dueDate;
  return iso ? parseApiDateString(iso) : null;
}

function resolveNextPaymentVencimientoDate(
  plan: CollaboratorLoanPlanResponse,
  payments: CollaboratorPaymentItem[],
): Date | null {
  if (payments.length === 0) {
    return parseApiDateString(plan.nextPaymentDueDate);
  }

  const sorted = [...payments].sort((a, b) => {
    const na = a.installmentNumber ?? a.sequence ?? 0;
    const nb = b.installmentNumber ?? b.sequence ?? 0;
    return na - nb;
  });

  const firstUnpaidWithDate = sorted.find(
    payment =>
      !isPaidStatus(payment.status) &&
      !isCancelledStatus(payment.status) &&
      parseApiDateString(payment.dueDate) != null,
  );
  if (firstUnpaidWithDate) {
    return parseApiDateString(firstUnpaidWithDate.dueDate);
  }

  const dueDate = parseFirstDuePaymentVencimientoDate(payments);
  if (dueDate) {
    return dueDate;
  }
  return parseApiDateString(plan.nextPaymentDueDate);
}

export function buildMicroloansStatusViewModel(
  plan: CollaboratorLoanPlanResponse,
  payments: CollaboratorPaymentItem[],
  t: LokaliseTFunction,
  locale: Locale,
  periodicity: Periodicity = 'weekly',
): {
  planStatus: MicroloanPlanStatus;
  overduePaymentsCount: number;
  overdueDaysAgo: number;
  summaryCards: MicroloansStatusSummaryCard[];
  capitalCredited: number | null;

  planStartDateDisplay: string;
  progressPercent: number;
  progressPaidLabel: string;
  progressRemainingLabel: string;
  nextDueInlineDate: string;
  scheduleTitleSuffix: string;
  scheduleRows: ScheduleRow[];
  scheduleFooterTotal: number | null;
  creditDetailsPrincipal: number;
  creditDetailsTotalInterests: number;
  creditDetailsTotalToPay: number;
  creditDetailsInstallmentsCount: number;
  disclosureCatPercent?: number;
  disclosureTnaPercent?: number;
  periodicity: Periodicity;
} {
  const scheduleRows = resolveScheduleRows(plan, payments, t, locale);

  const totalInstallments = Math.max(
    plan.installmentsChosen ?? 0,
    payments.length,
    scheduleRows.length,
  );

  const paidFromRows = countPaidRows(scheduleRows);
  const paidCount =
    payments.length > 0
      ? paidFromRows
      : (plan.paidInstallments ?? plan.paidInstallmentsCount ?? paidFromRows);

  const overdueRowCount = scheduleRows.filter(r => r.tone === 'error').length;
  const hasOverdueRow = overdueRowCount > 0;
  const delinquent = (plan.overdueInstallmentsCount ?? 0) > 0 || hasOverdueRow;

  const apiPlanStatus = normStatus(plan.status);
  let planStatus: MicroloanPlanStatus;
  if (apiPlanStatus === 'PENDING') {
    planStatus = 'PENDING_DISBURSEMENT';
  } else if (apiPlanStatus === 'PAUSED') {
    planStatus = 'PAUSED';
  } else if (delinquent) {
    planStatus = 'OVERDUE';
  } else {
    planStatus = 'ON_TRACK';
  }

  const overduePaymentsCount = plan.overdueInstallmentsCount ?? overdueRowCount;
  const overdueDaysAgo =
    plan.overdueDays ?? (delinquent && overduePaymentsCount > 0 ? 1 : 0);

  const amountPerCycle = plan.amountPerCycle ?? 0;
  const totalToRepay = plan.totalToRepay ?? 0;
  let outstanding = 0;
  if (payments.length > 0 && totalToRepay > 0) {
    const paidSumMinor = sumPaidInstallmentsMinorFromEndpoint(
      payments,
      plan.amountPerCycle,
    );
    outstanding = Math.max(0, totalToRepay - paidSumMinor);
  } else if (plan.outstandingBalance != null) {
    outstanding = plan.outstandingBalance;
  } else if (totalToRepay > 0 && amountPerCycle >= 0 && paidCount >= 0) {
    outstanding = Math.max(0, totalToRepay - amountPerCycle * paidCount);
  }

  const pct =
    totalInstallments > 0
      ? Math.min(100, Math.round((paidCount / totalInstallments) * 100))
      : 0;

  const nextPaymentDate = resolveNextPaymentVencimientoDate(plan, payments);
  const nextDueInlineDate = nextPaymentDate
    ? formatLongPaymentDateMX(nextPaymentDate, locale)
    : '—';

  const summaryCards = [
    {
      kind: 'balance' as const,
      label: t('status.balance'),
      balance: majorFromMinorOrNull(outstanding),
      caption: t('status.caption_mxn'),
    },
    {
      kind: 'paidRatio' as const,
      label: t('status.completed_pct'),
      value:
        totalInstallments > 0
          ? `${paidCount}/${totalInstallments}`
          : String(paidCount),
      caption: t('status.completed_pct_value', { pct }),
    },
    {
      kind: 'nextDue' as const,
      label: t('status.next_due_card_label'),
      value: nextDueInlineDate,
      caption: t('status.next_due_label'),
      gridColumnSpan: 2 as const,
    },
  ];

  const capitalCredited = majorFromMinorOrNull(plan.totalAmount);

  const planStartDate =
    parseApiDateString(plan.loanPlan?.startDate ?? plan.startDate) ??
    parseApiDateString(payments[0]?.dueDate);
  const planStartDateDisplay = planStartDate
    ? formatLongPaymentDateMX(planStartDate, locale)
    : '—';

  let progressPaidLabel: string;
  if (totalInstallments > 0) {
    progressPaidLabel =
      paidCount === 1
        ? t('status.progress_paid_one', { count: paidCount, pct })
        : t('status.progress_paid_other', { count: paidCount, pct });
  } else {
    progressPaidLabel = t('status.progress_paid_no_installments', {
      count: paidCount,
    });
  }

  let progressRemainingLabel: string;
  if (totalInstallments > 0) {
    const remaining = Math.max(0, totalInstallments - paidCount);
    progressRemainingLabel = t('status.progress_remaining', {
      count: remaining,
    });
  } else {
    progressRemainingLabel = '';
  }

  const disclosureCatPercent =
    plan.cat != null && Number.isFinite(plan.cat) ? plan.cat : undefined;
  const disclosureTnaPercent =
    plan.tna != null && Number.isFinite(plan.tna) ? plan.tna : undefined;

  const scheduleFooterTotal = majorFromMinorOrNull(totalToRepay);
  const totalAmountMinor = plan.totalAmount ?? 0;
  const creditDetailsTotalInterests =
    plan.cost != null && Number.isFinite(plan.cost)
      ? collaboratorMinorUnitsToCurrency(plan.cost)
      : (majorFromMinorOrNull(Math.max(0, totalToRepay - totalAmountMinor)) ??
        0);

  return {
    planStatus,
    overduePaymentsCount,
    overdueDaysAgo,
    summaryCards,
    capitalCredited,
    planStartDateDisplay,
    progressPercent: pct,
    progressPaidLabel,
    progressRemainingLabel,
    nextDueInlineDate,
    scheduleTitleSuffix:
      totalInstallments > 0
        ? t(periodicityKey('status.installments_summary', periodicity), {
            count: totalInstallments,
          })
        : t('status.plan_payments_caption'),
    scheduleRows,
    scheduleFooterTotal,
    creditDetailsPrincipal: capitalCredited ?? 0,
    creditDetailsTotalInterests,
    creditDetailsTotalToPay: scheduleFooterTotal ?? 0,
    creditDetailsInstallmentsCount: totalInstallments,
    ...(disclosureCatPercent != null ? { disclosureCatPercent } : {}),
    ...(disclosureTnaPercent != null ? { disclosureTnaPercent } : {}),
    periodicity,
  };
}

export type MicroloansStatusViewModel = ReturnType<
  typeof buildMicroloansStatusViewModel
>;
