import { eq, sql } from 'drizzle-orm'
import type { Db } from '../client'
import { tenants, tenantSettings } from '../schema/tenants'
import { auditLog } from './_audit-forward'
import { DEFAULT_SEQUENCE_PREFIXES, syncInvoiceSequencePrefixes } from './invoice-sequences'
import {
  INVOICE_SETTINGS_DEFAULTS,
  type InvoiceSettings,
  type InvoiceTaskStatusTrigger,
  type UpdateInvoiceSettingsInput,
} from '@zync/types'

export type InvoiceSettingsObject = InvoiceSettings
export type InvoiceSettingsPatch = UpdateInvoiceSettingsInput
export type { InvoiceTaskStatusTrigger } from '@zync/types'

type TenantSettingsRow = {
  settings: unknown
  logoUrl: string | null
  defaultCurrency: string
  defaultPaymentTermsDays: number | null
  defaultTaxRate: string | number | null
  invoiceNumberPrefix: string | null
  issueTaxInvoices: boolean | null
  proformaNumberPrefix: string | null
  lateFeeType: string | null
  lateFeeAmount: string | number | null
  lateFeeThresholdDays: number | null
  invoiceFooterText: string | null
  invoiceShowPaymentLink: boolean | null
}

function asObject(value: unknown): Record<string, unknown> {
  return value && typeof value === 'object' && !Array.isArray(value)
    ? (value as Record<string, unknown>)
    : {}
}

function asNullableString(value: unknown): string | null {
  return typeof value === 'string' && value.trim().length > 0 ? value : null
}

function asNullableNumber(value: string | number | null | undefined): number | null {
  if (typeof value === 'number' && Number.isFinite(value)) return value
  if (typeof value === 'string' && value.length > 0) {
    const parsed = Number(value)
    return Number.isFinite(parsed) ? parsed : null
  }
  return null
}

function asBoolean(value: unknown): boolean | null {
  return typeof value === 'boolean' ? value : null
}

function readTaskStatusTrigger(settings: unknown): InvoiceTaskStatusTrigger | null {
  const root = asObject(settings)
  const raw = root.task_status_trigger
  const trigger = asObject(raw)
  const enabled = asBoolean(trigger.enabled)
  const statusId = asNullableString(trigger.status_id)
  const invoiceType = trigger.invoice_type

  if (
    enabled == null ||
    statusId == null ||
    (invoiceType !== 'draft' && invoiceType !== 'sent')
  ) {
    return null
  }

  return {
    enabled,
    status_id: statusId,
    invoice_type: invoiceType,
  }
}

export function extractInvoiceBusinessIdentity(
  settings: unknown,
  logoUrl: string | null,
): Pick<
  InvoiceSettings,
  'business_type' | 'business_tax_id' | 'vat_registration_number' | 'logo_url'
> {
  const root = asObject(settings)
  return {
    business_type:
      asNullableString(root.business_type) ??
      asNullableString(root.businessType) ??
      asNullableString(root.company_type),
    business_tax_id:
      asNullableString(root.tax_id) ??
      asNullableString(root.business_tax_id) ??
      asNullableString(root.business_primary_id),
    vat_registration_number:
      asNullableString(root.vat_registration_number) ??
      asNullableString(root.vat_number),
    logo_url: logoUrl ?? asNullableString(root.logo_url),
  }
}

export function normalizeInvoiceSettings(row: TenantSettingsRow | undefined): InvoiceSettings {
  const root = asObject(row?.settings)
  const identity = extractInvoiceBusinessIdentity(root, row?.logoUrl ?? null)
  return {
    default_payment_terms_days:
      row?.defaultPaymentTermsDays ?? INVOICE_SETTINGS_DEFAULTS.default_payment_terms_days,
    default_tax_rate:
      asNullableNumber(row?.defaultTaxRate) ?? INVOICE_SETTINGS_DEFAULTS.default_tax_rate,
    default_currency:
      (row?.defaultCurrency as InvoiceSettings['default_currency']) ??
      INVOICE_SETTINGS_DEFAULTS.default_currency,
    invoice_number_prefix:
      row?.invoiceNumberPrefix ??
      DEFAULT_SEQUENCE_PREFIXES.invoice ??
      INVOICE_SETTINGS_DEFAULTS.invoice_number_prefix,
    issue_tax_invoices:
      row?.issueTaxInvoices ?? INVOICE_SETTINGS_DEFAULTS.issue_tax_invoices,
    proforma_number_prefix:
      row?.proformaNumberPrefix ??
      DEFAULT_SEQUENCE_PREFIXES.proforma ??
      INVOICE_SETTINGS_DEFAULTS.proforma_number_prefix,
    late_fee_type:
      (row?.lateFeeType as InvoiceSettings['late_fee_type']) ??
      INVOICE_SETTINGS_DEFAULTS.late_fee_type,
    late_fee_amount: asNullableNumber(row?.lateFeeAmount),
    late_fee_threshold_days:
      row?.lateFeeThresholdDays ?? INVOICE_SETTINGS_DEFAULTS.late_fee_threshold_days,
    invoice_footer_text: row?.invoiceFooterText ?? INVOICE_SETTINGS_DEFAULTS.invoice_footer_text,
    invoice_show_payment_link:
      row?.invoiceShowPaymentLink ?? INVOICE_SETTINGS_DEFAULTS.invoice_show_payment_link,
    ...identity,
    auto_send_retainer_invoice: asBoolean(root.auto_send_retainer_invoice) ?? false,
    task_status_trigger: readTaskStatusTrigger(root),
  }
}

