import { prisma } from '../../lib/prisma'
import * as repo from './reservation.repo'

export function formatDocumentNumber(prefix: string, sequence: number): string {
  return `${prefix}-${String(sequence).padStart(6, '0')}`
}

export async function ensureReservationDocumentNumbers(companyId: string, reservationId: string) {
  return prisma.$transaction(async (tx: any) => {
    const reservation = await tx.reservation.findFirstOrThrow({
      where: { id: reservationId, companyId },
      select: { id: true, contractNumber: true, invoiceNumber: true },
    })
    if (reservation.contractNumber && reservation.invoiceNumber) return reservation

    const settings = await tx.contractSettings.upsert({
      where: { companyId },
      update: {},
      create: { companyId },
    })

    const data: { contractNumber?: string; invoiceNumber?: string } = {}
    const settingsUpdate: { lastContractSeq?: number; lastInvoiceSeq?: number } = {}

    if (!reservation.contractNumber) {
      const nextSeq = settings.lastContractSeq + 1
      data.contractNumber = formatDocumentNumber(settings.contractPrefix, nextSeq)
      settingsUpdate.lastContractSeq = nextSeq
    }
    if (!reservation.invoiceNumber) {
      const nextSeq = settings.lastInvoiceSeq + 1
      data.invoiceNumber = formatDocumentNumber(settings.invoicePrefix, nextSeq)
      settingsUpdate.lastInvoiceSeq = nextSeq
    }

    if (Object.keys(settingsUpdate).length > 0) {
      await tx.contractSettings.update({ where: { companyId }, data: settingsUpdate })
    }

    return tx.reservation.update({
      where: { id: reservationId },
      data,
      select: { id: true, contractNumber: true, invoiceNumber: true },
    })
  })
}

export function buildReservationInvoiceLineItems(reservation: {
  dailyRate: number
  totalDays: number
  discountAmount: number
  depositAmount: number
  pricingRulesApplied: Array<{ name?: string; amount?: number; type?: string }> | null
  insurances: Array<{ id: string; policyName: string; chargeType: string; chargeValue: number; totalCharge: number }>
  additionalDrivers: Array<{ id: string; firstName: string; lastName: string; totalCharge: number }>
  vehicle: { year: number; make: string; model: string }
}) {
  const baseAmount = reservation.dailyRate * reservation.totalDays
  return [
    {
      description: `${reservation.vehicle.year} ${reservation.vehicle.make} ${reservation.vehicle.model}`,
      qty: reservation.totalDays,
      unitPrice: reservation.dailyRate,
      total: baseAmount,
      category: 'RENTAL',
    },
    ...reservation.insurances.map((ins) => ({
      description: ins.policyName,
      qty: ins.chargeType === 'PER_DAY' ? reservation.totalDays : 1,
      unitPrice: ins.chargeType === 'PER_DAY' ? ins.chargeValue : ins.totalCharge,
      total: ins.totalCharge,
      category: 'INSURANCE',
    })),
    ...reservation.additionalDrivers
      .filter((d) => d.totalCharge > 0)
      .map((d) => ({
        description: `Additional driver - ${d.firstName} ${d.lastName}`,
        qty: 1,
        unitPrice: d.totalCharge,
        total: d.totalCharge,
        category: 'ADDITIONAL_DRIVER',
      })),
    ...((reservation.pricingRulesApplied ?? []).map((rule, i) => ({
      description: rule.name?.trim() || `Pricing adjustment ${i + 1}`,
      qty: 1,
      unitPrice: Number(rule.amount ?? 0),
      total: Number(rule.amount ?? 0),
      category: 'PRICING_RULE',
    }))),
    ...(reservation.discountAmount > 0 ? [{
      description: 'Discount',
      qty: 1,
      unitPrice: -reservation.discountAmount,
      total: -reservation.discountAmount,
      category: 'DISCOUNT',
    }] : []),
    ...(reservation.depositAmount > 0 ? [{
      description: 'Security deposit',
      qty: 1,
      unitPrice: reservation.depositAmount,
      total: reservation.depositAmount,
      category: 'DEPOSIT',
    }] : []),
  ]
}

