// Period Calculation Utilities for Danish Accounting

import dayjs from 'dayjs';
import type {
  FiscalYear,
  AccountingPeriod,
  VATPeriod,
  PeriodFrequency,
  PeriodStatus,
  PeriodValidationResult,
  DANISH_MONTHS,
  DANISH_MONTHS_SHORT,
} from '@/types/periods';
import type { VATPeriodicitet } from '@/types/periods';
import type { Transaction } from '@/types/accounting';

// =====================================================
// PERIOD GENERATION
// =====================================================

/**
 * Generate accounting periods for a fiscal year
 */
export function generateAccountingPeriods(
  fiscalYear: FiscalYear,
  frequency: PeriodFrequency
): Omit<AccountingPeriod, 'id' | 'createdAt' | 'updatedAt'>[] {
  const periods: Omit<AccountingPeriod, 'id' | 'createdAt' | 'updatedAt'>[] = [];
  const startDate = dayjs(fiscalYear.startDate);
  const endDate = dayjs(fiscalYear.endDate);

  let periodCount: number;
  let monthsPerPeriod: number;

  switch (frequency) {
    case 'monthly':
      periodCount = 12;
      monthsPerPeriod = 1;
      break;
    case 'quarterly':
      periodCount = 4;
      monthsPerPeriod = 3;
      break;
    case 'half-yearly':
      periodCount = 2;
      monthsPerPeriod = 6;
      break;
    case 'yearly':
      periodCount = 1;
      monthsPerPeriod = 12;
      break;
  }

  for (let i = 0; i < periodCount; i++) {
    const periodStart = startDate.add(i * monthsPerPeriod, 'month');
    const periodEnd = periodStart.add(monthsPerPeriod, 'month').subtract(1, 'day');

    // Make sure we don't exceed fiscal year end
    const actualEnd = periodEnd.isAfter(endDate) ? endDate : periodEnd;

    const { name, shortName } = getPeriodDisplayName(
      periodStart.format('YYYY-MM-DD'),
      actualEnd.format('YYYY-MM-DD'),
      frequency
    );

    const status = determinePeriodStatus(
      periodStart.format('YYYY-MM-DD'),
      actualEnd.format('YYYY-MM-DD')
    );

    periods.push({
      companyId: fiscalYear.companyId,
      fiscalYearId: fiscalYear.id,
      periodNumber: i + 1,
      name,
      shortName,
      startDate: periodStart.format('YYYY-MM-DD'),
      endDate: actualEnd.format('YYYY-MM-DD'),
      status,
    });
  }

  return periods;
}

/**
 * Generate VAT periods for a year
 */
export function generateVATPeriods(
  companyId: string,
  periodicitet: VATPeriodicitet,
  year: number
): Omit<VATPeriod, 'id' | 'createdAt' | 'updatedAt'>[] {
  const periods: Omit<VATPeriod, 'id' | 'createdAt' | 'updatedAt'>[] = [];

  let periodCount: number;
  let monthsPerPeriod: number;
  let deadlineDays: number;

  switch (periodicitet) {
    case 'monthly':
      periodCount = 12;
      monthsPerPeriod = 1;
      deadlineDays = 25;
      break;
    case 'quarterly':
      periodCount = 4;
      monthsPerPeriod = 3;
      deadlineDays = 40;
      break;
    case 'half-yearly':
      periodCount = 2;
      monthsPerPeriod = 6;
      deadlineDays = 60;
      break;
    case 'yearly':
      periodCount = 1;
      monthsPerPeriod = 12;
      deadlineDays = 90;
      break;
  }

  for (let i = 0; i < periodCount; i++) {
    const startMonth = i * monthsPerPeriod + 1;
    const periodStart = dayjs(`${year}-${String(startMonth).padStart(2, '0')}-01`);
    const periodEnd = periodStart.add(monthsPerPeriod, 'month').subtract(1, 'day');
    const deadline = periodEnd.add(deadlineDays, 'day');

    const name = getVATPeriodName(periodicitet, year, i + 1);
    const status = determineVATPeriodStatus(
      periodStart.format('YYYY-MM-DD'),
      periodEnd.format('YYYY-MM-DD')
    );

    periods.push({
      companyId,
      periodicitet,
      year,
      periodNumber: i + 1,
      name,
      startDate: periodStart.format('YYYY-MM-DD'),
      endDate: periodEnd.format('YYYY-MM-DD'),
      deadline: deadline.format('YYYY-MM-DD'),
      status,
    });
  }

  return periods;
}

// =====================================================
// PERIOD NAMING
// =====================================================

/**
 * Get period display name based on frequency
 */
