import { eq, inArray, asc, desc, sql } from "drizzle-orm";
import { db } from "@/db";
import { clients, projects, phases, tasks, deliverables, payments, documents, notes, comments, project_offers, offer_micros, offer_micro_services, offer_services, offer_macros, offer_tier_services, services, clientTranscripts } from "@/db/schema";

/**
 * ClientView: Legacy shape used by ClientDashboard component.
 * Kept for backward compatibility — new code uses ProjectView.
 * Deliberately excludes: quote_items, service_catalog, service prices, payment amounts.
 */
export interface ClientView {
  client: {
    id: string;
    name: string;
    brand_name: string;
    brief: string;
    accepted_total: string;
  };
  phases: Array<{
    id: string;
    title: string;
    status: "upcoming" | "active" | "done";
    sort_order: number;
    tasks: Array<{
      id: string;
      title: string;
      description: string | null;
      status: "todo" | "in_progress" | "done";
      sort_order: number;
      deliverables: Array<{
        id: string;
        title: string;
        url: string | null;
        status: "pending" | "submitted" | "approved";
        approved_at: string | null;
      }>;
    }>;
    progress_pct: number;
  }>;
  payments: Array<{
    id: string;
    label: string;
    status: "da_saldare" | "inviata" | "saldato";
  }>;
  documents: Array<{
    id: string;
    label: string;
    url: string;
  }>;
  notes: Array<{
    id: string;
    body: string;
    created_at: string;
  }>;
  global_progress_pct: number;
  activeOffers?: Array<{
    id: string;
    public_name: string;
    offer_name: string;        // offer_macros.public_name — macro-level name shown to client
    offer_type: string;        // una_tantum | retainer
    cumulative_price: string;
    accepted_total: string | null;
    services: Array<{ name: string; description: string | null }>;
  }>;
  transcripts: Array<{
    id: string;
    title: string | null;
    call_date: string;
    content: string;
    created_at: string;
  }>;
}

export interface ProjectView {
  project: {
    id: string;
    name: string;
    client_id: string;
    accepted_total: string;
  };
  phases: Array<{
    id: string;
    title: string;
    status: string;
    sort_order: number;
    tasks: Array<{
      id: string;
      title: string;
      description: string | null;
      status: string;
      sort_order: number;
      deliverables: Array<{
        id: string;
        title: string;
        url: string | null;
        status: string;
        approved_at: Date | null;
        task_id: string;
      }>;
    }>;
    progress_pct: number;
  }>;
  payments: Array<{
    id: string;
    label: string;
    // amount intentionally excluded — client API never exposes payment amounts (CLAUDE.md + DASH-07)
    status: string;
  }>;
  documents: Array<{
    id: string;
    label: string;
    url: string;
    created_at: Date;
  }>;
  notes: Array<{
    id: string;
    body: string;
    created_at: Date;
  }>;
  comments: Array<{
    id: string;
    entity_type: string;
    entity_id: string;
    author: string;
    body: string;
    created_at: Date;
  }>;
  global_progress_pct: number;
  activeOffers?: Array<{
    id: string;
    public_name: string;        // offer_micros.public_name — NEVER internal_name
    offer_name: string;         // offer_macros.public_name — macro-level name shown to client
    offer_type: string;         // una_tantum | retainer
    cumulative_price: string;   // sum of assigned services.unit_price (new) or offer_services.price (legacy)
    accepted_total: string | null;
    services: Array<{ name: string; description: string | null }>;  // included services list
  }>;
  transcripts: Array<{
    id: string;
    title: string | null;
    call_date: string;
    content: string;
    created_at: Date;
  }>;
}

export interface ClientProjectSummary {
  client: {
    id: string;
    name: string;
    brand_name: string;
    token: string;
    slug: string | null;
  };
  projects: Array<{
    id: string;
    name: string;
    archived: boolean;
  }>;
}

/**
 * Resolves a token-or-slug to a client and returns the client's active projects.
 * Lookup order: slug first, then token — mirrors middleware order (D-06).
 */
export async function getClientWithProjectsByToken(
  tokenOrSlug: string
): Promise<ClientProjectSummary | null> {
  try {
  // Try slug first
  let clientRows = await db
    .select({
      id: clients.id,
      name: clients.name,
      brand_name: clients.brand_name,
      token: clients.token,
      slug: clients.slug,
    })
    .from(clients)
    .where(eq(clients.slug, tokenOrSlug))
    .limit(1);

  // Fall back to token
  if (clientRows.length === 0) {
    clientRows = await db
      .select({
        id: clients.id,
        name: clients.name,
        brand_name: clients.brand_name,
        token: clients.token,
        slug: clients.slug,
      })
      .from(clients)
      .where(eq(clients.token, tokenOrSlug))
      .limit(1);
  }

  if (clientRows.length === 0) return null;
  const client = clientRows[0];

  const projectRows = await db
    .select({ id: projects.id, name: projects.name, archived: projects.archived })
    .from(projects)
    .where(eq(projects.client_id, client.id))
    .orderBy(asc(projects.created_at));

  const activeProjects = projectRows.filter((p) => !p.archived);

  return { client, projects: activeProjects };
  } catch (err) {
    console.error("[client-view] getClientWithProjectsByToken error:", err);
    return null;
  }
}

