/**
 * license-transform.ts
 *
 * Transformationsschicht: Wandelt den Wizard-Zustand in strukturierte
 * Datenstrukturen für DB-Speicherung (OrderSnapshot) und Lizenzserver (LicenseBundle) um.
 */

import type {
  Category,
  CategorySelection,
  CustomerSnapshot,
  EndCustomer,
  LicenseBundle,
  LicenseOption,
  OrderItem,
  OrderSnapshot,
  Product,
  Profile,
  WizardSelections,
} from '@/lib/types'

// ─── Hilfsfunktionen ─────────────────────────────────────────────────────────

/**
 * Wandelt einen Produktnamen in einen stabilen, URL-sicheren Slug um.
 * Beispiel: "CASPOS Basic" → "caspos_basic"
 */
export function slugify(name: string): string {
  return name
    .toLowerCase()
    .replace(/ä/g, 'ae')
    .replace(/ö/g, 'oe')
    .replace(/ü/g, 'ue')
    .replace(/ß/g, 'ss')
    .replace(/[^a-z0-9]+/g, '_')
    .replace(/^_|_$/g, '')
}

/**
 * Baut einen validen CustomerSnapshot aus einem Profil oder Formulardaten.
 *
 * Wenn ein Endkunde übergeben wird, werden DESSEN Daten eingefroren (für die Rechnung).
 * Ohne Endkunden-Übergabe: Partner-Profil als Fallback (Abwärtskompatibilität).
 * Fehlende Pflichtfelder werden mit leerem String aufgefüllt (Validierung im Wizard).
 */
export function buildCustomerSnapshot(
  profile: Partial<Profile>,
  endCustomer?: EndCustomer | null
): CustomerSnapshot {
  if (endCustomer) {
    return {
      schema_version: 1,
      end_customer_id: endCustomer.id,
      company_name: endCustomer.company_name ?? '',
      vat_id: endCustomer.vat_id ?? '',
      first_name: endCustomer.first_name ?? '',
      last_name: endCustomer.last_name ?? '',
      address: endCustomer.street ?? '',
      zip_code: endCustomer.zip ?? '',
      city: endCustomer.city ?? '',
      bank_iban: endCustomer.bank_iban ?? undefined,
      bank_bic: endCustomer.bank_bic ?? undefined,
      bank_name: endCustomer.bank_name ?? undefined,
      bank_owner: endCustomer.bank_owner ?? undefined,
    }
  }
  return {
    schema_version: 1,
    company_name: profile.company_name ?? '',
    vat_id: profile.vat_id ?? '',
    first_name: profile.first_name ?? '',
    last_name: profile.last_name ?? '',
    address: profile.address ?? '',
    zip_code: profile.zip_code ?? '',
    city: profile.city ?? '',
    ...(profile.email ? { email: profile.email } : {}),
  }
}

// ─── OrderSnapshot ────────────────────────────────────────────────────────────

/**
 * Baut den unveränderlichen OrderSnapshot für die JSONB-Spalte `orders.order_data`.
 *
 * WICHTIG: Alle Namen und Preise werden hier eingefroren (Snapshot-Prinzip).
 * Spätere Änderungen am Produktkatalog brechen keine historischen Bestellungen.
 */
export function buildOrderSnapshot(
  selections: WizardSelections,
  products: Product[],
  categories: Category[],
  billingInterval?: 'one_time' | 'monthly',
  moduleQuantities?: Record<string, number>,
  lastLicenseDate?: string | null
): OrderSnapshot {
  const items: OrderItem[] = []
  let subtotal = 0
  let dominantTaxRate = 19 // Fallback; wird durch das erste Produkt mit tax_rate überschrieben

  for (const cat of categories) {
    const sel: CategorySelection | undefined = selections[cat.id]
    if (!sel) continue

    const selectedProds: Product[] = []
    if (cat.allow_multiselect && sel.productIds && sel.productIds.length > 0) {
      sel.productIds.forEach(pId => {
        const p = products.find(prod => prod.id === pId)
        if (p) selectedProds.push(p)
      })
    } else if (sel.productId) {
      const p = products.find(prod => prod.id === sel.productId)
      if (p) selectedProds.push(p)
    }

    if (selectedProds.length === 0) continue

    // Apply free items limit: sort selected products by base_price ascending so that the cheapest are free
    const sortedProds = [...selectedProds].sort((a, b) => a.base_price - b.base_price)
    const freeLimit = cat.allow_multiselect ? cat.free_items_limit : 0

    sortedProds.forEach((prod, sortedIndex) => {
      dominantTaxRate = prod.tax_rate

      const selectedModules = sel.moduleIds
        .map((modId) => prod.modules?.find((m) => m.id === modId))
        .filter((m): m is NonNullable<typeof m> => !!m)
        .map((mod) => {
          const qty = moduleQuantities?.[mod.id] || 1
          return {
            module_id: mod.id,
            module_name: mod.name,
            price: mod.price,
            quantity: qty,
            total_price: mod.price * qty,
          }
        })

      const moduleTotal = selectedModules.reduce((acc, m) => acc + (m.total_price || m.price), 0)

      // If this product falls under the free limit, set its base price to 0
      const actualBasePrice = (sortedIndex < freeLimit) ? 0 : prod.base_price
      const itemTotal = actualBasePrice + moduleTotal
      subtotal += itemTotal

      items.push({
        category_id: cat.id,
        category_name: cat.name,
        product_id: prod.id,
        product_name: prod.name + (actualBasePrice === 0 ? "" : ""),
        base_price: actualBasePrice,
        billing_interval: prod.billing_interval,
        selected_modules: selectedModules,
        item_total: itemTotal,
      })
    })
  }

  // Wenn billingInterval übergeben, dieses nutzen; sonst automatisch ermitteln
  const billingCycle = billingInterval || (items.every((i) => i.billing_interval === 'one_time')
    ? 'one_time'
    : 'monthly')

  let multiplier = 1
  const hasOneTime = items.some(item => item.billing_interval === 'one_time')
  if (lastLicenseDate && hasOneTime) {
    const lastDate = new Date(lastLicenseDate)
    const today = new Date()
    const yearsDiff = today.getFullYear() - lastDate.getFullYear()
    const monthsDiff = today.getMonth() - lastDate.getMonth()
    const months = yearsDiff * 12 + monthsDiff

    if (months <= 12) {
      multiplier = 0
    } else if (months <= 14) {
      multiplier = 0.15
    } else if (months <= 25) {
      multiplier = 0.30
    } else if (months <= 37) {
      multiplier = 0.45
    } else if (months <= 49) {
      multiplier = 0.60
    } else {
      multiplier = 0.90
    }

    // Multiply only one_time item totals by multiplier if product allows it
    items.forEach(item => {
      if (item.billing_interval === 'one_time') {
        const prod = products.find(p => p.id === item.product_id);
        const allowDiscount = prod ? prod.allow_update_discount !== false : true;
        if (allowDiscount) {
          item.item_total = item.item_total * multiplier;
        }
      }
    })
    subtotal = items.reduce((acc, item) => acc + item.item_total, 0)
  }

  const taxAmount = Math.round(subtotal * (dominantTaxRate / 100) * 100) / 100
  const total = Math.round((subtotal + taxAmount) * 100) / 100

  return {
    schema_version: 1,
    billing_cycle: billingCycle,
    items,
    subtotal,
    tax_rate: dominantTaxRate,
    tax_amount: taxAmount,
    total,
    last_license_date: lastLicenseDate || null,
    price_multiplier: lastLicenseDate && hasOneTime ? multiplier : null,
  }
}

