"use server";

import { z } from "zod";
import { db } from "@/db";
import { leads, quotes, tags, clientTranscripts, projects } from "@/db/schema";
import { nanoid } from "nanoid";
import { eq, and } from "drizzle-orm";
import {
  createLeadSchema,
  updateLeadSchema,
  createActivitySchema,
  LEAD_STAGES,
} from "@/lib/lead-validators";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { createActivity, updateLeadStage } from "@/lib/lead-service";
import { createClientCore } from "@/app/admin/clients/new/actions";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";

async function requireAdminSession() {
  const session = await getServerSession(authOptions);
  if (!session) throw new Error("Non autorizzato");
}

// Converts a lead into a client (reusing createClientCore): creates the client
// + default project (linked via created_from_lead_id) + payment stubs, carries
// over the lead's call transcripts, then archives the lead while keeping its
// "won" status. Idempotent: a lead already converted just redirects to its
// client. Redirects to the new client page on success.
export async function convertLeadToClient(leadId: string): Promise<void> {
  await requireAdminSession();

  const [lead] = await db.select().from(leads).where(eq(leads.id, leadId)).limit(1);
  if (!lead) throw new Error("Lead non trovato");

  // Already converted? Redirect to the linked client instead of duplicating.
  const [existing] = await db
    .select({ client_id: projects.client_id })
    .from(projects)
    .where(eq(projects.created_from_lead_id, leadId))
    .limit(1);
  if (existing) {
    redirect(`/admin/clients/${existing.client_id}`);
  }

  const { clientId } = await createClientCore({
    name: lead.name,
    brand_name: lead.company?.trim() || lead.name,
    brief: lead.notes?.trim() || `Lead convertito: ${lead.name}`,
    email: lead.email,
    phone: lead.phone,
    created_from_lead_id: lead.id,
  });

  // Carry over the lead's transcripts to the new client.
  await db
    .update(clientTranscripts)
    .set({ client_id: clientId, lead_id: null })
    .where(eq(clientTranscripts.lead_id, leadId));

  // Archive the lead but keep its "won" status (traceability without clutter).
  await db.update(leads).set({ archived: true }).where(eq(leads.id, leadId));

  revalidatePath("/admin/pipeline");
  revalidatePath("/admin");
  redirect(`/admin/clients/${clientId}`);
}

export async function createLead(data: z.infer<typeof createLeadSchema>) {
  const parsed = createLeadSchema.safeParse(data);

  if (!parsed.success) {
    return { success: false, error: parsed.error.issues[0].message };
  }

  try {
    const [lead] = await db
      .insert(leads)
      .values({
        name: parsed.data.name,
        email: parsed.data.email || null,
        phone: parsed.data.phone || null,
        company: parsed.data.company || null,
        status: parsed.data.status || "contacted",
        notes: parsed.data.notes || null,
      })
      .returning();

    revalidatePath("/admin/pipeline");
    return { success: true, lead };
  } catch (error) {
    console.error("createLead error:", error);
    return { success: false, error: "Errore nella creazione del lead" };
  }
}

export async function updateLead(id: string, data: z.infer<typeof updateLeadSchema>) {
  const parsed = updateLeadSchema.safeParse(data);

  if (!parsed.success) {
    return { success: false, error: parsed.error.issues[0].message };
  }

  try {
    const updateData: Record<string, any> = { updated_at: new Date() };

    if (parsed.data.name !== undefined) updateData.name = parsed.data.name;
    if (parsed.data.email !== undefined) updateData.email = parsed.data.email || null;
    if (parsed.data.phone !== undefined) updateData.phone = parsed.data.phone || null;
    if (parsed.data.company !== undefined) updateData.company = parsed.data.company || null;
    if (parsed.data.status !== undefined) updateData.status = parsed.data.status;
    if (parsed.data.notes !== undefined) updateData.notes = parsed.data.notes || null;

    const [updated] = await db
      .update(leads)
      .set(updateData)
      .where(eq(leads.id, id))
      .returning();

    revalidatePath("/admin/pipeline");
    revalidatePath(`/admin/pipeline/${id}`);
    return { success: true, lead: updated };
  } catch (error) {
    console.error("updateLead error:", error);
    return { success: false, error: "Errore nell'aggiornamento" };
  }
}

export async function deleteLead(id: string) {
  try {
    await db.delete(leads).where(eq(leads.id, id));
    revalidatePath("/admin/pipeline");
    return { success: true };
  } catch (error) {
    console.error("deleteLead error:", error);
    return { success: false, error: "Errore nell'eliminazione" };
  }
}

// Activity logging
export async function logActivity(data: z.infer<typeof createActivitySchema>) {
  const parsed = createActivitySchema.safeParse(data);

  if (!parsed.success) {
    return { success: false, error: parsed.error.issues[0].message };
  }

  try {
    const activity = await createActivity({
      lead_id: parsed.data.lead_id,
      type: parsed.data.type,
      activity_date: new Date(parsed.data.activity_date),
      duration_minutes: parsed.data.duration_minutes,
      notes: parsed.data.notes,
    });

    revalidatePath(`/admin/pipeline/${parsed.data.lead_id}`);
    return { success: true, activity };
  } catch (error) {
    console.error("logActivity error:", error);
    return { success: false, error: "Errore nella registrazione" };
  }
}

// Quote assignment
const assignQuoteSchema = z.object({
  lead_id: z.string().min(1),
  quote_token: z.string().optional(),
});