export async function getContract(id: string, companyId: string) {
  await ensureReservationDocumentNumbers(companyId, id)

  const reservation = await repo.findForContract(id, companyId)
  const contractSettings = reservation.company.contractSettings ?? await prisma.contractSettings.upsert({
    where: { companyId },
    update: {},
    create: { companyId },
  })

  const extras = reservation.extras && typeof reservation.extras === 'object' && !Array.isArray(reservation.extras)
    ? (reservation.extras as Record<string, unknown>)
    : {}

  const invoiceLineItems = buildReservationInvoiceLineItems({
    dailyRate: reservation.dailyRate,
    totalDays: reservation.totalDays,
    discountAmount: reservation.discountAmount,
    depositAmount: reservation.depositAmount,
    pricingRulesApplied: Array.isArray(reservation.pricingRulesApplied)
      ? (reservation.pricingRulesApplied as any)
      : [],
    insurances: reservation.insurances,
    additionalDrivers: reservation.additionalDrivers,
    vehicle: reservation.vehicle,
  })

  const subtotal   = invoiceLineItems.reduce((s, i) => s + i.total, 0)
  const taxes      = contractSettings.showTax && contractSettings.taxRate
    ? [{ label: contractSettings.taxLabel?.trim() || 'Tax', rate: contractSettings.taxRate, amount: Math.round(subtotal * contractSettings.taxRate / 100) }]
    : []
  const taxTotal   = taxes.reduce((s, t) => s + t.amount, 0)
  const grandTotal = subtotal + taxTotal
  const amountPaid = reservation.rentalPayments
    .filter((p: any) => p.status === 'SUCCEEDED')
    .reduce((s: number, p: any) => s + p.amount, 0)

  const checkInInspection  = reservation.inspections.find((i: any) => i.type === 'CHECKIN') ?? null
  const checkOutInspection = reservation.inspections.find((i: any) => i.type === 'CHECKOUT') ?? null

  return {
    reservationId: reservation.id,
    contractLocale: reservation.company.brand?.defaultLocale ?? 'en',
    contractNumber: reservation.contractNumber,
    invoiceNumber:  reservation.invoiceNumber,
    generatedAt:    new Date().toISOString(),
    status:         reservation.status,
    paymentStatus:  reservation.paymentStatus,
    paymentMode:    typeof extras.paymentMode === 'string' ? extras.paymentMode : null,
    notes:          reservation.notes,
    company: {
      name:               reservation.company.brand?.displayName ?? reservation.company.name,
      legalName:          contractSettings.legalName || reservation.company.name,
      email:              reservation.company.brand?.publicEmail ?? reservation.company.email,
      phone:              reservation.company.brand?.publicPhone ?? reservation.company.phone,
      address:            reservation.company.brand?.publicAddress ?? reservation.company.address ?? null,
      city:               reservation.company.brand?.publicCity ?? null,
      country:            reservation.company.brand?.publicCountry ?? null,
      registrationNumber: contractSettings.registrationNumber,
      taxId:              contractSettings.taxId,
      logoUrl:            reservation.company.brand?.logoUrl ?? null,
    },
    driver: {
      firstName:       reservation.customer.firstName,
      lastName:        reservation.customer.lastName,
      email:           reservation.customer.email,
      phone:           reservation.customer.phone,
      dateOfBirth:     reservation.customer.dateOfBirth,
      driverLicense:   reservation.customer.driverLicense,
      licenseCountry:  reservation.customer.licenseCountry,
      licenseCategory: reservation.customer.licenseCategory,
      licenseIssuedAt: reservation.customer.licenseIssuedAt,
      licenseExpiry:   reservation.customer.licenseExpiry,
      nationality:     reservation.customer.nationality,
    },
    additionalDrivers: reservation.additionalDrivers.map((d: any) => ({
      id: d.id, firstName: d.firstName, lastName: d.lastName,
      email: d.email, phone: d.phone, driverLicense: d.driverLicense,
      licenseIssuedAt: d.licenseIssuedAt, licenseExpiry: d.licenseExpiry,
      licenseCountry: d.nationality, dateOfBirth: d.dateOfBirth, totalCharge: d.totalCharge,
    })),
    vehicle: {
      make: reservation.vehicle.make, model: reservation.vehicle.model, year: reservation.vehicle.year,
      color: reservation.vehicle.color, licensePlate: reservation.vehicle.licensePlate,
      vin: reservation.vehicle.vin, category: reservation.vehicle.category,
    },
    rentalPeriod: {
      startDate: reservation.startDate, endDate: reservation.endDate, totalDays: reservation.totalDays,
      pickupLocation: reservation.pickupLocation, returnLocation: reservation.returnLocation,
    },
    insurance: {
      total: reservation.insuranceTotal,
      policies: reservation.insurances.map((ins: any) => ({
        id: ins.id, name: ins.policyName, chargeType: ins.chargeType,
        unitPrice: ins.chargeValue, totalCharge: ins.totalCharge,
      })),
    },
    inspections: { checkIn: checkInInspection, checkOut: checkOutInspection },
    terms: {
      terms:             contractSettings.terms,
      fuelPolicy:        contractSettings.fuelPolicy,
      fuelPolicyType:    contractSettings.fuelPolicyType,
      depositPolicy:     contractSettings.depositPolicy,
      lateFeePolicy:     contractSettings.lateFeePolicy,
      damagePolicy:      contractSettings.damagePolicy,
      footerNote:        contractSettings.contractFooterNote,
      signatureRequired: contractSettings.signatureRequired,
    },
    invoice: {
      currency: reservation.company.accountingSettings?.currency ?? reservation.company.brand?.defaultCurrency ?? 'MAD',
      lineItems: invoiceLineItems,
      subtotal, taxes, taxTotal, total: grandTotal, amountPaid,
      balanceDue: grandTotal - amountPaid,
      payments: reservation.rentalPayments.map((p: any) => ({
        id: p.id, amount: p.amount, currency: p.currency, type: p.type,
        status: p.status, provider: p.paymentProvider,
        paymentMethod: p.paymentMethod, paidAt: p.paidAt, createdAt: p.createdAt,
      })),
    },
  }
}