// ─── LicenseBundle ────────────────────────────────────────────────────────────

/**
 * Transformiert einen gespeicherten OrderSnapshot (oder den aktuellen Wizard-Zustand)
 * in ein LicenseBundle für den Lizenzserver / das ERP-System.
 *
 * Jedes Produkt und jedes Modul wird auf einen stabilen `key` (Slug) gemappt,
 * der als Identifier im Lizenzserver verwendet werden kann.
 */
export function transformToLicenseBundle(
  selections: WizardSelections,
  products: Product[],
  categories: Category[],
  customer: Partial<Profile>,
  moduleQuantities?: Record<string, number>
): LicenseBundle {
  const options: LicenseOption[] = []
  let monthlyTotal = 0
  let oneTimeTotal = 0

  for (const cat of categories) {
    const sel: CategorySelection | undefined = selections[cat.id]
    if (!sel?.productId) continue

    const prod = products.find((p) => p.id === sel.productId)
    if (!prod) continue

    const prodKey = slugify(prod.name)

    options.push({
      key: prodKey,
      label: prod.name,
      active: true,
      billing: prod.billing_interval,
      price_net: prod.base_price,
    })

    if (prod.billing_interval === 'monthly') monthlyTotal += prod.base_price
    else oneTimeTotal += prod.base_price

    for (const modId of sel.moduleIds) {
      const mod = prod.modules?.find((m) => m.id === modId)
      if (!mod) continue
      const qty = moduleQuantities?.[mod.id] || 1

      options.push({
        key: `${prodKey}__${slugify(mod.name)}`,
        label: mod.name,
        active: true,
        // Module erben das Billing-Intervall des übergeordneten Produkts
        billing: prod.billing_interval,
        price_net: mod.price,
        quantity: qty,
      })

      if (prod.billing_interval === 'monthly') monthlyTotal += mod.price * qty
      else oneTimeTotal += mod.price * qty
    }
  }

  return {
    customer_ref: customer.company_name ?? customer.last_name ?? 'unknown',
    generated_at: new Date().toISOString(),
    license_key_prefix: `CASP-${new Date().getFullYear()}-`,
    options,
    monthly_total_net: monthlyTotal,
    one_time_total_net: oneTimeTotal,
  }
}

/**
 * Rekonstruiert ein LicenseBundle direkt aus einem gespeicherten OrderSnapshot
 * (ohne Zugriff auf den Live-Katalog – gut für historische Bestellungen).
 */
export function licenseBundleFromSnapshot(
  snapshot: OrderSnapshot,
  customerRef: string
): LicenseBundle {
  const options: LicenseOption[] = []
  let monthlyTotal = 0
  let oneTimeTotal = 0

  for (const item of snapshot.items) {
    const prodKey = slugify(item.product_name)

    options.push({
      key: prodKey,
      label: item.product_name,
      active: true,
      billing: item.billing_interval,
      price_net: item.base_price,
    })

    if (item.billing_interval === 'monthly') monthlyTotal += item.base_price
    else oneTimeTotal += item.base_price

    for (const mod of item.selected_modules) {
      const qty = mod.quantity || 1
      const modPrice = mod.total_price || (mod.price * qty)
      options.push({
        key: `${prodKey}__${slugify(mod.module_name)}`,
        label: mod.module_name,
        active: true,
        billing: item.billing_interval,
        price_net: mod.price,
        quantity: qty,
      })

      if (item.billing_interval === 'monthly') monthlyTotal += modPrice
      else oneTimeTotal += modPrice
    }
  }

  return {
    customer_ref: customerRef,
    generated_at: new Date().toISOString(),
    license_key_prefix: `CASP-${new Date().getFullYear()}-`,
    options,
    monthly_total_net: monthlyTotal,
    one_time_total_net: oneTimeTotal,
  }
}
