import {
  FixedDebt,
  VariableDebt,
  CreditCard,
  MonthlyBudget,
  Alert,
} from './types'
import {
  getDaysUntil,
  getNextDateByDay,
  formatCurrency,
  calculateTotalFixedDebts,
  calculateTotalVariableDebts,
} from './utils'

export interface GenerateAlertsParams {
  fixedDebts: FixedDebt[]
  variableDebts: VariableDebt[]
  creditCards: CreditCard[]
  monthlyBudgets: MonthlyBudget[]
  currentMonth: number
  currentYear: number
}

interface AlertDraft {
  type: Alert['type']
  title: string
  message: string
  severity: Alert['severity']
  relatedId?: string
}

/**
 * Obtiene las deudas fijas no pagadas que vencen en los próximos N días
 */
export function getUpcomingFixedDebts(
  fixedDebts: FixedDebt[],
  days: number
): Array<{ debt: FixedDebt; daysUntil: number; dueDate: Date }> {
  const today = new Date()
  const currentDay = today.getDate()
  const currentMonth = today.getMonth()
  const currentYear = today.getFullYear()

  return fixedDebts
    .filter((debt) => !debt.isPaid)
    .map((debt) => {
      // Calcular la fecha de vencimiento para este mes
      let dueDate = new Date(currentYear, currentMonth, debt.dueDay)

      // Si ya pasó, calcular para el mes siguiente
      if (currentDay > debt.dueDay) {
        dueDate = new Date(currentYear, currentMonth + 1, debt.dueDay)
      }

      const daysUntil = getDaysUntil(dueDate)

      return { debt, daysUntil, dueDate }
    })
    .filter(({ daysUntil }) => daysUntil >= 0 && daysUntil <= days)
    .sort((a, b) => a.daysUntil - b.daysUntil)
}

/**
 * Obtiene el presupuesto del mes actual
 */
export function getCurrentMonthBudget(
  monthlyBudgets: MonthlyBudget[],
  month: number,
  year: number
): MonthlyBudget | null {
  return (
    monthlyBudgets.find(
      (budget) => budget.month === month && budget.year === year
    ) || null
  )
}

/**
 * Calcula el gasto actual del mes (deudas fijas + variables no pagadas)
 */
export function calculateCurrentSpending(
  fixedDebts: FixedDebt[],
  variableDebts: VariableDebt[]
): number {
  const fixedSpending = calculateTotalFixedDebts(fixedDebts)
  const variableSpending = calculateTotalVariableDebts(variableDebts)

  return fixedSpending + variableSpending
}

interface CardEvent {
  card: CreditCard
  type: 'closing' | 'due'
  daysUntil: number
  date: Date
}

/**
 * Obtiene los eventos próximos de tarjetas (cierre o vencimiento)
 */
export function getUpcomingCardEvents(
  creditCards: CreditCard[],
  days: number
): CardEvent[] {
  const events: CardEvent[] = []

  for (const card of creditCards) {
    // Calcular próximo cierre
    const closingDate = getNextDateByDay(card.closingDay)
    const daysUntilClosing = getDaysUntil(closingDate)

    if (daysUntilClosing >= 0 && daysUntilClosing <= days) {
      events.push({
        card,
        type: 'closing',
        daysUntil: daysUntilClosing,
        date: closingDate,
      })
    }

    // Calcular próximo vencimiento
    const dueDate = getNextDateByDay(card.dueDay)
    const daysUntilDue = getDaysUntil(dueDate)

    if (daysUntilDue >= 0 && daysUntilDue <= days) {
      events.push({
        card,
        type: 'due',
        daysUntil: daysUntilDue,
        date: dueDate,
      })
    }
  }

  // Ordenar por días hasta el evento
  return events.sort((a, b) => a.daysUntil - b.daysUntil)
}

/**
 * Genera alertas de pagos próximos (deudas fijas)
 */
function generatePaymentDueAlerts(fixedDebts: FixedDebt[]): AlertDraft[] {
  const upcomingDebts = getUpcomingFixedDebts(fixedDebts, 3)
  const alerts: AlertDraft[] = []

  for (const { debt, daysUntil } of upcomingDebts) {
    const severity: Alert['severity'] =
      daysUntil <= 1 ? 'danger' : 'warning'

    const daysText = daysUntil === 0 ? 'hoy' : daysUntil === 1 ? 'mañana' : `en ${daysUntil} días`

    alerts.push({
      type: 'PAYMENT_DUE',
      title: 'Pago próximo',
      message: `'${debt.name}' vence ${daysText}: ${formatCurrency(debt.amount)}`,
      severity,
      relatedId: debt.id,
    })
  }

  return alerts
}

/**
 * Genera alertas de presupuesto
 */
