// VAT Calculation Engine for Danish SKAT Compliance

import dayjs from 'dayjs';
import type {
  VATBoxId,
  BasisBoxId,
  VATReportPeriod,
  VATTransactionLine,
  VATCalculationResult,
  VATCalculationWarning,
  VATCalculationError,
  CalculatedVATBox,
  VATReport,
  SKATExportCSV,
  SKATExportXML,
} from '@/types/vat';
import type { Transaction, TransactionLine, Company } from '@/types/accounting';
import {
  VAT_CODE_CONFIG,
  SKAT_VAT_BOXES,
  calculateVATFromGross,
  isValidVATCode,
} from './vatCodes';

// =====================================================
// MAIN CALCULATION FUNCTION
// =====================================================

/**
 * Calculate VAT report from transactions
 */
export function calculateVATReport(
  transactions: Transaction[],
  period: VATReportPeriod
): VATCalculationResult {
  const warnings: VATCalculationWarning[] = [];
  const errors: VATCalculationError[] = [];
  const vatLines: VATTransactionLine[] = [];

  // Initialize box accumulators
  const boxes: Record<VATBoxId | BasisBoxId, {
    amount: number;
    transactionCount: number;
    transactionIds: string[];
  }> = {
    A: { amount: 0, transactionCount: 0, transactionIds: [] },
    B: { amount: 0, transactionCount: 0, transactionIds: [] },
    C: { amount: 0, transactionCount: 0, transactionIds: [] },
    D: { amount: 0, transactionCount: 0, transactionIds: [] },
    '1': { amount: 0, transactionCount: 0, transactionIds: [] },
    '2': { amount: 0, transactionCount: 0, transactionIds: [] },
    '3': { amount: 0, transactionCount: 0, transactionIds: [] },
    '4': { amount: 0, transactionCount: 0, transactionIds: [] },
  };

  // Process each transaction
  for (const transaction of transactions) {
    // Check transaction is within period
    const txDate = dayjs(transaction.date);
    const periodStart = dayjs(period.startDate);
    const periodEnd = dayjs(period.endDate);

    if (txDate.isBefore(periodStart) || txDate.isAfter(periodEnd)) {
      warnings.push({
        type: 'period_mismatch',
        message: `Transaction ${transaction.transactionNumber} is outside the period`,
        messageDanish: `Transaktion ${transaction.transactionNumber} er uden for perioden`,
        transactionId: transaction.id,
        severity: 'medium',
      });
      continue;
    }

    // Skip voided transactions
    if (transaction.isVoided) continue;

    // Process each line
    for (const line of transaction.lines) {
      const vatLine = processTransactionLine(transaction, line, warnings, errors);
      if (vatLine) {
        vatLines.push(vatLine);

        // Add to appropriate boxes
        const codeConfig = VAT_CODE_CONFIG[vatLine.vatCode];

        if (codeConfig.affectsBoxes.vatBox) {
          const boxId = codeConfig.affectsBoxes.vatBox;
          boxes[boxId].amount += vatLine.vatAmount;
          if (!boxes[boxId].transactionIds.includes(transaction.id)) {
            boxes[boxId].transactionIds.push(transaction.id);
            boxes[boxId].transactionCount++;
          }
        }

        if (codeConfig.affectsBoxes.basisBox) {
          const basisId = codeConfig.affectsBoxes.basisBox;
          boxes[basisId].amount += Math.abs(vatLine.netAmount);
          if (!boxes[basisId].transactionIds.includes(transaction.id)) {
            boxes[basisId].transactionIds.push(transaction.id);
            boxes[basisId].transactionCount++;
          }
        }

        // For reverse charge, also add to input VAT (Box B) as deductible
        if (codeConfig.reverseCharge && codeConfig.deductible) {
          boxes.B.amount += vatLine.vatAmount;
          if (!boxes.B.transactionIds.includes(transaction.id)) {
            boxes.B.transactionIds.push(transaction.id);
            boxes.B.transactionCount++;
          }
        }
      }
    }
  }

  // Calculate summary
  const totalOutputVAT = boxes.A.amount + boxes.C.amount + boxes.D.amount;
  const totalInputVAT = boxes.B.amount;
  const netVAT = totalOutputVAT - totalInputVAT;

  return {
    period,
    transactions: vatLines,
    boxes,
    summary: {
      totalOutputVAT: roundToOre(totalOutputVAT),
      totalInputVAT: roundToOre(totalInputVAT),
      netVAT: roundToOre(netVAT),
    },
    warnings,
    errors,
  };
}

/**
 * Process a single transaction line for VAT
 */
