/**
 * Recurring Quotation Service
 * Create, manage, and auto-generate quotations from recurring templates
 */

import { prisma } from '@/lib/db';
import { quotationRecurringTemplatesAdapter } from '@/lib/adapters/quotations';
import { logger } from '@/lib/logger';
import { createId } from '@paralleldrive/cuid2';
import { enqueueJob } from '@/lib/queue/job-queue';
// Prisma types replaced with inline equivalents after Convex migration

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

// ═══════════════════════════════════════════════════════════════════════
// Types
// ═══════════════════════════════════════════════════════════════════════

interface RecurringTemplateItemInput {
  description: string;
  quantity: number;
  unitPrice: number;
}

interface CreateRecurringTemplateInput {
  customerId?: string;
  title: string;
  notes?: string;
  currency?: string;
  taxRate?: number;
  discountRate?: number;
  validDays?: number;
  paymentDueDays?: number;
  frequency: 'MONTHLY' | 'QUARTERLY' | 'SEMI_ANNUAL' | 'ANNUAL' | 'CUSTOM';
  customIntervalDays?: number;
  startDate: string;
  endDate?: string;
  maxOccurrences?: number;
  autoSend?: boolean;
  items: RecurringTemplateItemInput[];
}

interface UpdateRecurringTemplateInput {
  customerId?: string;
  title?: string;
  notes?: string;
  currency?: string;
  taxRate?: number;
  discountRate?: number;
  validDays?: number;
  paymentDueDays?: number;
  frequency?: 'MONTHLY' | 'QUARTERLY' | 'SEMI_ANNUAL' | 'ANNUAL' | 'CUSTOM';
  customIntervalDays?: number;
  endDate?: string | null;
  maxOccurrences?: number | null;
  autoSend?: boolean;
  items?: RecurringTemplateItemInput[];
}

interface RecurringTemplateFilter {
  status?: 'ACTIVE' | 'PAUSED' | 'CANCELLED' | 'COMPLETED';
  page?: number;
  limit?: number;
}

// ═══════════════════════════════════════════════════════════════════════
// Next Run Date Calculation
// ═══════════════════════════════════════════════════════════════════════

export function calculateNextRunDate(
  currentDate: Date,
  frequency: string,
  customIntervalDays?: number | null,
): Date {
  const next = new Date(currentDate);

  switch (frequency) {
    case 'MONTHLY':
      next.setMonth(next.getMonth() + 1);
      break;
    case 'QUARTERLY':
      next.setMonth(next.getMonth() + 3);
      break;
    case 'SEMI_ANNUAL':
      next.setMonth(next.getMonth() + 6);
      break;
    case 'ANNUAL':
      next.setFullYear(next.getFullYear() + 1);
      break;
    case 'CUSTOM':
      if (!customIntervalDays || customIntervalDays < 1) {
        throw new Error('customIntervalDays required for CUSTOM frequency');
      }
      next.setDate(next.getDate() + customIntervalDays);
      break;
    default:
      throw new Error(`Unknown frequency: ${frequency}`);
  }

  return next;
}

// ═══════════════════════════════════════════════════════════════════════
// Quotation Number Generation (duplicated from quotations.service.ts for independent use)
// ═══════════════════════════════════════════════════════════════════════

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')}`;

  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 createRecurringTemplate(userId: string, input: CreateRecurringTemplateInput) {
  const id = createId();
  const now = new Date();
  const startDate = new Date(input.startDate);

  if (input.frequency === 'CUSTOM' && (!input.customIntervalDays || input.customIntervalDays < 1)) {
    throw new Error('customIntervalDays is required for CUSTOM frequency');
  }

  const template = await prisma.$transaction(async (tx) => {
    const created = await tx.quotation_recurring_templates.create({
      data: {
        id,
        userId,
        customerId: input.customerId ?? null,
        title: input.title,
        notes: input.notes ?? null,
        currency: input.currency ?? 'QAR',
        taxRate: input.taxRate ?? 0,
        discountRate: input.discountRate ?? 0,
        validDays: input.validDays ?? 30,
        paymentDueDays: input.paymentDueDays ?? 30,
        frequency: input.frequency,
        customIntervalDays: input.customIntervalDays ?? null,
        status: 'ACTIVE',
        startDate,
        nextRunDate: startDate,
        endDate: input.endDate ? new Date(input.endDate) : null,
        maxOccurrences: input.maxOccurrences ?? null,
        occurrenceCount: 0,
        autoSend: input.autoSend ?? true,
        updatedAt: now,
        items: {
          create: input.items.map((item, index) => ({
            id: createId(),
            description: item.description,
            quantity: item.quantity,
            unitPrice: item.unitPrice,
            total: Math.round(item.quantity * item.unitPrice * 100) / 100,
            sortOrder: index,
            updatedAt: now,
          })),
        },
      },
      include: {
        items: { orderBy: { sortOrder: 'asc' } },
        customer: true,
      },
    });

    return created;
  });

  log.info('Recurring template created', { templateId: id, userId });

  // If autoSend and startDate is now or in the past, generate the first quotation immediately
  if (input.autoSend !== false && startDate <= now) {
    try {
      await generateQuotationFromTemplate(id);
    } catch (error) {
      log.error('Failed to generate first quotation from template', { templateId: id, error });
    }
  }

  return template;
}