function generateBudgetAlerts(
  fixedDebts: FixedDebt[],
  variableDebts: VariableDebt[],
  monthlyBudgets: MonthlyBudget[],
  currentMonth: number,
  currentYear: number
): AlertDraft[] {
  const currentBudget = getCurrentMonthBudget(
    monthlyBudgets,
    currentMonth,
    currentYear
  )

  if (!currentBudget) {
    return []
  }

  const totalBudget =
    currentBudget.fixedExpenses + currentBudget.variableExpenses

  if (totalBudget <= 0) {
    return []
  }

  const currentSpending = calculateCurrentSpending(fixedDebts, variableDebts)
  const percentageUsed = (currentSpending / totalBudget) * 100

  if (percentageUsed < 80) {
    return []
  }

  const severity: Alert['severity'] =
    percentageUsed > 95 ? 'danger' : 'warning'

  return [
    {
      type: 'BUDGET_WARNING',
      title: 'Presupuesto al límite',
      message: `Has usado el ${percentageUsed.toFixed(1)}% de tu presupuesto mensual`,
      severity,
    },
  ]
}

/**
 * Genera alertas de eventos de tarjetas (cierre y vencimiento)
 */
function generateCardAlerts(creditCards: CreditCard[]): AlertDraft[] {
  const events = getUpcomingCardEvents(creditCards, 3)
  const closingAlerts: AlertDraft[] = []
  const dueAlerts: AlertDraft[] = []

  for (const event of events) {
    if (event.type === 'closing') {
      const daysText =
        event.daysUntil === 0
          ? 'hoy'
          : event.daysUntil === 1
            ? 'mañana'
            : `en ${event.daysUntil} días`

      closingAlerts.push({
        type: 'CARD_CLOSING',
        title: 'Cierre de tarjeta próximo',
        message: `Tu tarjeta ${event.card.name} cierra ${daysText}. Balance: ${formatCurrency(event.card.currentBalance)}`,
        severity: 'info',
        relatedId: event.card.id,
      })
    } else {
      const severity: Alert['severity'] =
        event.daysUntil <= 2 ? 'warning' : 'info'

      const daysText =
        event.daysUntil === 0
          ? 'hoy'
          : event.daysUntil === 1
            ? 'mañana'
            : `en ${event.daysUntil} días`

      dueAlerts.push({
        type: 'CARD_DUE',
        title: 'Vencimiento de tarjeta',
        message: `Vencimiento de ${event.card.name} ${daysText}. Balance: ${formatCurrency(event.card.currentBalance)}`,
        severity,
        relatedId: event.card.id,
      })
    }
  }

  return [...closingAlerts, ...dueAlerts]
}

/**
 * Genera alertas de meta de ahorro
 */
function generateSavingsAlerts(
  fixedDebts: FixedDebt[],
  variableDebts: VariableDebt[],
  monthlyBudgets: MonthlyBudget[],
  currentMonth: number,
  currentYear: number
): AlertDraft[] {
  const currentBudget = getCurrentMonthBudget(
    monthlyBudgets,
    currentMonth,
    currentYear
  )

  if (!currentBudget || currentBudget.savingsGoal <= 0) {
    return []
  }

  const currentSpending = calculateCurrentSpending(fixedDebts, variableDebts)
  const projectedSavings = currentBudget.totalIncome - currentSpending

  if (projectedSavings >= currentBudget.savingsGoal) {
    return []
  }

  const percentageBelow =
    ((currentBudget.savingsGoal - projectedSavings) / currentBudget.savingsGoal) *
    100

  return [
    {
      type: 'SAVINGS_GOAL',
      title: 'Meta de ahorro',
      message: `Vas ${percentageBelow.toFixed(0)}% por debajo de tu meta de ahorro mensual`,
      severity: 'info',
    },
  ]
}

/**
 * Elimina alertas duplicadas basándose en tipo y relatedId
 */
function deduplicateAlerts(alerts: AlertDraft[]): AlertDraft[] {
  const seen = new Set<string>()

  return alerts.filter((alert) => {
    const key = `${alert.type}-${alert.relatedId || 'global'}`

    if (seen.has(key)) {
      return false
    }

    seen.add(key)
    return true
  })
}

/**
 * Genera todas las alertas inteligentes basadas en el estado actual
 */
export function generateAlerts(params: GenerateAlertsParams): AlertDraft[] {
  const {
    fixedDebts,
    variableDebts,
    creditCards,
    monthlyBudgets,
    currentMonth,
    currentYear,
  } = params

  const allAlerts: AlertDraft[] = [
    ...generatePaymentDueAlerts(fixedDebts),
    ...generateBudgetAlerts(
      fixedDebts,
      variableDebts,
      monthlyBudgets,
      currentMonth,
      currentYear
    ),
    ...generateCardAlerts(creditCards),
    ...generateSavingsAlerts(
      fixedDebts,
      variableDebts,
      monthlyBudgets,
      currentMonth,
      currentYear
    ),
  ]

  // Eliminar duplicados y ordenar por severidad (danger > warning > info)
  const uniqueAlerts = deduplicateAlerts(allAlerts)

  const severityOrder = { danger: 0, warning: 1, info: 2 }

  return uniqueAlerts.sort(
    (a, b) => severityOrder[a.severity] - severityOrder[b.severity]
  )
}
