import { db } from "@/db";
import { clients, payments, time_entries, projects } from "@/db/schema";
import { sql, and, eq, inArray } from "drizzle-orm";

export async function getAnalyticsByYear(year: number) {
  const [contracted] = await db
    .select({ total: sql<string>`coalesce(sum(${projects.accepted_total}::numeric), 0)` })
    .from(projects)
    .innerJoin(clients, eq(projects.client_id, clients.id))
    .where(sql`extract(year from ${clients.created_at}) = ${year}`);

  const [clientsRow] = await db
    .select({ count: sql<string>`count(*)` })
    .from(clients)
    .where(sql`extract(year from ${clients.created_at}) = ${year}`);

  const [collected] = await db
    .select({ total: sql<string>`coalesce(sum(${payments.amount}::numeric), 0)` })
    .from(payments)
    .where(
      and(
        eq(payments.status, "saldato"),
        sql`${payments.paid_at} is not null and extract(year from ${payments.paid_at}) = ${year}`
      )
    );

  const [pending] = await db
    .select({ total: sql<string>`coalesce(sum(${payments.amount}::numeric), 0)` })
    .from(payments)
    .where(sql`${payments.status} in ('da_saldare', 'inviata')`);

  return {
    contracted: parseFloat(contracted?.total ?? "0"),
    collected: parseFloat(collected?.total ?? "0"),
    clientsAcquired: parseInt(clientsRow?.count ?? "0"),
    pending: parseFloat(pending?.total ?? "0"),
  };
}

export async function getMonthlyCollected(year: number): Promise<number[]> {
  const rows = await db
    .select({
      month: sql<number>`extract(month from ${payments.paid_at})::int`,
      total: sql<string>`coalesce(sum(${payments.amount}::numeric), 0)`,
    })
    .from(payments)
    .where(
      and(
        eq(payments.status, "saldato"),
        sql`${payments.paid_at} is not null and extract(year from ${payments.paid_at}) = ${year}`
      )
    )
    .groupBy(sql`extract(month from ${payments.paid_at})`);

  const byMonth: number[] = Array(12).fill(0);
  for (const row of rows) {
    byMonth[(row.month as number) - 1] = parseFloat(row.total);
  }
  return byMonth;
}

export async function getAvailableYears(): Promise<number[]> {
  const rows = await db
    .select({ year: sql<number>`extract(year from ${clients.created_at})::int` })
    .from(clients)
    .groupBy(sql`extract(year from ${clients.created_at})`);

  const years = rows.map((r) => r.year as number);
  const currentYear = new Date().getFullYear();
  if (!years.includes(currentYear)) years.push(currentYear);
  return years.sort((a, b) => b - a);
}

// ── Time tracking ─────────────────────────────────────────────────────────────

export type ClientTimeRow = {
  clientId: string;
  clientName: string;
  totalSeconds: number;
};

export async function getTimeByClient(year: number): Promise<ClientTimeRow[]> {
  const rows = await db
    .select({
      project_id: time_entries.project_id,
      total: sql<string>`coalesce(sum(${time_entries.duration_seconds}), 0)`,
    })
    .from(time_entries)
    .where(
      sql`${time_entries.ended_at} is not null
          and extract(year from ${time_entries.started_at}) = ${year}`
    )
    .groupBy(time_entries.project_id);

  if (rows.length === 0) return [];

  // Map project_id → client_id
  const projectIds = rows.map((r) => r.project_id);
  const projectRows = await db
    .select({ id: projects.id, client_id: projects.client_id })
    .from(projects)
    .where(inArray(projects.id, projectIds));
  const projectToClient = new Map(projectRows.map((p) => [p.id, p.client_id]));

  const allClients = await db.select({ id: clients.id, name: clients.name }).from(clients);
  const nameMap = new Map(allClients.map((c) => [c.id, c.name]));

  // Aggregate by client_id (a client may have multiple projects)
  const clientTotals = new Map<string, number>();
  for (const row of rows) {
    const clientId = projectToClient.get(row.project_id);
    if (!clientId) continue;
    clientTotals.set(clientId, (clientTotals.get(clientId) ?? 0) + parseInt(row.total));
  }

  return [...clientTotals.entries()]
    .map(([clientId, totalSeconds]) => ({
      clientId,
      clientName: nameMap.get(clientId) ?? clientId,
      totalSeconds,
    }))
    .sort((a, b) => b.totalSeconds - a.totalSeconds);
}

export async function getTotalTrackedHours(year: number): Promise<number> {
  const [row] = await db
    .select({ total: sql<string>`coalesce(sum(${time_entries.duration_seconds}), 0)` })
    .from(time_entries)
    .where(
      sql`${time_entries.ended_at} is not null
          and extract(year from ${time_entries.started_at}) = ${year}`
    );
  return Math.round(parseInt(row?.total ?? "0") / 3600 * 10) / 10;
}

// ── Redditività oraria per cliente ──────────────────────────────────────────

/** Valore orario "target" di riferimento (€/h) — costante di consulenza. */
export const TARGET_HOURLY = 100;

export type ProfitabilityMargin = "ottimo" | "in_linea" | "sotto";

export type ClientProfitabilityRow = {
  clientId: string;
  clientName: string;
  hours: number;          // ore tracciate (anno), 1 decimale
  contracted: number;     // totale contrattualizzato del cliente (accepted_total dei progetti)
  realRate: number;       // contracted / hours, arrotondato a 2 decimali
  target: number;         // TARGET_HOURLY
  margin: ProfitabilityMargin;
};

/**
 * Redditività oraria reale per cliente: contrattualizzato ÷ ore tracciate (anno).
 * Solo clienti con ore tracciate > 0 (serve il denominatore). Ordinati per realRate desc.
 */
export async function getClientProfitability(year: number): Promise<ClientProfitabilityRow[]> {
  const timeRows = await getTimeByClient(year);
  if (timeRows.length === 0) return [];

  const clientIds = timeRows.map((r) => r.clientId);

  // Contrattualizzato per cliente = somma accepted_total dei suoi progetti.
  const contractRows = await db
    .select({
      client_id: projects.client_id,
      total: sql<string>`coalesce(sum(${projects.accepted_total}::numeric), 0)`,
    })
    .from(projects)
    .where(inArray(projects.client_id, clientIds))
    .groupBy(projects.client_id);
  const contractedMap = new Map(
    contractRows.map((r) => [r.client_id, parseFloat(r.total)])
  );

  return timeRows
    .map((row) => {
      const hours = Math.round((row.totalSeconds / 3600) * 10) / 10;
      const contracted = contractedMap.get(row.clientId) ?? 0;
      const realRate = hours > 0 ? Math.round((contracted / hours) * 100) / 100 : 0;
      const margin: ProfitabilityMargin =
        realRate >= TARGET_HOURLY * 2 ? "ottimo" : realRate >= TARGET_HOURLY ? "in_linea" : "sotto";
      return {
        clientId: row.clientId,
        clientName: row.clientName,
        hours,
        contracted,
        realRate,
        target: TARGET_HOURLY,
        margin,
      };
    })
    .sort((a, b) => b.realRate - a.realRate);
}