import type { NotificationChannel } from '@rentaldrivego/database'
import { prisma } from '../lib/prisma'

export const SUPPORTED_NOTIFICATION_LOCALES = ['en', 'fr', 'ar'] as const

export type NotificationLocale = (typeof SUPPORTED_NOTIFICATION_LOCALES)[number]

export const DEFAULT_NOTIFICATION_LOCALE: NotificationLocale = 'en'

const localeConfig: Record<NotificationLocale, { intl: string; direction: 'ltr' | 'rtl' }> = {
  en: { intl: 'en-US', direction: 'ltr' },
  fr: { intl: 'fr-FR', direction: 'ltr' },
  ar: { intl: 'ar-MA', direction: 'rtl' },
}

const subscriptionStatusLabels: Record<NotificationLocale, Record<string, string>> = {
  en: {
    trialing: 'Trial active',
    active: 'Active',
    payment_pending: 'Payment pending',
    past_due: 'Payment overdue',
    suspended: 'Suspended',
    cancelled: 'Canceled',
    canceled: 'Canceled',
    expired: 'Expired',
  },
  fr: {
    trialing: 'Essai actif',
    active: 'Actif',
    payment_pending: 'Paiement en attente',
    past_due: 'Paiement en retard',
    suspended: 'Suspendu',
    cancelled: 'Annule',
    canceled: 'Annule',
    expired: 'Expire',
  },
  ar: {
    trialing: 'الفترة التجريبية نشطة',
    active: 'نشط',
    payment_pending: 'الدفع قيد الانتظار',
    past_due: 'الدفع متأخر',
    suspended: 'معلّق',
    cancelled: 'ملغى',
    canceled: 'ملغى',
    expired: 'منتهي',
  },
}

const invoiceStatusLabels: Record<NotificationLocale, Record<string, string>> = {
  en: {
    draft: 'Preparing',
    open: 'Due',
    payment_pending: 'Payment processing',
    paid: 'Paid',
    partially_paid: 'Partially paid',
    past_due: 'Past due',
    void: 'Canceled',
    uncollectible: 'Contact support',
    refunded: 'Refunded',
    partially_refunded: 'Partially refunded',
  },
  fr: {
    draft: 'En preparation',
    open: 'A payer',
    payment_pending: 'Paiement en cours',
    paid: 'Payee',
    partially_paid: 'Partiellement payee',
    past_due: 'En retard',
    void: 'Annulee',
    uncollectible: 'Contacter le support',
    refunded: 'Remboursee',
    partially_refunded: 'Partiellement remboursee',
  },
  ar: {
    draft: 'قيد الإعداد',
    open: 'مستحقة الدفع',
    payment_pending: 'الدفع قيد المعالجة',
    paid: 'مدفوعة',
    partially_paid: 'مدفوعة جزئياً',
    past_due: 'متأخرة',
    void: 'ملغاة',
    uncollectible: 'تواصل مع الدعم',
    refunded: 'مستردة',
    partially_refunded: 'مستردة جزئياً',
  },
}

const billingPeriodLabels: Record<NotificationLocale, Record<string, string>> = {
  en: {
    monthly: 'monthly',
    annual: 'annual',
  },
  fr: {
    monthly: 'mensuel',
    annual: 'annuel',
  },
  ar: {
    monthly: 'شهري',
    annual: 'سنوي',
  },
}

const planLabels: Record<NotificationLocale, Record<string, string>> = {
  en: {
    starter: 'Starter',
    growth: 'Growth',
    pro: 'Pro',
  },
  fr: {
    starter: 'Starter',
    growth: 'Growth',
    pro: 'Pro',
  },
  ar: {
    starter: 'Starter',
    growth: 'Growth',
    pro: 'Pro',
  },
}

type TemplateDateValue = {
  type: 'date'
  value: Date | string | number
  options?: Intl.DateTimeFormatOptions
}

type TemplateCurrencyValue = {
  type: 'currency'
  amountMinor: number
  currency: string
}

type TemplateSubscriptionStatusValue = {
  type: 'subscription_status'
  status: string
}

type TemplateInvoiceStatusValue = {
  type: 'invoice_status'
  status: string
}

export type NotificationTemplateValue =
  | string
  | number
  | boolean
  | Date
  | null
  | undefined
  | TemplateDateValue
  | TemplateCurrencyValue
  | TemplateSubscriptionStatusValue
  | TemplateInvoiceStatusValue

export type NotificationTemplateVariables = Record<string, NotificationTemplateValue>

type NotificationLocalizationDb = Pick<
  typeof prisma,
  'brandSettings' | 'billingAccount' | 'employee' | 'notificationTemplate' | 'renter'
