/**
 * Quotations Service
 * CRUD, send, duplicate, line items, totals recalculation
 */

import { prisma } from '@/lib/db';
import { logger } from '@/lib/logger';
import { createId } from '@paralleldrive/cuid2';
// Prisma types replaced with inline equivalents after Convex migration
import type { CreateQuotationInput, UpdateQuotationInput, QuotationFilterInput, QuotationItemInput } from '@/lib/validators/quotations.validator';
import { createPortalCheckoutSession } from '@/lib/services/quotation-portal.service';
import { notifyQuotationCustomer } from '@/lib/services/quotation-notifier.service';

const log = logger.scope('QuotationsService');

// ═══════════════════════════════════════════════════════════════════════
// Quotation Number Generation
// ═══════════════════════════════════════════════════════════════════════

async function generateQuotationNumber(userId: string): Promise<string> {
  const now = new Date();
  const dateStr = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`;

  // Count existing quotations for this user today to get sequence
  const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  const todayEnd = new Date(todayStart.getTime() + 86400000);

  const count = await prisma.quotations.count({
    where: {
      userId,
      createdAt: { gte: todayStart, lt: todayEnd },
    },
  });

  const seq = String(count + 1).padStart(4, '0');
  return `Q-${dateStr}-${seq}`;
}

// ═══════════════════════════════════════════════════════════════════════
// Totals Calculation
// ═══════════════════════════════════════════════════════════════════════

function calculateTotals(items: { quantity: number; unitPrice: number }[], taxRate: number, discountRate: number) {
  const subtotal = items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);
  const discountAmount = subtotal * (discountRate / 100);
  const afterDiscount = subtotal - discountAmount;
  const taxAmount = afterDiscount * (taxRate / 100);
  const total = afterDiscount + taxAmount;

  return {
    subtotal: Math.round(subtotal * 100) / 100,
    discountAmount: Math.round(discountAmount * 100) / 100,
    taxAmount: Math.round(taxAmount * 100) / 100,
    total: Math.round(total * 100) / 100,
  };
}

// ═══════════════════════════════════════════════════════════════════════
// CRUD
// ═══════════════════════════════════════════════════════════════════════

export async function createQuotation(userId: string, input: CreateQuotationInput) {
  const id = createId();
  const quotationNumber = await generateQuotationNumber(userId);
  const now = new Date();

  // Calculate item totals
  const itemsWithTotals = input.items.map((item) => ({
    ...item,
    total: Math.round(item.quantity * item.unitPrice * 100) / 100,
  }));

  const totals = calculateTotals(input.items, input.taxRate ?? 0, input.discountRate ?? 0);

  const quotation = await prisma.quotations.create({
    data: {
      id,
      userId,
      quotationNumber,
      title: input.title,
      customerId: input.customerId ?? null,
      notes: input.notes ?? null,
      currency: input.currency ?? 'QAR',
      taxRate: input.taxRate ?? 0,
      discountRate: input.discountRate ?? 0,
      ...totals,
      validUntil: input.validUntil ? new Date(input.validUntil) : null,
      updatedAt: now,
      items: {
        create: itemsWithTotals.map((item, index) => ({
          id: createId(),
          description: item.description,
          quantity: item.quantity,
          unitPrice: item.unitPrice,
          total: item.total,
          sortOrder: index,
          updatedAt: now,
        })),
      },
    },
    include: { items: { orderBy: { sortOrder: 'asc' } }, customer: true },
  });

  log.info('Quotation created', { quotationId: id, userId, number: quotationNumber });
  return quotation;
}

export async function getQuotation(
  userId: string,
  quotationId: string,
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  _includes?: { payments?: boolean; penalties?: boolean },
) {
  // Note: payments/penalties relations not yet in Prisma schema — include when available
  return prisma.quotations.findFirst({
    where: { id: quotationId, userId },
    include: {
      items: { orderBy: { sortOrder: 'asc' } },
      customer: true,
    },
  });
}

export async function updateQuotation(userId: string, quotationId: string, input: UpdateQuotationInput) {
  const existing = await prisma.quotations.findFirst({ where: { id: quotationId, userId } });
  if (!existing) return null;

  const data: Record<string, unknown> = {
    ...(input.title !== undefined && { title: input.title }),
    ...(input.customerId !== undefined && { customer: input.customerId ? { connect: { id: input.customerId } } : { disconnect: true } }),
    ...(input.notes !== undefined && { notes: input.notes }),
    ...(input.currency !== undefined && { currency: input.currency }),
    ...(input.taxRate !== undefined && { taxRate: input.taxRate }),
    ...(input.discountRate !== undefined && { discountRate: input.discountRate }),
    ...(input.validUntil !== undefined && { validUntil: input.validUntil ? new Date(input.validUntil) : null }),
    ...(input.status !== undefined && { status: input.status }),
    updatedAt: new Date(),
  };

  // Recalculate totals if tax or discount changed
  if (input.taxRate !== undefined || input.discountRate !== undefined) {
    const items = await prisma.quotation_items.findMany({ where: { quotationId } });
    const totals = calculateTotals(
      items.map((i) => ({ quantity: i.quantity, unitPrice: i.unitPrice })),
      input.taxRate ?? existing.taxRate,
      input.discountRate ?? existing.discountRate,
    );
    Object.assign(data, totals);
  }

  return prisma.quotations.update({
    where: { id: quotationId },
    data,
    include: { items: { orderBy: { sortOrder: 'asc' } }, customer: true },
  });
}

export async function deleteQuotation(userId: string, quotationId: string): Promise<boolean> {
  const existing = await prisma.quotations.findFirst({ where: { id: quotationId, userId } });
  if (!existing) return false;

  await prisma.quotations.delete({ where: { id: quotationId } });
  log.info('Quotation deleted', { quotationId, userId });
  return true;
}

export async function listQuotations(userId: string, filters: QuotationFilterInput) {
  const page = filters.page ?? 1;
  const limit = Math.min(filters.limit ?? 20, 100);
  const skip = (page - 1) * limit;

  const where: Record<string, unknown> = { userId };

  if (filters.status) where.status = filters.status;
  if (filters.customerId) where.customerId = filters.customerId;
  if (filters.search) {
    where.OR = [
      { title: { contains: filters.search, mode: 'insensitive' } },
      { quotationNumber: { contains: filters.search, mode: 'insensitive' } },
    ];
  }

  const [quotations, total] = await Promise.all([
    prisma.quotations.findMany({
      where,
      include: { items: { orderBy: { sortOrder: 'asc' } }, customer: true },
      orderBy: { [filters.sortBy ?? 'createdAt']: filters.sortOrder ?? 'desc' },
      skip,
      take: limit,
    }),
    prisma.quotations.count({ where }),
  ]);

  return {
    quotations,
    total,
    page,
    limit,
    totalPages: Math.ceil(total / limit),
  };
}

// ═══════════════════════════════════════════════════════════════════════
// Dispatch & Duplicate
// ═══════════════════════════════════════════════════════════════════════

type DispatchResult = {
  quotation: Awaited<ReturnType<typeof prisma.quotations.update>>;
  phoneStatus: 'sent';
  emailQueued: boolean;
  whatsappSent: boolean;
};

/**
 * Dispatch a quotation to the customer:
 * 1. Generate portal token (or reuse existing)
 * 2. Mark status = SENT, save portal token
 * 3. Create Stripe checkout session via portal service (non-blocking on failure)
 * 4. Enqueue email with payment link (non-blocking)
 * 5. Mock phone — log only
 */
export async function dispatchQuotation(
  userId: string,
  quotationId: string,
): Promise<DispatchResult | null> {
  const existing = await prisma.quotations.findFirst({
    where: { id: quotationId, userId },
    include: {
      customer: { select: { name: true, email: true, phone: true } },
      items: { orderBy: { sortOrder: 'asc' } },
    },
  });
  if (!existing) return null;

  // Use existing portal token or generate a new one
  const portalToken = existing.portalToken ?? createId();

  // Update status and save portal token
  const quotation = await prisma.quotations.update({
    where: { id: quotationId },
    data: {
      status: 'SENT',
      sentAt: new Date(),
      portalToken,
      updatedAt: new Date(),
    },
    include: {
      items: { orderBy: { sortOrder: 'asc' } },
      customer: true,
    },
  });

  // Build portal URL
  const baseUrl = process.env.NEXTAUTH_URL ?? 'http://localhost:9000';
  const portalUrl = `${baseUrl}/quotation/${portalToken}`;

  // Create Stripe checkout session (non-blocking on failure)
  try {
    await createPortalCheckoutSession(
      portalToken,
      `${portalUrl}?paid=1`,
      portalUrl,
    );
  } catch (err) {
    log.warn('Failed to create Stripe checkout for quotation', { quotationId, err });
  }

  // Notify customer via email (with PDF) + WhatsApp
  const notify = await notifyQuotationCustomer(quotation, 'dispatch');

  return { quotation, phoneStatus: 'sent', emailQueued: notify.emailQueued, whatsappSent: notify.whatsappSent };
}

export async function duplicateQuotation(userId: string, quotationId: string) {
  const source = await prisma.quotations.findFirst({
    where: { id: quotationId, userId },
    include: { items: { orderBy: { sortOrder: 'asc' } } },
  });
  if (!source) return null;

  const newId = createId();
  const quotationNumber = await generateQuotationNumber(userId);
  const now = new Date();

  return prisma.quotations.create({
    data: {
      id: newId,
      userId,
      quotationNumber,
      title: `${source.title} (Copy)`,
      customerId: source.customerId,
      notes: source.notes,
      status: 'DRAFT',
      currency: source.currency,
      subtotal: source.subtotal,
      taxRate: source.taxRate,
      taxAmount: source.taxAmount,
      discountRate: source.discountRate,
      discountAmount: source.discountAmount,
      total: source.total,
      validUntil: source.validUntil,
      updatedAt: now,
      items: {
        create: source.items.map((item, index) => ({
          id: createId(),
          description: item.description,
          quantity: item.quantity,
          unitPrice: item.unitPrice,
          total: item.total,
          sortOrder: index,
          updatedAt: now,
        })),
      },
    },
    include: { items: { orderBy: { sortOrder: 'asc' } }, customer: true },
  });
}

// ═══════════════════════════════════════════════════════════════════════
// Line Items
// ═══════════════════════════════════════════════════════════════════════

export async function addItem(userId: string, quotationId: string, input: QuotationItemInput) {
  const existing = await prisma.quotations.findFirst({ where: { id: quotationId, userId } });
  if (!existing) return null;

  const maxOrder = await prisma.quotation_items.findFirst({
    where: { quotationId },
    orderBy: { sortOrder: 'desc' },
    select: { sortOrder: true },
  });

  const item = await prisma.quotation_items.create({
    data: {
      id: createId(),
      quotationId,
      description: input.description,
      quantity: input.quantity,
      unitPrice: input.unitPrice,
      total: Math.round(input.quantity * input.unitPrice * 100) / 100,
      sortOrder: (maxOrder?.sortOrder ?? -1) + 1,
      updatedAt: new Date(),
    },
  });

  await recalculateTotals(quotationId);
  return item;
}

export async function updateItem(userId: string, quotationId: string, itemId: string, input: Partial<QuotationItemInput>) {
  const existing = await prisma.quotations.findFirst({ where: { id: quotationId, userId } });
  if (!existing) return null;

  const item = await prisma.quotation_items.findFirst({ where: { id: itemId, quotationId } });
  if (!item) return null;

  const quantity = input.quantity ?? item.quantity;
  const unitPrice = input.unitPrice ?? item.unitPrice;

  const updated = await prisma.quotation_items.update({
    where: { id: itemId },
    data: {
      ...(input.description !== undefined && { description: input.description }),
      ...(input.quantity !== undefined && { quantity: input.quantity }),
      ...(input.unitPrice !== undefined && { unitPrice: input.unitPrice }),
      total: Math.round(quantity * unitPrice * 100) / 100,
      updatedAt: new Date(),
    },
  });

  await recalculateTotals(quotationId);
  return updated;
}

export async function removeItem(userId: string, quotationId: string, itemId: string): Promise<boolean> {
  const existing = await prisma.quotations.findFirst({ where: { id: quotationId, userId } });
  if (!existing) return false;

  const item = await prisma.quotation_items.findFirst({ where: { id: itemId, quotationId } });
  if (!item) return false;

  await prisma.quotation_items.delete({ where: { id: itemId } });
  await recalculateTotals(quotationId);
  return true;
}

export async function recalculateTotals(quotationId: string) {
  const quotation = await prisma.quotations.findUnique({ where: { id: quotationId } });
  if (!quotation) return null;

  const items = await prisma.quotation_items.findMany({ where: { quotationId } });
  const totals = calculateTotals(
    items.map((i) => ({ quantity: i.quantity, unitPrice: i.unitPrice })),
    quotation.taxRate,
    quotation.discountRate,
  );

  return prisma.quotations.update({
    where: { id: quotationId },
    data: { ...totals, updatedAt: new Date() },
  });
}
