'use server';

import { and, eq } from 'drizzle-orm';
import { revalidatePath } from 'next/cache';
import { db } from '@/db';
import { leads, leadEstadoHistory, leadPipelineEventos, precisionHistory } from '@/db/schema';
import { getCurrentTenantId as getTenantId } from '@/lib/auth/current-user';
import { getPricingConfig, getCatalog } from '@/db/pricing-queries';
import { computeBudget } from '@/budget';
import { applyConceptoEdits } from '@/budget/edit';
import type { BudgetInputs, BudgetResult, PartidaResult } from '@/budget/types';

type Estado = (typeof leads.estado.enumValues)[number];

export async function cambiarEstado(leadId: string, estado: Estado) {
  const tenantId = await getTenantId();

  const [updated] = await db
    .update(leads)
    .set({ estado, updatedAt: new Date() })
    .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId)))
    .returning();

  if (!updated) throw new Error('Lead no encontrado.');

  await db.insert(leadEstadoHistory).values({ leadId, estado });

  revalidatePath('/panel');
  revalidatePath(`/panel/${leadId}`);
}

export async function marcarGanado(leadId: string, precioFinalEuros: number) {
  const tenantId = await getTenantId();

  const [lead] = await db
    .select()
    .from(leads)
    .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId)))
    .limit(1);

  if (!lead) throw new Error('Lead no encontrado.');
  if (lead.presupuestoEstimado == null) {
    throw new Error('El lead no tiene presupuesto estimado, no se puede calcular la precisión.');
  }

  const finalCents = Math.round(precioFinalEuros * 100);
  const deltaPct = ((finalCents - lead.presupuestoEstimado) / lead.presupuestoEstimado) * 100;

  await db
    .update(leads)
    .set({ estado: 'ganado', updatedAt: new Date() })
    .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId)));
  await db.insert(leadEstadoHistory).values({ leadId, estado: 'ganado' });
  await db.insert(precisionHistory).values({
    leadId,
    estimated: lead.presupuestoEstimado,
    final: finalCents,
    deltaPct: deltaPct.toFixed(2),
  });

  revalidatePath('/panel');
  revalidatePath(`/panel/${leadId}`);
}

export async function editarConceptos(leadId: string, formData: FormData) {
  const tenantId = await getTenantId();
  const [lead] = await db
    .select()
    .from(leads)
    .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId)))
    .limit(1);
  if (!lead) throw new Error('Lead no encontrado.');

  const snapshot = lead.desgloseSnapshot as ({ result: BudgetResult } & Record<string, unknown>) | null;
  if (!snapshot?.result) {
    throw new Error('El lead no tiene presupuesto que editar.');
  }

  const keys = formData.getAll('key').map(String);
  const labels = formData.getAll('label').map(String);
  const importes = formData.getAll('importeEuros').map((v) => Number(v));

  const partidas: PartidaResult[] = labels.map((label, i) => {
    const euros = importes[i];
    const importe = Number.isFinite(euros) ? Math.round(euros * 100) : 0;
    return { key: keys[i] || `custom-${i}`, label, importe };
  });

  const edited = applyConceptoEdits(snapshot.result, partidas);

  await db
    .update(leads)
    .set({
      presupuestoEstimado: edited.total,
      desgloseSnapshot: { ...snapshot, result: edited },
      updatedAt: new Date(),
    })
    .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId)));

  revalidatePath('/panel');
  revalidatePath(`/panel/${leadId}`);
}

export async function enviarPresupuesto(leadId: string) {
  const tenantId = await getTenantId();
  const [lead] = await db
    .select()
    .from(leads)
    .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId)))
    .limit(1);
  if (!lead) throw new Error('Lead no encontrado.');
  if (lead.presupuestoEstimado == null) {
    throw new Error('El lead no tiene presupuesto que enviar.');
  }

  await db
    .update(leads)
    .set({ estado: 'presupuesto_enviado', pipelineStage: 'whatsapp_entregado', updatedAt: new Date() })
    .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId)));

  await db.insert(leadEstadoHistory).values({ leadId, estado: 'presupuesto_enviado' });
  await db.insert(leadPipelineEventos).values({
    leadId,
    stage: 'whatsapp_entregado',
    metadata: { via: 'whatsapp', simulado: true, total: lead.presupuestoEstimado },
  });

  revalidatePath('/panel');
  revalidatePath(`/panel/${leadId}`);
}

export async function solicitarOpinion(leadId: string) {
  const tenantId = await getTenantId();
  const [updated] = await db
    .update(leads)
    .set({ testimonioSolicitadoAt: new Date(), updatedAt: new Date() })
    .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId)))
    .returning({ id: leads.id });
  if (!updated) throw new Error('Lead no encontrado.');

  revalidatePath(`/panel/${leadId}`);
}

export async function recalcularPresupuesto(leadId: string) {
  const tenantId = await getTenantId();
  const [lead] = await db
    .select()
    .from(leads)
    .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId)))
    .limit(1);
  if (!lead) throw new Error('Lead no encontrado.');

  const [config, catalog] = await Promise.all([getPricingConfig(), getCatalog()]);

  const inputs: BudgetInputs = {
    tipoReforma: lead.tipoReforma ?? 'otro',
    m2Suelo: lead.m2Suelo ?? null,
    alturaTecho: lead.alturaTecho ?? null,
    calidadGlobal: lead.calidadGlobal ?? 'media',
    estructural: lead.estructural,
    provincia: lead.provincia ?? null,
    materialSelections: (lead.materialSelections as Record<string, string>) ?? {},
  };

  const result = computeBudget(inputs, config, catalog);

  await db
    .update(leads)
    .set({
      presupuestoEstimado: result.total,
      desgloseSnapshot: { stage: lead.pipelineStage, result },
      updatedAt: new Date(),
    })
    .where(and(eq(leads.id, leadId), eq(leads.tenantId, tenantId)));

  await db.insert(leadPipelineEventos).values({
    leadId,
    stage: 'presupuesto_generado',
    metadata: { total: result.total, confianza: result.confianza },
  });

  revalidatePath('/panel');
  revalidatePath(`/panel/${leadId}`);
}