>

type NotificationTemplateRecord = {
  templateKey: string
  channel: NotificationChannel
  locale: string
  subject: string | null
  body: string
  requiredVariables: unknown
  optionalVariables: unknown
  version: number
}

export function coerceNotificationLocale(locale?: string | null): NotificationLocale {
  if (locale && SUPPORTED_NOTIFICATION_LOCALES.includes(locale as NotificationLocale)) {
    return locale as NotificationLocale
  }

  return DEFAULT_NOTIFICATION_LOCALE
}

export function getNotificationTextDirection(locale: NotificationLocale) {
  return localeConfig[locale].direction
}

export function formatLocalizedDate(
  value: Date | string | number,
  locale: NotificationLocale,
  options?: Intl.DateTimeFormatOptions,
) {
  const date = value instanceof Date ? value : new Date(value)
  return new Intl.DateTimeFormat(localeConfig[locale].intl, options ?? {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  }).format(date)
}

export function formatLocalizedCurrency(amountMinor: number, currency: string, locale: NotificationLocale) {
  return new Intl.NumberFormat(localeConfig[locale].intl, {
    style: 'currency',
    currency,
  }).format(amountMinor / 100)
}

export function localizeSubscriptionStatus(status: string, locale: NotificationLocale) {
  const normalized = status.toLowerCase()
  return subscriptionStatusLabels[locale][normalized] ?? status
}

export function localizeInvoiceStatus(status: string, locale: NotificationLocale) {
  const normalized = status.toLowerCase()
  return invoiceStatusLabels[locale][normalized] ?? status
}

export function localizeBillingPeriod(period: string, locale: NotificationLocale) {
  const normalized = period.toLowerCase()
  return billingPeriodLabels[locale][normalized] ?? period
}

export function localizePlanName(plan: string, locale: NotificationLocale) {
  const normalized = plan.toLowerCase()
  return planLabels[locale][normalized] ?? plan
}

function isTemplateDateValue(value: NotificationTemplateValue): value is TemplateDateValue {
  return typeof value === 'object' && value !== null && 'type' in value && value.type === 'date'
}

function isTemplateCurrencyValue(value: NotificationTemplateValue): value is TemplateCurrencyValue {
  return typeof value === 'object' && value !== null && 'type' in value && value.type === 'currency'
}

function isTemplateSubscriptionStatusValue(value: NotificationTemplateValue): value is TemplateSubscriptionStatusValue {
  return typeof value === 'object' && value !== null && 'type' in value && value.type === 'subscription_status'
}

function isTemplateInvoiceStatusValue(value: NotificationTemplateValue): value is TemplateInvoiceStatusValue {
  return typeof value === 'object' && value !== null && 'type' in value && value.type === 'invoice_status'
}

function formatTemplateValue(value: NotificationTemplateValue, locale: NotificationLocale): string {
  if (value instanceof Date) return formatLocalizedDate(value, locale)
  if (isTemplateDateValue(value)) return formatLocalizedDate(value.value, locale, value.options)
  if (isTemplateCurrencyValue(value)) return formatLocalizedCurrency(value.amountMinor, value.currency, locale)
  if (isTemplateSubscriptionStatusValue(value)) return localizeSubscriptionStatus(value.status, locale)
  if (isTemplateInvoiceStatusValue(value)) return localizeInvoiceStatus(value.status, locale)
  if (value === null || value === undefined) return ''
  return String(value)
}

function normalizeTemplateVariableList(value: unknown): string[] {
  if (!Array.isArray(value)) return []
  return value.filter((item): item is string => typeof item === 'string')
}

function escapeHtml(value: string) {
  return value
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
}

async function findTemplate(
  db: NotificationLocalizationDb,
  templateKey: string,
  channel: NotificationChannel,
  locale: NotificationLocale,
): Promise<NotificationTemplateRecord | null> {
  return db.notificationTemplate.findFirst({
    where: { templateKey, channel, locale, isActive: true },
    orderBy: { version: 'desc' },
    select: {
      templateKey: true,
      channel: true,
      locale: true,
      subject: true,
      body: true,
      requiredVariables: true,
      optionalVariables: true,
      version: true,
    },
  })
}