export async function assignQuoteToLead(data: z.infer<typeof assignQuoteSchema>) {
  const parsed = assignQuoteSchema.safeParse(data);

  if (!parsed.success) {
    return { success: false, error: parsed.error.issues[0].message };
  }

  try {
    // Link quote to lead and update lead status
    const token = parsed.data.quote_token;
    const leadId = parsed.data.lead_id;

    if (!token) {
      return { success: false, error: "Token preventivo richiesto" };
    }

    // Find quote by token
    const [quote] = await db
      .select()
      .from(quotes)
      .where(eq(quotes.token, token))
      .limit(1);

    if (!quote) {
      return { success: false, error: "Preventivo non trovato" };
    }

    // Update quote to link to lead (if not already linked)
    await db
      .update(quotes)
      .set({ lead_id: leadId })
      .where(eq(quotes.id, quote.id));

    // Update lead status to "proposal_sent"
    await updateLeadStage(leadId, "proposal_sent");

    revalidatePath(`/admin/pipeline/${leadId}`);
    return { success: true };
  } catch (error) {
    console.error("assignQuoteToLead error:", error);
    return { success: false, error: "Errore nell'assegnazione" };
  }
}

async function requireAdmin() {
  const session = await getServerSession(authOptions);
  if (!session) throw new Error("Non autorizzato");
}

// ── Inline-edit field update (Phase 14 database-view) ───────────────────────

const EDITABLE_FIELDS = ["name", "email", "phone", "company", "status", "next_action"] as const;
type EditableField = (typeof EDITABLE_FIELDS)[number];

export async function updateLeadField(
  leadId: string,
  fieldName: EditableField,
  value: string
) {
  await requireAdmin();

  if (!EDITABLE_FIELDS.includes(fieldName)) {
    throw new Error(`Campo non editabile: ${fieldName}`);
  }

  if (fieldName === "name") {
    const s = value.trim();
    if (s.length === 0) throw new Error("Nome richiesto");
    await db.update(leads).set({ name: s, updated_at: new Date() }).where(eq(leads.id, leadId));
  } else if (fieldName === "status") {
    if (!LEAD_STAGES.includes(value as (typeof LEAD_STAGES)[number])) {
      throw new Error("Stato non valido");
    }
    await db.update(leads).set({ status: value, updated_at: new Date() }).where(eq(leads.id, leadId));
  } else {
    // email | phone | company | next_action — nullable text fields, empty clears
    const s = value.trim();
    await db
      .update(leads)
      .set({ [fieldName]: s || null, updated_at: new Date() })
      .where(eq(leads.id, leadId));
  }

  revalidatePath("/admin/pipeline");
  revalidatePath(`/admin/pipeline/${leadId}`);
}

// ── Lead tags (Phase 14, CRM-09) — polymorphic `tags` table, entity_type="leads" ─

const LEADS_TAG_ENTITY = "leads";

export async function addLeadTag(leadId: string, value: string) {
  await requireAdmin();
  const trimmed = value.trim();
  if (trimmed.length === 0) throw new Error("Valore richiesto");

  await db
    .insert(tags)
    .values({ entity_type: LEADS_TAG_ENTITY, entity_id: leadId, name: trimmed })
    .onConflictDoNothing();

  revalidatePath("/admin/pipeline");
  revalidatePath(`/admin/pipeline/${leadId}`);
}

export async function removeLeadTag(leadId: string, value: string) {
  await requireAdmin();

  await db
    .delete(tags)
    .where(
      and(
        eq(tags.entity_type, LEADS_TAG_ENTITY),
        eq(tags.entity_id, leadId),
        eq(tags.name, value)
      )
    );

  revalidatePath("/admin/pipeline");
  revalidatePath(`/admin/pipeline/${leadId}`);
}

export async function renameLeadTag(oldValue: string, newValue: string) {
  await requireAdmin();
  const next = newValue.trim();
  if (next.length === 0) throw new Error("Nuovo nome richiesto");
  if (next === oldValue) return;

  await db
    .update(tags)
    .set({ name: next })
    .where(and(eq(tags.entity_type, LEADS_TAG_ENTITY), eq(tags.name, oldValue)));

  revalidatePath("/admin/pipeline");
}

// ── Transcript actions (Phase 20 — KB-02) ────────────────────────────────────

const addTranscriptSchema = z.object({
  lead_id: z.string().min(1),
  title: z.string().optional(),
  content: z.string().min(1, "Il testo del transcript è obbligatorio"),
  call_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Data non valida (YYYY-MM-DD)"),
});

export async function addTranscript(data: z.infer<typeof addTranscriptSchema>) {
  await requireAdmin();

  const parsed = addTranscriptSchema.safeParse(data);
  if (!parsed.success) {
    return { success: false, error: parsed.error.issues[0].message };
  }

  try {
    const [transcript] = await db
      .insert(clientTranscripts)
      .values({
        id: nanoid(),
        lead_id: parsed.data.lead_id,
        title: parsed.data.title || null,
        content: parsed.data.content,
        call_date: parsed.data.call_date,
      })
      .returning();

    revalidatePath(`/admin/pipeline/${parsed.data.lead_id}`);
    return { success: true, transcript };
  } catch (error) {
    console.error("addTranscript error:", error);
    return { success: false, error: "Errore nel salvataggio del transcript" };
  }
}

export async function deleteTranscript(transcriptId: string, leadId: string) {
  await requireAdmin();

  if (!transcriptId) throw new Error("ID transcript richiesto");

  try {
    await db
      .delete(clientTranscripts)
      .where(eq(clientTranscripts.id, transcriptId));

    revalidatePath(`/admin/pipeline/${leadId}`);
    return { success: true };
  } catch (error) {
    console.error("deleteTranscript error:", error);
    return { success: false, error: "Errore nell'eliminazione del transcript" };
  }
}