export function getPeriodDisplayName(
  startDate: string,
  endDate: string,
  frequency: PeriodFrequency
): { name: string; shortName: string } {
  const start = dayjs(startDate);
  const MONTHS = [
    'Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni',
    'Juli', 'August', 'September', 'Oktober', 'November', 'December'
  ];
  const MONTHS_SHORT = [
    'Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun',
    'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'
  ];

  switch (frequency) {
    case 'monthly':
      return {
        name: `${MONTHS[start.month()]} ${start.year()}`,
        shortName: `${MONTHS_SHORT[start.month()]} ${start.year()}`,
      };
    case 'quarterly': {
      const quarter = Math.floor(start.month() / 3) + 1;
      return {
        name: `Q${quarter} ${start.year()}`,
        shortName: `Q${quarter}`,
      };
    }
    case 'half-yearly': {
      const half = start.month() < 6 ? 1 : 2;
      return {
        name: `H${half} ${start.year()}`,
        shortName: `H${half}`,
      };
    }
    case 'yearly':
      return {
        name: `${start.year()}`,
        shortName: `${start.year()}`,
      };
  }
}

/**
 * Get VAT period name
 */
export function getVATPeriodName(
  periodicitet: VATPeriodicitet,
  year: number,
  periodNumber: number
): string {
  const MONTHS = [
    'Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni',
    'Juli', 'August', 'September', 'Oktober', 'November', 'December'
  ];

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

/**
 * Get Danish month name
 */
export function getDanishMonthName(month: number): string {
  const MONTHS = [
    'Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni',
    'Juli', 'August', 'September', 'Oktober', 'November', 'December'
  ];
  return MONTHS[month - 1] || '';
}

// =====================================================
// PERIOD STATUS
// =====================================================

/**
 * Determine period status based on dates
 */
export function determinePeriodStatus(
  startDate: string,
  endDate: string
): PeriodStatus {
  const now = dayjs();
  const start = dayjs(startDate);
  const end = dayjs(endDate);

  if (now.isBefore(start)) return 'future';
  if (now.isBefore(end) || now.isSame(end, 'day')) return 'open';
  return 'closed';
}

/**
 * Determine VAT period status
 */
export function determineVATPeriodStatus(
  startDate: string,
  endDate: string
): 'future' | 'open' | 'closed' {
  const now = dayjs();
  const start = dayjs(startDate);
  const end = dayjs(endDate);

  if (now.isBefore(start)) return 'future';
  if (now.isBefore(end) || now.isSame(end, 'day')) return 'open';
  return 'closed';
}

// =====================================================
// PERIOD LOOKUP
// =====================================================

/**
 * Get period containing a specific date
 */
export function getPeriodForDate(
  date: string,
  periods: AccountingPeriod[]
): AccountingPeriod | undefined {
  const targetDate = dayjs(date);

  return periods.find((period) => {
    const start = dayjs(period.startDate);
    const end = dayjs(period.endDate);
    return (
      (targetDate.isAfter(start) || targetDate.isSame(start, 'day')) &&
      (targetDate.isBefore(end) || targetDate.isSame(end, 'day'))
    );
  });
}

/**
 * Get previous period (for comparison)
 */
export function getPreviousPeriod(
  currentPeriod: AccountingPeriod,
  allPeriods: AccountingPeriod[]
): AccountingPeriod | undefined {
  const currentStart = dayjs(currentPeriod.startDate);

  return allPeriods.find((period) => {
    const periodEnd = dayjs(period.endDate);
    // Previous period ends the day before current period starts
    return periodEnd.add(1, 'day').isSame(currentStart, 'day');
  });
}

/**
 * Get same period from previous year
 */
export function getSamePeriodPreviousYear(
  currentPeriod: AccountingPeriod,
  allPeriods: AccountingPeriod[]
): AccountingPeriod | undefined {
  const currentStart = dayjs(currentPeriod.startDate);
  const previousYearStart = currentStart.subtract(1, 'year');

  return allPeriods.find((period) => {
    const periodStart = dayjs(period.startDate);
    return (
      periodStart.month() === previousYearStart.month() &&
      periodStart.year() === previousYearStart.year() &&
      period.periodNumber === currentPeriod.periodNumber
    );
  });
}

/**
 * Calculate year-to-date range
 */
export function getYearToDateRange(
  currentPeriod: AccountingPeriod,
  fiscalYear: FiscalYear
): { startDate: string; endDate: string } {
  return {
    startDate: fiscalYear.startDate,
    endDate: currentPeriod.endDate,
  };
}

// =====================================================
// PERIOD VALIDATION
// =====================================================

/**
 * Check if a date can be posted to based on period status
 */
export function canPostToDate(
  date: string,
  periods: AccountingPeriod[],
  settings: {
    preventPostingToClosedPeriods: boolean;
    preventPostingToFuturePeriods: boolean;
  }
): { allowed: boolean; reason?: string; reasonDanish?: string } {
  const period = getPeriodForDate(date, periods);

  if (!period) {
    return {
      allowed: false,
      reason: 'No period found for this date',
      reasonDanish: 'Ingen periode fundet for denne dato',
    };
  }

  if (period.status === 'locked') {
    return {
      allowed: false,
      reason: 'Period is locked',
      reasonDanish: 'Perioden er laast',
    };
  }

  if (period.status === 'closed' && settings.preventPostingToClosedPeriods) {
    return {
      allowed: false,
      reason: 'Period is closed',
      reasonDanish: 'Perioden er lukket',
    };
  }

  if (period.status === 'future' && settings.preventPostingToFuturePeriods) {
    return {
      allowed: false,
      reason: 'Cannot post to future periods',
      reasonDanish: 'Kan ikke bogfoere i fremtidige perioder',
    };
  }

  return { allowed: true };
}

/**
 * Validate if period can be closed
 */
export function validatePeriodClose(
  period: AccountingPeriod,
  transactions: Transaction[],
  options?: {
    requireAllReconciled?: boolean;
  }
): PeriodValidationResult {
  const errors: PeriodValidationResult['errors'] = [];
  const warnings: PeriodValidationResult['warnings'] = [];

  // Check if period is already locked
  if (period.status === 'locked') {
    errors.push({
      code: 'PERIOD_LOCKED',
      message: 'Period is already locked and cannot be modified',
      messageDanish: 'Perioden er allerede laast og kan ikke aendres',
    });
  }

  // Check for unreconciled transactions
  if (options?.requireAllReconciled) {
    const unreconciledCount = transactions.filter((tx) => !tx.isReconciled).length;
    if (unreconciledCount > 0) {
      warnings.push({
        code: 'UNRECONCILED_TRANSACTIONS',
        message: `${unreconciledCount} transactions are not reconciled`,
        messageDanish: `${unreconciledCount} transaktioner er ikke afstemt`,
      });
    }
  }

  return {
    isValid: errors.length === 0,
    canPost: period.status !== 'locked',
    errors,
    warnings,
  };
}

// =====================================================
// VAT PERIOD HELPERS
// =====================================================

/**
 * Calculate VAT deadlines based on SKAT rules
 */
export function calculateVATDeadline(
  periodEndDate: string,
  periodicitet: VATPeriodicitet
): { deadline: string; paymentDeadline: string } {
  const endDate = dayjs(periodEndDate);
  let deadlineDays: number;

  switch (periodicitet) {
    case 'monthly':
      deadlineDays = 25;
      break;
    case 'quarterly':
      deadlineDays = 40;
      break;
    case 'half-yearly':
      deadlineDays = 60;
      break;
    case 'yearly':
      deadlineDays = 90;
      break;
  }

  const deadline = endDate.add(deadlineDays, 'day');
  // Payment deadline is typically same as reporting deadline
  const paymentDeadline = deadline;

  return {
    deadline: deadline.format('YYYY-MM-DD'),
    paymentDeadline: paymentDeadline.format('YYYY-MM-DD'),
  };
}

/**
 * Format period for SKAT export
 */
export function formatPeriodForSKAT(
  periodicitet: VATPeriodicitet,
  year: number,
  periodNumber: number
): string {
  switch (periodicitet) {
    case 'monthly':
      return `${year}${String(periodNumber).padStart(2, '0')}`;
    case 'quarterly':
      return `${year}Q${periodNumber}`;
    case 'half-yearly':
      return `${year}H${periodNumber}`;
    case 'yearly':
      return `${year}`;
  }
}

// =====================================================
// FISCAL YEAR HELPERS
// =====================================================

/**
 * Create a new fiscal year
 */
export function createFiscalYear(
  companyId: string,
  startMonth: number,
  year: number
): Omit<FiscalYear, 'id' | 'createdAt' | 'updatedAt'> {
  const startDate = dayjs(`${year}-${String(startMonth).padStart(2, '0')}-01`);
  const endDate = startDate.add(1, 'year').subtract(1, 'day');

  // Determine fiscal year name
  const name = startMonth === 1
    ? `${year}`
    : `${year}/${year + 1}`;

  return {
    companyId,
    name,
    startDate: startDate.format('YYYY-MM-DD'),
    endDate: endDate.format('YYYY-MM-DD'),
    status: 'open',
    openingBalancePosted: false,
  };
}

/**
 * Check if a date is within a fiscal year
 */
export function isDateInFiscalYear(date: string, fiscalYear: FiscalYear): boolean {
  const targetDate = dayjs(date);
  const start = dayjs(fiscalYear.startDate);
  const end = dayjs(fiscalYear.endDate);

  return (
    (targetDate.isAfter(start) || targetDate.isSame(start, 'day')) &&
    (targetDate.isBefore(end) || targetDate.isSame(end, 'day'))
  );
}