export async function resolveNotificationLocale(input: {
  companyId?: string | null
  employeeId?: string | null
  renterId?: string | null
  billingAccountId?: string | null
  locale?: string | null
}, db: NotificationLocalizationDb = prisma): Promise<NotificationLocale> {
  if (input.locale) return coerceNotificationLocale(input.locale)

  const employeePromise = input.employeeId
    ? db.employee.findUnique({
        where: { id: input.employeeId },
        select: { preferredLanguage: true },
      })
    : Promise.resolve(null)

  const renterPromise = input.renterId
    ? db.renter.findUnique({
        where: { id: input.renterId },
        select: { preferredLocale: true },
      })
    : Promise.resolve(null)

  const workspacePromise = input.companyId
    ? db.brandSettings.findUnique({
        where: { companyId: input.companyId },
        select: { defaultLocale: true },
      })
    : Promise.resolve(null)

  const billingPromise = input.billingAccountId
    ? db.billingAccount.findUnique({
        where: { id: input.billingAccountId },
        select: { preferredLanguage: true },
      })
    : input.companyId
      ? db.billingAccount.findFirst({
          where: { companyId: input.companyId, isPrimary: true },
          select: { preferredLanguage: true },
          orderBy: { createdAt: 'asc' },
        })
      : Promise.resolve(null)

  const [employee, renter, workspace, billingAccount] = await Promise.all([
    employeePromise,
    renterPromise,
    workspacePromise,
    billingPromise,
  ])

  return coerceNotificationLocale(
    employee?.preferredLanguage ??
      renter?.preferredLocale ??
      workspace?.defaultLocale ??
      billingAccount?.preferredLanguage ??
      DEFAULT_NOTIFICATION_LOCALE,
  )
}

export async function resolveNotificationTemplate(input: {
  templateKey: string
  channel: NotificationChannel
  locale: NotificationLocale
  variables?: NotificationTemplateVariables
}, db: NotificationLocalizationDb = prisma) {
  const requested = await findTemplate(db, input.templateKey, input.channel, input.locale)

  if (requested) {
    return {
      locale: coerceNotificationLocale(requested.locale),
      subject: renderNotificationText(
        requested.subject,
        input.variables,
        normalizeTemplateVariableList(requested.requiredVariables),
        input.locale,
      ),
      body: renderNotificationText(
        requested.body,
        input.variables,
        normalizeTemplateVariableList(requested.requiredVariables),
        input.locale,
      ),
      templateKey: requested.templateKey,
      usedFallback: false,
    }
  }

  if (input.locale !== DEFAULT_NOTIFICATION_LOCALE) {
    console.warn(
      `[Notifications] Missing ${input.locale} template for ${input.templateKey}/${input.channel}; falling back to ${DEFAULT_NOTIFICATION_LOCALE}.`,
    )
  }

  const fallback = await findTemplate(db, input.templateKey, input.channel, DEFAULT_NOTIFICATION_LOCALE)
  if (!fallback) {
    throw new Error(
      `Missing notification template for ${input.templateKey}/${input.channel} in ${input.locale} and ${DEFAULT_NOTIFICATION_LOCALE}`,
    )
  }

  return {
    locale: DEFAULT_NOTIFICATION_LOCALE,
    subject: renderNotificationText(
      fallback.subject,
      input.variables,
      normalizeTemplateVariableList(fallback.requiredVariables),
      DEFAULT_NOTIFICATION_LOCALE,
    ),
    body: renderNotificationText(
      fallback.body,
      input.variables,
      normalizeTemplateVariableList(fallback.requiredVariables),
      DEFAULT_NOTIFICATION_LOCALE,
    ),
    templateKey: fallback.templateKey,
    usedFallback: true,
  }
}

export function renderNotificationText(
  template: string | null,
  variables: NotificationTemplateVariables = {},
  requiredVariables: string[] = [],
  locale: NotificationLocale,
) {
  if (!template) return null

  for (const variableName of requiredVariables) {
    if (variables[variableName] === undefined || variables[variableName] === null) {
      throw new Error(`Missing notification template variable: ${variableName}`)
    }
  }

  return template.replace(/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g, (_match, variableName: string) =>
    formatTemplateValue(variables[variableName], locale),
  )
}

export function renderLocalizedEmailHtml(body: string, locale: NotificationLocale) {
  const direction = getNotificationTextDirection(locale)
  const textAlign = direction === 'rtl' ? 'right' : 'left'
  const paragraphs = body
    .split(/\n{2,}/)
    .map((paragraph) => `<p style="margin:0 0 16px;text-align:${textAlign};">${escapeHtml(paragraph).replace(/\n/g, '<br />')}</p>`)
    .join('')

  return [
    `<html lang="${locale}" dir="${direction}">`,
    `<body style="font-family:sans-serif;max-width:560px;margin:0 auto;padding:32px;color:#1c1917;direction:${direction};text-align:${textAlign};">`,
    paragraphs,
    '</body>',
    '</html>',
  ].join('')
}