function processTransactionLine(
  transaction: Transaction,
  line: TransactionLine,
  _warnings: VATCalculationWarning[],
  errors: VATCalculationError[]
): VATTransactionLine | null {
  // Skip lines without VAT code
  if (!line.vatCode) {
    return null;
  }

  // Validate VAT code instead of using type assertion
  if (!isValidVATCode(line.vatCode)) {
    errors.push({
      type: 'invalid_vat_code',
      message: `Invalid VAT code: ${line.vatCode}`,
      messageDanish: `Ugyldig momskode: ${line.vatCode}`,
      transactionId: transaction.id,
    });
    return null;
  }

  const vatCode = line.vatCode;

  // Skip lines without VAT relevance
  if (vatCode === 'NONE') {
    return null;
  }

  const codeConfig = VAT_CODE_CONFIG[vatCode];

  // Calculate net amount (amount without VAT)
  const grossAmount = line.debit > 0 ? line.debit : line.credit;
  let netAmount: number;
  let vatAmount: number;

  if (line.vatAmount !== undefined && line.vatAmount !== null) {
    // VAT amount explicitly provided
    vatAmount = line.vatAmount;
    netAmount = grossAmount - vatAmount;
  } else if (codeConfig.rate > 0) {
    // Calculate VAT from gross amount
    const calculated = calculateVATFromGross(grossAmount, codeConfig.rate);
    netAmount = calculated.net;
    vatAmount = calculated.vat;
  } else {
    // Zero-rated or exempt
    netAmount = grossAmount;
    vatAmount = 0;
  }

  // Determine sign based on transaction type
  const isOutput = codeConfig.type === 'output';
  const isInput = codeConfig.type === 'input' || codeConfig.type === 'reverse_charge';

  // For output VAT (sales), credit increases VAT liability
  // For input VAT (purchases), debit increases VAT deduction
  if (isOutput && line.debit > 0) {
    // This is a reversal/return of sales
    netAmount = -netAmount;
    vatAmount = -vatAmount;
  } else if (isInput && line.credit > 0) {
    // This is a reversal/return of purchases
    netAmount = -netAmount;
    vatAmount = -vatAmount;
  }

  return {
    transactionId: transaction.id,
    transactionLineId: line.id,
    transactionNumber: transaction.transactionNumber,
    transactionDate: transaction.date,
    accountId: line.accountId,
    accountNumber: line.account?.accountNumber || '',
    accountName: line.account?.name || '',
    description: line.description || transaction.description,
    debit: line.debit,
    credit: line.credit,
    netAmount: roundToOre(netAmount),
    vatCode,
    vatAmount: roundToOre(Math.abs(vatAmount)),
    vatRate: codeConfig.rate,
    isReverseCharge: codeConfig.reverseCharge,
  };
}

// =====================================================
// EXPORT FUNCTIONS
// =====================================================

/**
 * Generate SKAT CSV export data
 */
export function generateSKATCSV(
  calculation: VATCalculationResult,
  company: Company
): SKATExportCSV {
  return {
    cvr: company.cvr.replace(/\s/g, ''),
    periode: formatPeriodForSKATExport(calculation.period),
    rubrikA: Math.round(calculation.boxes.A.amount),
    rubrikB: Math.round(calculation.boxes.B.amount),
    rubrikC: Math.round(calculation.boxes.C.amount),
    rubrikD: Math.round(calculation.boxes.D.amount),
    felt1: Math.round(calculation.boxes['1'].amount),
    felt2: Math.round(calculation.boxes['2'].amount),
    felt3: Math.round(calculation.boxes['3'].amount),
    felt4: Math.round(calculation.boxes['4'].amount),
  };
}

/**
 * Generate SKAT XML export data
 */
export function generateSKATXML(
  calculation: VATCalculationResult,
  company: Company
): SKATExportXML {
  return {
    version: '1.0',
    cvr: company.cvr.replace(/\s/g, ''),
    periodeStart: calculation.period.startDate,
    periodeSlut: calculation.period.endDate,
    angivelse: {
      salgsmoms: roundToOre(calculation.boxes.A.amount),
      koebsmoms: roundToOre(calculation.boxes.B.amount),
      euVarekoebMoms: roundToOre(calculation.boxes.C.amount),
      ydelseskoebMoms: roundToOre(calculation.boxes.D.amount),
      salgMedMoms: roundToOre(calculation.boxes['1'].amount),
      salgUdenMoms: roundToOre(calculation.boxes['2'].amount),
      euVarekoeb: roundToOre(calculation.boxes['3'].amount),
      ydelseskoeb: roundToOre(calculation.boxes['4'].amount),
    },
  };
}

/**
 * Export VAT report as CSV string
 */