export async function getBilling(id: string, companyId: string) {
  const reservation = await repo.findForBilling(id, companyId)
  const baseAmount = reservation.dailyRate * reservation.totalDays
  const lineItems = [
    {
      description: `${reservation.vehicle.year} ${reservation.vehicle.make} ${reservation.vehicle.model} — ${reservation.totalDays} day(s)`,
      qty: reservation.totalDays, unitPrice: reservation.dailyRate, total: baseAmount, category: 'RENTAL',
    },
    ...reservation.insurances.map((ins: any) => ({
      description: ins.policyName,
      qty: ins.chargeType === 'PER_DAY' ? reservation.totalDays : 1,
      unitPrice: ins.chargeType === 'PER_DAY' ? ins.chargeValue : ins.totalCharge,
      total: ins.totalCharge, category: 'INSURANCE',
    })),
    ...reservation.additionalDrivers
      .filter((d: any) => d.totalCharge > 0)
      .map((d: any) => ({
        description: `Additional Driver — ${d.firstName} ${d.lastName}`,
        qty: 1, unitPrice: d.totalCharge, total: d.totalCharge, category: 'ADDITIONAL_DRIVER',
      })),
    ...(reservation.depositAmount > 0 ? [{
      description: 'Security Deposit (refundable)',
      qty: 1, unitPrice: reservation.depositAmount, total: reservation.depositAmount, category: 'DEPOSIT',
    }] : []),
  ]
  const grandTotal = lineItems.reduce((s, i) => s + i.total, 0) - reservation.discountAmount

  return {
    lineItems,
    discountAmount:      reservation.discountAmount,
    pricingRulesApplied: reservation.pricingRulesApplied,
    pricingRulesTotal:   reservation.pricingRulesTotal,
    grandTotal,
  }
}