/**
 * Returns full project data for the client dashboard.
 * CRITICAL: Does NOT include quote_items — client API never exposes them (CLAUDE.md constraint).
 * payments include status only, NOT amount or unit_price (DASH-07).
 */
export async function getProjectView(projectId: string): Promise<ProjectView | null> {
  const projectRows = await db
    .select({
      id: projects.id,
      name: projects.name,
      client_id: projects.client_id,
      accepted_total: projects.accepted_total,
    })
    .from(projects)
    .where(eq(projects.id, projectId))
    .limit(1);

  if (projectRows.length === 0) return null;
  const project = projectRows[0];

  const phasesRows = await db
    .select()
    .from(phases)
    .where(eq(phases.project_id, projectId))
    .orderBy(asc(phases.sort_order));

  const phaseIds = phasesRows.map((p) => p.id);

  const tasksRows =
    phaseIds.length === 0
      ? []
      : await db
          .select()
          .from(tasks)
          .where(inArray(tasks.phase_id, phaseIds))
          .orderBy(asc(tasks.sort_order));

  const taskIds = tasksRows.map((t) => t.id);

  // approved_at included — immutable audit trail (CLAUDE.md constraint LOCKED)
  const deliverablesRows =
    taskIds.length === 0
      ? []
      : await db
          .select({
            id: deliverables.id,
            title: deliverables.title,
            url: deliverables.url,
            status: deliverables.status,
            approved_at: deliverables.approved_at,
            task_id: deliverables.task_id,
          })
          .from(deliverables)
          .where(inArray(deliverables.task_id, taskIds));

  // Payments — status only, NO amount (DASH-07 / CLAUDE.md)
  const paymentsRows = await db
    .select({
      id: payments.id,
      label: payments.label,
      status: payments.status,
      // amount intentionally excluded — client API never exposes payment amounts
    })
    .from(payments)
    .where(eq(payments.project_id, projectId));

  const documentsRows = await db
    .select({
      id: documents.id,
      label: documents.label,
      url: documents.url,
      created_at: documents.created_at,
    })
    .from(documents)
    .where(eq(documents.project_id, projectId))
    .orderBy(asc(documents.created_at));

  const notesRows = await db
    .select({ id: notes.id, body: notes.body, created_at: notes.created_at })
    .from(notes)
    .where(eq(notes.project_id, projectId))
    .orderBy(asc(notes.created_at));

  // Fetch active offers for this project (client-safe fields only — never internal_name)
  const projectOfferRows = await db
    .select({
      id: project_offers.id,
      public_name: offer_micros.public_name,        // EXPLICITLY public_name, never internal_name
      offer_name: offer_macros.public_name,          // macro-level public name — NEVER internal_name
      offer_type: offer_macros.offer_type,           // una_tantum | retainer
      accepted_total: project_offers.accepted_total,
      micro_id: project_offers.micro_id,
    })
    .from(project_offers)
    .innerJoin(offer_micros, eq(project_offers.micro_id, offer_micros.id))
    .innerJoin(offer_macros, eq(offer_micros.macro_id, offer_macros.id))
    .where(eq(project_offers.project_id, projectId));

  // Cumulative price + service list per micro.
  // Primary: offer_tier_services → services (Phase 12 catalog). Fallback: legacy offer_micro_services → offer_services.
  const microIds = projectOfferRows.map((o) => o.micro_id);

  // New-style: services from offer_tier_services joined to unified services catalog
  const tierServiceRows = microIds.length === 0 ? [] : await db
    .select({
      tier_id: offer_tier_services.tier_id,
      name: services.name,
      description: services.description,
      unit_price: services.unit_price,
    })
    .from(offer_tier_services)
    .innerJoin(services, eq(offer_tier_services.service_id, services.id))
    .where(inArray(offer_tier_services.tier_id, microIds));

  // Group by tier_id for quick lookup
  const tierServicesMap = new Map<string, Array<{ name: string; description: string | null; unit_price: string }>>();
  for (const row of tierServiceRows) {
    const existing = tierServicesMap.get(row.tier_id) ?? [];
    existing.push({ name: row.name, description: row.description, unit_price: row.unit_price });
    tierServicesMap.set(row.tier_id, existing);
  }

  // Determine which micros have NO entries in the new table → need legacy fallback
  const microsNeedingLegacy = microIds.filter((id) => !tierServicesMap.has(id));

  // Legacy fallback: offer_micro_services → offer_services (for old offers with no tier_services rows)
  const legacyPriceRows = microsNeedingLegacy.length === 0 ? [] : await db
    .select({
      micro_id: offer_micro_services.micro_id,
      name: offer_services.name,
      price: offer_services.price,
    })
    .from(offer_micro_services)
    .innerJoin(offer_services, eq(offer_micro_services.service_id, offer_services.id))
    .where(inArray(offer_micro_services.micro_id, microsNeedingLegacy));

  const legacyServicesMap = new Map<string, Array<{ name: string; description: string | null; price: string }>>();
  for (const row of legacyPriceRows) {
    const existing = legacyServicesMap.get(row.micro_id) ?? [];
    existing.push({ name: row.name, description: null, price: row.price });
    legacyServicesMap.set(row.micro_id, existing);
  }

  const activeOffers = projectOfferRows.map((o) => {
    const newStyleServices = tierServicesMap.get(o.micro_id);
    const legacyServices = legacyServicesMap.get(o.micro_id);

    let cumulative_price: string;
    let servicesList: Array<{ name: string; description: string | null }>;

    if (newStyleServices && newStyleServices.length > 0) {
      // Use new catalog prices
      const total = newStyleServices.reduce((sum, s) => sum + parseFloat(s.unit_price ?? "0"), 0);
      cumulative_price = total.toFixed(2);
      servicesList = newStyleServices.map((s) => ({ name: s.name, description: s.description }));
    } else if (legacyServices && legacyServices.length > 0) {
      // Legacy fallback
      const total = legacyServices.reduce((sum, s) => sum + parseFloat(s.price ?? "0"), 0);
      cumulative_price = total.toFixed(2);
      servicesList = legacyServices.map((s) => ({ name: s.name, description: null }));
    } else {
      cumulative_price = "0";
      servicesList = [];
    }

    return {
      id: o.id,
      public_name: o.public_name,
      offer_name: o.offer_name,
      offer_type: o.offer_type,
      cumulative_price,
      accepted_total: o.accepted_total ? String(o.accepted_total) : null,
      services: servicesList,
    };
  });

  // Transcripts linked to the project's client (post lead→client conversion)
  const transcriptsRows = await db
    .select({
      id: clientTranscripts.id,
      title: clientTranscripts.title,
      call_date: clientTranscripts.call_date,
      content: clientTranscripts.content,
      created_at: clientTranscripts.created_at,
    })
    .from(clientTranscripts)
    .where(eq(clientTranscripts.client_id, project.client_id))
    .orderBy(desc(clientTranscripts.call_date));

  // Comments scoped to: general (client_id), phases, tasks, and deliverables of this project
  const allEntityIds = [
    project.client_id,                          // general messages entity_id = client_id
    ...phaseIds,                                // phase-tagged messages
    ...taskIds,
    ...deliverablesRows.map((d) => d.id),
  ];
  const commentsRows =
    allEntityIds.length === 0
      ? []
      : await db
          .select()
          .from(comments)
          .where(inArray(comments.entity_id, allEntityIds))
          .orderBy(asc(comments.created_at));

  const phasesWithTasks = phasesRows.map((phase) => {
    const phaseTasks = tasksRows
      .filter((t) => t.phase_id === phase.id)
      .map((task) => ({
        ...task,
        deliverables: deliverablesRows.filter((d) => d.task_id === task.id),
      }));

    const doneCount = phaseTasks.filter((t) => t.status === "done").length;
    const progress_pct =
      phaseTasks.length > 0 ? Math.round((doneCount / phaseTasks.length) * 100) : 0;

    return { ...phase, tasks: phaseTasks, progress_pct };
  });

  const doneTasks = tasksRows.filter((t) => t.status === "done").length;
  const global_progress_pct =
    tasksRows.length > 0 ? Math.round((doneTasks / tasksRows.length) * 100) : 0;

  return {
    project: {
      id: project.id,
      name: project.name,
      client_id: project.client_id,
      accepted_total: project.accepted_total ?? "0",
    },
    phases: phasesWithTasks,
    payments: paymentsRows,
    documents: documentsRows,
    notes: notesRows,
    comments: commentsRows,
    global_progress_pct,
    activeOffers: activeOffers.length > 0 ? activeOffers : undefined,
    transcripts: transcriptsRows,
  };
}