export async function updateRecurringTemplate(userId: string, templateId: string, input: UpdateRecurringTemplateInput) {
  const existing = await quotationRecurringTemplatesAdapter.findFirst({
    where: { id: templateId, userId },
  });
  if (!existing) return null;

  const now = new Date();

  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.validDays !== undefined && { validDays: input.validDays }),
    ...(input.paymentDueDays !== undefined && { paymentDueDays: input.paymentDueDays }),
    ...(input.frequency !== undefined && { frequency: input.frequency }),
    ...(input.customIntervalDays !== undefined && { customIntervalDays: input.customIntervalDays }),
    ...(input.endDate !== undefined && { endDate: input.endDate ? new Date(input.endDate) : null }),
    ...(input.maxOccurrences !== undefined && { maxOccurrences: input.maxOccurrences }),
    ...(input.autoSend !== undefined && { autoSend: input.autoSend }),
    updatedAt: now,
  };

  return prisma.$transaction(async (tx) => {
    // If items provided, replace all
    if (input.items) {
      await tx.quotation_recurring_template_items.deleteMany({ where: { templateId } });
      await tx.quotation_recurring_template_items.createMany({
        data: input.items.map((item, index) => ({
          id: createId(),
          templateId,
          description: item.description,
          quantity: item.quantity,
          unitPrice: item.unitPrice,
          total: Math.round(item.quantity * item.unitPrice * 100) / 100,
          sortOrder: index,
          updatedAt: now,
        })),
      });
    }

    return tx.quotation_recurring_templates.update({
      where: { id: templateId },
      data,
      include: {
        items: { orderBy: { sortOrder: 'asc' } },
        customer: true,
      },
    });
  });
}

export async function pauseRecurringTemplate(userId: string, templateId: string) {
  const existing = await quotationRecurringTemplatesAdapter.findFirst({
    where: { id: templateId, userId, status: 'ACTIVE' },
  });
  if (!existing) return null;

  return quotationRecurringTemplatesAdapter.update({
    where: { id: templateId },
    data: { status: 'PAUSED', updatedAt: new Date() },
    include: { items: { orderBy: { sortOrder: 'asc' } }, customer: true },
  });
}

export async function resumeRecurringTemplate(userId: string, templateId: string) {
  const existing = await quotationRecurringTemplatesAdapter.findFirst({
    where: { id: templateId, userId, status: 'PAUSED' },
  });
  if (!existing) return null;

  // Recalculate nextRunDate from now
  const nextRunDate = calculateNextRunDate(
    new Date(),
    existing.frequency,
    existing.customIntervalDays,
  );

  return quotationRecurringTemplatesAdapter.update({
    where: { id: templateId },
    data: { status: 'ACTIVE', nextRunDate, updatedAt: new Date() },
    include: { items: { orderBy: { sortOrder: 'asc' } }, customer: true },
  });
}

export async function cancelRecurringTemplate(userId: string, templateId: string) {
  const existing = await quotationRecurringTemplatesAdapter.findFirst({
    where: { id: templateId, userId, status: { in: ['ACTIVE', 'PAUSED'] } },
  });
  if (!existing) return null;

  return quotationRecurringTemplatesAdapter.update({
    where: { id: templateId },
    data: { status: 'CANCELLED', updatedAt: new Date() },
    include: { items: { orderBy: { sortOrder: 'asc' } }, customer: true },
  });
}