export async function getInvoiceSettings(
  db: Db,
  tenantId: string,
): Promise<InvoiceSettingsObject> {
  const [row] = await db
    .select({
      settings: tenants.settings,
      logoUrl: tenants.logoUrl,
      defaultCurrency: tenants.defaultCurrency,
      defaultPaymentTermsDays: tenantSettings.defaultPaymentTermsDays,
      defaultTaxRate: tenantSettings.defaultTaxRate,
      invoiceNumberPrefix: tenantSettings.invoiceNumberPrefix,
      issueTaxInvoices: tenantSettings.issueTaxInvoices,
      proformaNumberPrefix: tenantSettings.proformaNumberPrefix,
      lateFeeType: tenantSettings.lateFeeType,
      lateFeeAmount: tenantSettings.lateFeeAmount,
      lateFeeThresholdDays: tenantSettings.lateFeeThresholdDays,
      invoiceFooterText: tenantSettings.invoiceFooterText,
      invoiceShowPaymentLink: tenantSettings.invoiceShowPaymentLink,
    })
    .from(tenants)
    .leftJoin(tenantSettings, eq(tenantSettings.tenantId, tenants.id))
    .where(eq(tenants.id, tenantId))
    .limit(1)

  return normalizeInvoiceSettings(row)
}

export async function updateInvoiceSettings(
  db: Db,
  tenantId: string,
  actorId: string,
  patch: InvoiceSettingsPatch,
): Promise<InvoiceSettingsObject> {
  const current = await getInvoiceSettings(db, tenantId)
  const invoicePrefix = patch.invoice_number_prefix ?? current.invoice_number_prefix
  const proformaPrefix = patch.proforma_number_prefix ?? current.proforma_number_prefix

  await db.transaction(async (tx) => {
    if (patch.default_currency !== undefined) {
      await tx
        .update(tenants)
        .set({ defaultCurrency: patch.default_currency })
        .where(eq(tenants.id, tenantId))
    }

    const values = {
      tenantId,
      defaultPaymentTermsDays:
        patch.default_payment_terms_days ?? current.default_payment_terms_days,
      defaultTaxRate: String(patch.default_tax_rate ?? current.default_tax_rate),
      invoiceNumberPrefix: invoicePrefix,
      issueTaxInvoices: patch.issue_tax_invoices ?? current.issue_tax_invoices,
      proformaNumberPrefix: proformaPrefix,
      lateFeeType: patch.late_fee_type ?? current.late_fee_type,
      lateFeeAmount:
        patch.late_fee_amount === undefined
          ? current.late_fee_amount == null
            ? null
            : String(current.late_fee_amount)
          : patch.late_fee_amount == null
            ? null
            : String(patch.late_fee_amount),
      lateFeeThresholdDays:
        patch.late_fee_threshold_days === undefined
          ? current.late_fee_threshold_days
          : patch.late_fee_threshold_days,
      invoiceFooterText:
        patch.invoice_footer_text === undefined
          ? current.invoice_footer_text
          : patch.invoice_footer_text,
      invoiceShowPaymentLink:
        patch.invoice_show_payment_link ?? current.invoice_show_payment_link,
      updatedAt: sql`now()`,
    }

    await tx
      .insert(tenantSettings)
      .values(values)
      .onConflictDoUpdate({
        target: tenantSettings.tenantId,
        set: {
          defaultPaymentTermsDays: values.defaultPaymentTermsDays,
          defaultTaxRate: values.defaultTaxRate,
          invoiceNumberPrefix: values.invoiceNumberPrefix,
          issueTaxInvoices: values.issueTaxInvoices,
          proformaNumberPrefix: values.proformaNumberPrefix,
          lateFeeType: values.lateFeeType,
          lateFeeAmount: values.lateFeeAmount,
          lateFeeThresholdDays: values.lateFeeThresholdDays,
          invoiceFooterText: values.invoiceFooterText,
          invoiceShowPaymentLink: values.invoiceShowPaymentLink,
          updatedAt: sql`now()`,
        },
      })

    await syncInvoiceSequencePrefixes(tx, tenantId, {
      invoice: invoicePrefix,
      proforma: proformaPrefix,
      credit_note: DEFAULT_SEQUENCE_PREFIXES.credit_note,
    })

    await tx.insert(auditLog).values({
      tenantId,
      actorId,
      actorType: 'user',
      entityType: 'invoice_settings',
      entityId: tenantId,
      action: 'update',
      changes: null,
    })
  })

  return getInvoiceSettings(db, tenantId)
}