export function exportVATReportCSV(
  calculation: VATCalculationResult,
  company: Company
): string {
  const data = generateSKATCSV(calculation, company);

  const header = 'CVR;Periode;RubrikA;RubrikB;RubrikC;RubrikD;Felt1;Felt2;Felt3;Felt4';
  const row = `${data.cvr};${data.periode};${data.rubrikA};${data.rubrikB};${data.rubrikC};${data.rubrikD};${data.felt1};${data.felt2};${data.felt3};${data.felt4}`;

  return `${header}\n${row}`;
}

/**
 * Export VAT report as XML string
 */
export function exportVATReportXML(
  calculation: VATCalculationResult,
  company: Company
): string {
  const data = generateSKATXML(calculation, company);

  return `<?xml version="1.0" encoding="UTF-8"?>
<Momsangivelse version="${data.version}">
  <Virksomhed>
    <CVR>${data.cvr}</CVR>
    <Navn>${company.name}</Navn>
  </Virksomhed>
  <Periode>
    <Start>${data.periodeStart}</Start>
    <Slut>${data.periodeSlut}</Slut>
  </Periode>
  <Angivelse>
    <Salgsmoms>${data.angivelse.salgsmoms}</Salgsmoms>
    <Koebsmoms>${data.angivelse.koebsmoms}</Koebsmoms>
    <EUVarekoebMoms>${data.angivelse.euVarekoebMoms}</EUVarekoebMoms>
    <YdelseskoebMoms>${data.angivelse.ydelseskoebMoms}</YdelseskoebMoms>
    <SalgMedMoms>${data.angivelse.salgMedMoms}</SalgMedMoms>
    <SalgUdenMoms>${data.angivelse.salgUdenMoms}</SalgUdenMoms>
    <EUVarekoeb>${data.angivelse.euVarekoeb}</EUVarekoeb>
    <Ydelseskoeb>${data.angivelse.ydelseskoeb}</Ydelseskoeb>
  </Angivelse>
  <Resultat>
    <UdgaaendeMoms>${calculation.summary.totalOutputVAT}</UdgaaendeMoms>
    <IndgaaendeMoms>${calculation.summary.totalInputVAT}</IndgaaendeMoms>
    <NettoMoms>${calculation.summary.netVAT}</NettoMoms>
  </Resultat>
</Momsangivelse>`;
}

// =====================================================
// HELPER FUNCTIONS
// =====================================================

/**
 * Round to Danish ore (2 decimal places)
 */
function roundToOre(amount: number): number {
  return Math.round(amount * 100) / 100;
}

/**
 * Format period for SKAT export
 */
function formatPeriodForSKATExport(period: VATReportPeriod): string {
  switch (period.periodicitet) {
    case 'monthly':
      return dayjs(period.startDate).format('YYYYMM');
    case 'quarterly':
      return `${period.year}Q${period.periodNumber}`;
    case 'half-yearly':
      return `${period.year}H${period.periodNumber}`;
    case 'yearly':
      return `${period.year}`;
  }
}

/**
 * Format VAT period for display
 */
export function formatVATPeriodDisplay(period: VATReportPeriod): string {
  const MONTHS = [
    'Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni',
    'Juli', 'August', 'September', 'Oktober', 'November', 'December'
  ];

  switch (period.periodicitet) {
    case 'monthly':
      return `${MONTHS[period.periodNumber - 1]} ${period.year}`;
    case 'quarterly':
      return `Q${period.periodNumber} ${period.year}`;
    case 'half-yearly':
      return `H${period.periodNumber} ${period.year}`;
    case 'yearly':
      return `${period.year}`;
  }
}

/**
 * Create empty calculated VAT boxes
 */
export function createEmptyCalculatedBoxes(): VATReport['boxes'] {
  const createBox = (id: VATBoxId | BasisBoxId): CalculatedVATBox => ({
    ...SKAT_VAT_BOXES[id],
    amount: 0,
    transactionCount: 0,
    transactionIds: [],
  });

  return {
    A: createBox('A'),
    B: createBox('B'),
    C: createBox('C'),
    D: createBox('D'),
    '1': createBox('1'),
    '2': createBox('2'),
    '3': createBox('3'),
    '4': createBox('4'),
  };
}

/**
 * Calculate days until VAT deadline
 */
export function getDaysUntilDeadline(deadline: string): number {
  const today = dayjs();
  const deadlineDate = dayjs(deadline);
  return deadlineDate.diff(today, 'day');
}

/**
 * Check if VAT deadline is approaching (within 14 days)
 */
export function isDeadlineApproaching(deadline: string): boolean {
  const days = getDaysUntilDeadline(deadline);
  return days >= 0 && days <= 14;
}

/**
 * Check if VAT deadline is overdue
 */
export function isDeadlineOverdue(deadline: string): boolean {
  return getDaysUntilDeadline(deadline) < 0;
}