export async function getRecurringTemplate(userId: string, templateId: string) {
  return quotationRecurringTemplatesAdapter.findFirst({
    where: { id: templateId, userId },
    include: {
      items: { orderBy: { sortOrder: 'asc' } },
      customer: true,
    },
  });
}

export async function listRecurringTemplates(userId: string, filters?: RecurringTemplateFilter) {
  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;

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

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

// ═══════════════════════════════════════════════════════════════════════
// Generate Quotation from Template
// ═══════════════════════════════════════════════════════════════════════

export async function generateQuotationFromTemplate(templateId: string) {
  const template = await quotationRecurringTemplatesAdapter.findUnique({
    where: { id: templateId },
    include: { items: { orderBy: { sortOrder: 'asc' } } },
  }) as (Awaited<ReturnType<typeof prisma.quotation_recurring_templates.findUnique>> & {
    items: { quantity: number; unitPrice: number; description: string; sortOrder: number }[];
  }) | null;

  if (!template) throw new Error(`Template not found: ${templateId}`);
  if (template.status !== 'ACTIVE') throw new Error(`Template not active: ${templateId}`);

  const now = new Date();
  const quotationId = createId();
  const quotationNumber = await generateQuotationNumber(template.userId);
  const sequence = template.occurrenceCount + 1;

  // Calculate validUntil and dueDate from template settings
  const validUntil = new Date(now);
  validUntil.setDate(validUntil.getDate() + template.validDays);

  const dueDate = new Date(now);
  dueDate.setDate(dueDate.getDate() + template.paymentDueDays);

  const itemsWithTotals = template.items.map((item) => ({
    quantity: item.quantity,
    unitPrice: item.unitPrice,
    total: Math.round(item.quantity * item.unitPrice * 100) / 100,
    description: item.description,
    sortOrder: item.sortOrder,
  }));

  const totals = calculateTotals(
    itemsWithTotals,
    template.taxRate,
    template.discountRate,
  );

  // Determine the next run date and whether template is complete
  const nextRunDate = calculateNextRunDate(now, template.frequency, template.customIntervalDays);
  const maxReached = template.maxOccurrences ? sequence >= template.maxOccurrences : false;
  const pastEndDate = template.endDate ? nextRunDate > template.endDate : false;
  const isCompleted = maxReached || pastEndDate;

  const quotation = await prisma.$transaction(async (tx) => {
    // Create the quotation
    const created = await tx.quotations.create({
      data: {
        id: quotationId,
        userId: template.userId,
        quotationNumber,
        title: template.title,
        customerId: template.customerId,
        notes: template.notes,
        currency: template.currency,
        taxRate: template.taxRate,
        discountRate: template.discountRate,
        ...totals,
        validUntil,
        dueDate,
        status: template.autoSend ? 'SENT' : 'DRAFT',
        sentAt: template.autoSend ? now : null,
        recurringTemplateId: templateId,
        recurringSequence: sequence,
        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 },
    });

    // Update the template
    await tx.quotation_recurring_templates.update({
      where: { id: templateId },
      data: {
        occurrenceCount: sequence,
        nextRunDate: isCompleted ? template.nextRunDate : nextRunDate,
        status: isCompleted ? 'COMPLETED' : 'ACTIVE',
        updatedAt: now,
      },
    });

    return created;
  });

  log.info('Quotation generated from recurring template', {
    quotationId,
    templateId,
    sequence,
    isCompleted,
  });

  return quotation;
}

// ═══════════════════════════════════════════════════════════════════════
// Process Due Recurring Templates
// ═══════════════════════════════════════════════════════════════════════

export async function processDueRecurringTemplates(): Promise<{ processed: number; enqueued: number }> {
  const now = new Date();

  const dueTemplates = await quotationRecurringTemplatesAdapter.findMany({
    where: {
      status: 'ACTIVE',
      nextRunDate: { lte: now },
    },
    select: { id: true },
  });

  let enqueued = 0;
  for (const template of dueTemplates) {
    try {
      await enqueueJob('quotation_recurring_gen', { templateId: template.id });
      enqueued++;
    } catch (error) {
      log.error('Failed to enqueue recurring template job', { templateId: template.id, error });
    }
  }

  log.info('Processed due recurring templates', { processed: dueTemplates.length, enqueued });
  return { processed: dueTemplates.length, enqueued };
}
