import { DepartmentKey, type Prisma, type PrismaClient, WeeklyKpiStatus } from "@prisma/client";
import { prisma } from "@/lib/prisma";
import { KPI_BASELINE_SOURCE, KPI_BASELINE_WEEK_END, KPI_BASELINE_WEEK_START, WEEKLY_KPI_BASELINE_SECTIONS } from "@/lib/kpis/baseline";
import { addDays, getWeekBounds, mexicoTodayIso } from "@/lib/kpis/date";
import { buildRowKey, deriveKpiStatus, normalizeRowKey, parseDueDateIso, parseNumber, parsePercent } from "@/lib/kpis/parsing";
import type {
  KpiBoardCoverage,
  KpiBoardSummary,
  KpiBoardResponse,
  KpiRowStatus,
  KpiSectionDTO,
  KpiViewerContext,
  WeeklyKpiIngestPayload,
  WeeklyKpiPatchPayload,
} from "@/lib/kpis/types";

const ensureSnapshotInFlight = new Map<string, Promise<void>>();

function toDepartmentKey(value: string | null | undefined): DepartmentKey | null {
  if (!value) {
    return null;
  }

  if (value === DepartmentKey.marketing || value === DepartmentKey.administracion || value === DepartmentKey.capital_humano || value === DepartmentKey.operaciones || value === DepartmentKey.proyectos) {
    return value;
  }

  return null;
}

function toRowStatus(value: WeeklyKpiStatus): KpiRowStatus {
  if (value === WeeklyKpiStatus.on_track || value === WeeklyKpiStatus.watch || value === WeeklyKpiStatus.risk) {
    return value;
  }

  return "no_score";
}

function toPrismaStatus(value: KpiRowStatus): WeeklyKpiStatus {
  if (value === "on_track") {
    return WeeklyKpiStatus.on_track;
  }

  if (value === "watch") {
    return WeeklyKpiStatus.watch;
  }

  if (value === "risk") {
    return WeeklyKpiStatus.risk;
  }

  return WeeklyKpiStatus.no_score;
}

function toIsoDate(value: Date | null): string | null {
  if (!value) {
    return null;
  }

  return value.toISOString().slice(0, 10);
}

function canonicalMappedDepartment(rawSectionLabel: string, mappedDepartment: DepartmentKey | null): DepartmentKey | null {
  const normalized = rawSectionLabel.toUpperCase();

  if (normalized.includes("ADMINISTRACION") || normalized.includes("PRIMITIVO V.")) {
    return DepartmentKey.capital_humano;
  }

  if (normalized.includes("OOH / COMUNIDAD") || normalized.includes("COMMUNITY SQUAD") || normalized.includes("CHAMPIONS")) {
    return DepartmentKey.proyectos;
  }

  if (mappedDepartment === DepartmentKey.administracion) {
    return DepartmentKey.capital_humano;
  }

  return mappedDepartment;
}

function normalizeTextForDedup(value: string | null | undefined): string {
  if (!value) {
    return "";
  }

  return value
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase()
    .replace(/\s+/g, " ")
    .trim();
}

function buildStableRowKey(input: {
  rowKey?: string | null;
  responsibility: string;
  fallbackIndex: number;
}): string {
  return normalizeRowKey(input.rowKey ?? buildRowKey([input.responsibility])) || `row-${input.fallbackIndex + 1}`;
}

function dedupeSectionRowsInput(
  rows: WeeklyKpiIngestPayload["sections"][number]["rows"]
): WeeklyKpiIngestPayload["sections"][number]["rows"] {
  const deduped = new Map<string, WeeklyKpiIngestPayload["sections"][number]["rows"][number]>();

  for (const [rowIndex, row] of rows.entries()) {
    const responsibility = row.responsibility.trim();
    if (!responsibility) {
      continue;
    }

    const rowKey = buildStableRowKey({
      rowKey: row.rowKey,
      responsibility,
      fallbackIndex: rowIndex,
    });

    const dedupKey = normalizeRowKey(rowKey) || normalizeTextForDedup(responsibility);
    const existing = deduped.get(dedupKey);
    if (!existing) {
      deduped.set(dedupKey, {
        ...row,
        responsibility,
        rowKey,
        sortOrder: row.sortOrder ?? rowIndex,
      });
      continue;
    }

    deduped.set(dedupKey, {
      ...existing,
      objectiveIndicator:
        row.objectiveIndicator?.trim().length ? row.objectiveIndicator : existing.objectiveIndicator,
      quantityQuality: row.quantityQuality?.trim().length ? row.quantityQuality : existing.quantityQuality,
      compliance: row.compliance?.trim().length ? row.compliance : existing.compliance,
      dueCommitment: row.dueCommitment?.trim().length ? row.dueCommitment : existing.dueCommitment,
    });
  }

  return Array.from(deduped.values()).sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
}

function dedupeSectionsInput(
  sections: WeeklyKpiIngestPayload["sections"]
): WeeklyKpiIngestPayload["sections"] {
  const deduped = new Map<string, WeeklyKpiIngestPayload["sections"][number]>();

  for (const [sectionIndex, section] of sections.entries()) {
    const rawSectionLabel = section.rawSectionLabel.trim();
    if (!rawSectionLabel) {
      continue;
    }

    const canonicalDepartment = canonicalMappedDepartment(rawSectionLabel, toDepartmentKey(section.mappedDepartment ?? null));
    const sectionKeyFallback =
      normalizeRowKey(section.sectionKey ?? `${canonicalDepartment ?? "unmapped"}-${rawSectionLabel}`) || `section-${sectionIndex + 1}`;
    const dedupKey = `${canonicalDepartment ?? "unmapped"}|${normalizeTextForDedup(rawSectionLabel)}`;
    const rows = dedupeSectionRowsInput(section.rows);
    if (rows.length === 0) {
      continue;
    }

    const normalizedSection: WeeklyKpiIngestPayload["sections"][number] = {
      ...section,
      rawSectionLabel,
      mappedDepartment: canonicalDepartment,
      sectionKey: section.sectionKey?.trim() ? section.sectionKey : sectionKeyFallback,
      ownerTeamLabel: section.ownerTeamLabel?.trim() || null,
      sortOrder: section.sortOrder ?? sectionIndex,
      rows,
    };

    const existing = deduped.get(dedupKey);
    if (!existing) {
      deduped.set(dedupKey, normalizedSection);
      continue;
    }

    const mergedRows = dedupeSectionRowsInput([...(existing.rows ?? []), ...rows]);
    deduped.set(dedupKey, {
      ...existing,
      rows: mergedRows,
      ownerTeamLabel: existing.ownerTeamLabel ?? normalizedSection.ownerTeamLabel,
      sectionKey: existing.sectionKey ?? normalizedSection.sectionKey,
      sortOrder: Math.min(existing.sortOrder ?? sectionIndex, normalizedSection.sortOrder ?? sectionIndex),
    });
  }

  return Array.from(deduped.values()).sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
}

function resolveParsedFields(input: {
  objectiveIndicator?: string | null;
  quantityQuality?: string | null;
  compliance?: string | null;
  dueCommitment?: string | null;
  weekStart: string;
}): {
  targetValue: number | null;
  quantityValue: number | null;
  compliancePct: number | null;
  dueDate: Date | null;
  status: WeeklyKpiStatus;
} {
  const targetValue = parseNumber(input.objectiveIndicator);
  const quantityValue = parseNumber(input.quantityQuality);
  const compliancePct =
    parsePercent(input.compliance) ?? parsePercent(input.quantityQuality) ?? parsePercent(input.objectiveIndicator);
  const dueDateIso =
    parseDueDateIso(input.dueCommitment, input.weekStart) ?? parseDueDateIso(input.objectiveIndicator, input.weekStart);
  const status = toPrismaStatus(
    deriveKpiStatus({
      compliancePct,
      quantityValue,
      objectiveIndicatorText: input.objectiveIndicator,
      dueDateIso,
      todayIso: input.weekStart,
    })
  );

  return {
    targetValue,
    quantityValue,
    compliancePct,
    dueDate: dueDateIso ? new Date(`${dueDateIso}T00:00:00.000Z`) : null,
    status,
  };
}

function sectionToDTO(section: {
  id: string;
  sectionKey: string;
  rawSectionLabel: string;
  mappedDepartment: DepartmentKey | null;
  ownerTeamLabel: string | null;
  sortOrder: number;
  rows: Array<{
    id: string;
    rowKey: string;
    responsibilityText: string;
    objectiveIndicatorText: string | null;
    quantityQualityText: string | null;
    complianceText: string | null;
    dueCommitmentText: string | null;
    targetValue: number | null;
    quantityValue: number | null;
    compliancePct: number | null;
    dueDate: Date | null;
    status: WeeklyKpiStatus;
    sortOrder: number;
  }>;
}): KpiSectionDTO {
  return {
    id: section.id,
    sectionKey: section.sectionKey,
    rawSectionLabel: section.rawSectionLabel,
    mappedDepartment: canonicalMappedDepartment(section.rawSectionLabel, section.mappedDepartment),
    ownerTeamLabel: section.ownerTeamLabel,
    sortOrder: section.sortOrder,
    rows: section.rows.map((row) => ({
      id: row.id,
      rowKey: row.rowKey,
      responsibility: row.responsibilityText,
      objectiveIndicator: row.objectiveIndicatorText,
      quantityQuality: row.quantityQualityText,
      compliance: row.complianceText,
      dueCommitment: row.dueCommitmentText,
      targetValue: row.targetValue,
      quantityValue: row.quantityValue,
      compliancePct: row.compliancePct,
      dueDate: toIsoDate(row.dueDate),
      status: toRowStatus(row.status),
      sortOrder: row.sortOrder,
    })),
  };
}

export function canViewWeeklyKpiBoard(viewer: KpiViewerContext): boolean {
  return viewer.role === "owner" || viewer.role === "leader" || viewer.role === "lead";
}

function normalizeDepartmentScope(department: DepartmentKey | null | undefined): DepartmentKey | null {
  if (!department) {
    return null;
  }

  if (department === DepartmentKey.administracion) {
    return DepartmentKey.capital_humano;
  }

  return department;
}

export function canEditWeeklyKpiRow(viewer: KpiViewerContext, mappedDepartment: DepartmentKey | null): boolean {
  if (viewer.role === "owner") {
    return true;
  }

  if ((viewer.role === "leader" || viewer.role === "lead") && viewer.department && mappedDepartment) {
    return normalizeDepartmentScope(viewer.department) === normalizeDepartmentScope(mappedDepartment);
  }

  return false;
}

export function scopeSectionsForViewer<T extends { mappedDepartment: DepartmentKey | null }>(
  sections: T[],
  viewer: KpiViewerContext
): T[] {
  if (viewer.role === "owner") {
    return sections;
  }

  if ((viewer.role === "leader" || viewer.role === "lead") && viewer.department) {
    const scopedDepartment = normalizeDepartmentScope(viewer.department);
    return sections.filter((section) => normalizeDepartmentScope(section.mappedDepartment) === scopedDepartment);
  }

  return [];
}

async function upsertWeeklySnapshot(client: Prisma.TransactionClient | PrismaClient, payload: {
  weekStart: string;
  weekEnd: string;
  source: string;
  sections: WeeklyKpiIngestPayload["sections"];
}) {
  const snapshot = await client.weeklyKpiSnapshot.upsert({
    where: { weekStart: new Date(`${payload.weekStart}T00:00:00.000Z`) },
    create: {
      weekStart: new Date(`${payload.weekStart}T00:00:00.000Z`),
      weekEnd: new Date(`${payload.weekEnd}T00:00:00.000Z`),
      source: payload.source,
    },
    update: {
      weekEnd: new Date(`${payload.weekEnd}T00:00:00.000Z`),
      source: payload.source,
    },
  });

  for (const [sectionIndex, sectionInput] of payload.sections.entries()) {
    const rawSectionLabel = sectionInput.rawSectionLabel.trim();
    const mappedDepartment = canonicalMappedDepartment(rawSectionLabel, toDepartmentKey(sectionInput.mappedDepartment ?? null));
    const sectionKey =
      normalizeRowKey(sectionInput.sectionKey ?? `${mappedDepartment ?? "unmapped"}-${rawSectionLabel}`) || `section-${sectionIndex + 1}`;
    const ownerTeamLabel = sectionInput.ownerTeamLabel?.trim() || null;

    const existingByKey = await client.weeklyKpiSection.findUnique({
      where: {
        snapshotId_sectionKey: {
          snapshotId: snapshot.id,
          sectionKey,
        },
      },
      select: { id: true },
    });

    const existingByLabel =
      existingByKey ??
      (await client.weeklyKpiSection.findFirst({
        where: {
          snapshotId: snapshot.id,
          mappedDepartment,
          rawSectionLabel,
        },
        orderBy: [{ sortOrder: "asc" }, { createdAt: "asc" }],
        select: { id: true },
      }));

    const section = existingByLabel
      ? await client.weeklyKpiSection.update({
          where: { id: existingByLabel.id },
          data: {
            sectionKey,
            rawSectionLabel,
            mappedDepartment,
            ownerTeamLabel,
            sortOrder: sectionInput.sortOrder ?? sectionIndex,
          },
        })
      : await client.weeklyKpiSection.create({
          data: {
            snapshotId: snapshot.id,
            sectionKey,
            rawSectionLabel,
            mappedDepartment,
            ownerTeamLabel,
            sortOrder: sectionInput.sortOrder ?? sectionIndex,
          },
        });

    for (const [rowIndex, rowInput] of sectionInput.rows.entries()) {
      const rowKey = buildStableRowKey({
        rowKey: rowInput.rowKey,
        responsibility: rowInput.responsibility,
        fallbackIndex: rowIndex,
      });

      const parsed = resolveParsedFields({
        objectiveIndicator: rowInput.objectiveIndicator,
        quantityQuality: rowInput.quantityQuality,
        compliance: rowInput.compliance,
        dueCommitment: rowInput.dueCommitment,
        weekStart: payload.weekStart,
      });

      await client.weeklyKpiRow.upsert({
        where: {
          sectionId_rowKey: {
            sectionId: section.id,
            rowKey,
          },
        },
        create: {
          sectionId: section.id,
          rowKey,
          responsibilityText: rowInput.responsibility.trim(),
          objectiveIndicatorText: rowInput.objectiveIndicator?.trim() || null,
          quantityQualityText: rowInput.quantityQuality?.trim() || null,
          complianceText: rowInput.compliance?.trim() || null,
          dueCommitmentText: rowInput.dueCommitment?.trim() || null,
          targetValue: parsed.targetValue,
          quantityValue: parsed.quantityValue,
          compliancePct: parsed.compliancePct,
          dueDate: parsed.dueDate,
          status: parsed.status,
          sortOrder: rowInput.sortOrder ?? rowIndex,
        },
        update: {
          responsibilityText: rowInput.responsibility.trim(),
          objectiveIndicatorText: rowInput.objectiveIndicator?.trim() || null,
          quantityQualityText: rowInput.quantityQuality?.trim() || null,
          complianceText: rowInput.compliance?.trim() || null,
          dueCommitmentText: rowInput.dueCommitment?.trim() || null,
          targetValue: parsed.targetValue,
          quantityValue: parsed.quantityValue,
          compliancePct: parsed.compliancePct,
          dueDate: parsed.dueDate,
          status: parsed.status,
          sortOrder: rowInput.sortOrder ?? rowIndex,
        },
      });
    }
  }
}

async function createWeeklySnapshotFromTemplate(client: PrismaClient, payload: {
  weekStart: string;
  weekEnd: string;
  source: string;
  sections: WeeklyKpiIngestPayload["sections"];
}) {
  await client.weeklyKpiSnapshot.create({
    data: {
      weekStart: new Date(`${payload.weekStart}T00:00:00.000Z`),
      weekEnd: new Date(`${payload.weekEnd}T00:00:00.000Z`),
      source: payload.source,
      sections: {
        create: payload.sections.map((sectionInput, sectionIndex) => ({
          sectionKey:
            normalizeRowKey(
              sectionInput.sectionKey ??
                `${canonicalMappedDepartment(sectionInput.rawSectionLabel.trim(), toDepartmentKey(sectionInput.mappedDepartment ?? null)) ?? "unmapped"}-${sectionInput.rawSectionLabel.trim()}`
            ) || `section-${sectionIndex + 1}`,
          rawSectionLabel: sectionInput.rawSectionLabel.trim(),
          mappedDepartment: canonicalMappedDepartment(
            sectionInput.rawSectionLabel.trim(),
            toDepartmentKey(sectionInput.mappedDepartment ?? null)
          ),
          ownerTeamLabel: sectionInput.ownerTeamLabel?.trim() || null,
          sortOrder: sectionInput.sortOrder ?? sectionIndex,
          rows: {
            create: sectionInput.rows.map((rowInput, rowIndex) => {
              const rowKey = buildStableRowKey({
                rowKey: rowInput.rowKey,
                responsibility: rowInput.responsibility,
                fallbackIndex: rowIndex,
              });

              const parsed = resolveParsedFields({
                objectiveIndicator: rowInput.objectiveIndicator,
                quantityQuality: rowInput.quantityQuality,
                compliance: rowInput.compliance,
                dueCommitment: rowInput.dueCommitment,
                weekStart: payload.weekStart,
              });

              return {
                rowKey,
                responsibilityText: rowInput.responsibility.trim(),
                objectiveIndicatorText: rowInput.objectiveIndicator?.trim() || null,
                quantityQualityText: rowInput.quantityQuality?.trim() || null,
                complianceText: rowInput.compliance?.trim() || null,
                dueCommitmentText: rowInput.dueCommitment?.trim() || null,
                targetValue: parsed.targetValue,
                quantityValue: parsed.quantityValue,
                compliancePct: parsed.compliancePct,
                dueDate: parsed.dueDate,
                status: parsed.status,
                sortOrder: rowInput.sortOrder ?? rowIndex,
              };
            }),
          },
        })),
      },
    },
  });
}

export async function seedWeeklyKpiBaseline(client: PrismaClient): Promise<void> {
  const payload: WeeklyKpiIngestPayload = {
    weekStart: KPI_BASELINE_WEEK_START,
    weekEnd: KPI_BASELINE_WEEK_END,
    source: KPI_BASELINE_SOURCE,
    sections: WEEKLY_KPI_BASELINE_SECTIONS.map((section, index) => ({
      sectionKey: section.sectionKey,
      rawSectionLabel: section.rawSectionLabel,
      mappedDepartment: section.mappedDepartment,
      ownerTeamLabel: section.ownerTeamLabel ?? null,
      sortOrder: index,
      rows: section.rows.map((row, rowIndex) => ({
        responsibility: row.responsibility,
        objectiveIndicator: row.objectiveIndicator ?? null,
        quantityQuality: row.quantityQuality ?? null,
        compliance: row.compliance ?? null,
        dueCommitment: row.dueCommitment ?? null,
        sortOrder: rowIndex,
      })),
    })),
  };

  await upsertWeeklySnapshot(client, {
    weekStart: payload.weekStart!,
    weekEnd: payload.weekEnd!,
    source: payload.source!,
    sections: payload.sections,
  });
}

export async function ensureWeeklyKpiBaseline(): Promise<void> {
  const existing = await prisma.weeklyKpiSnapshot.findUnique({
    where: {
      weekStart: new Date(`${KPI_BASELINE_WEEK_START}T00:00:00.000Z`),
    },
    select: { id: true },
  });

  if (existing) {
    return;
  }

  await seedWeeklyKpiBaseline(prisma);
}

function snapshotToRecurringSections(snapshot: {
  sections: Array<{
    sectionKey: string;
    rawSectionLabel: string;
    mappedDepartment: DepartmentKey | null;
    ownerTeamLabel: string | null;
    sortOrder: number;
    rows: Array<{
      rowKey: string;
      responsibilityText: string;
      objectiveIndicatorText: string | null;
      quantityQualityText: string | null;
      complianceText: string | null;
      dueCommitmentText: string | null;
      sortOrder: number;
    }>;
  }>;
}): WeeklyKpiIngestPayload["sections"] {
  return snapshot.sections.map((section) => ({
    sectionKey: section.sectionKey,
    rawSectionLabel: section.rawSectionLabel,
    mappedDepartment: section.mappedDepartment,
    ownerTeamLabel: section.ownerTeamLabel,
    sortOrder: section.sortOrder,
    rows: section.rows.map((row) => ({
      rowKey: row.rowKey,
      responsibility: row.responsibilityText,
      objectiveIndicator: row.objectiveIndicatorText,
      quantityQuality: row.quantityQualityText,
      compliance: row.complianceText,
      dueCommitment: row.dueCommitmentText,
      sortOrder: row.sortOrder,
    })),
  }));
}

async function findRecurringTemplateSnapshot(weekStart: string) {
  const weekStartDate = new Date(`${weekStart}T00:00:00.000Z`);
  const include = {
    sections: {
      include: {
        rows: {
          orderBy: { sortOrder: "asc" as const },
        },
      },
      orderBy: { sortOrder: "asc" as const },
    },
  };
  const previous = await prisma.weeklyKpiSnapshot.findFirst({
    where: {
      weekStart: {
        lt: weekStartDate,
      },
    },
    include,
    orderBy: { weekStart: "desc" },
  });

  if (previous) {
    return previous;
  }

  return prisma.weeklyKpiSnapshot.findFirst({
    include,
    orderBy: { weekStart: "asc" },
  });
}

async function runWithEnsureSnapshotLock(weekStart: string, runner: () => Promise<void>): Promise<void> {
  const inFlight = ensureSnapshotInFlight.get(weekStart);
  if (inFlight) {
    await inFlight;
    return;
  }

  const taskRef: { current: Promise<void> | null } = { current: null };
  const task = (async () => {
    try {
      await runner();
    } finally {
      if (ensureSnapshotInFlight.get(weekStart) === taskRef.current) {
        ensureSnapshotInFlight.delete(weekStart);
      }
    }
  })();
  taskRef.current = task;

  ensureSnapshotInFlight.set(weekStart, task);
  await task;
}

export async function ensureWeeklyKpiSnapshotForWeek(weekStartInput?: string | null): Promise<{ weekStart: string; weekEnd: string }> {
  await ensureWeeklyKpiBaseline();
  const { weekStart, weekEnd } = getWeekBounds(weekStartInput);
  await runWithEnsureSnapshotLock(weekStart, async () => {
    const targetWeekStartDate = new Date(`${weekStart}T00:00:00.000Z`);
    const existing = await prisma.weeklyKpiSnapshot.findUnique({
      where: {
        weekStart: targetWeekStartDate,
      },
      select: {
        id: true,
      },
    });

    if (existing) {
      return;
    }

    const latestBefore = await prisma.weeklyKpiSnapshot.findFirst({
      where: {
        weekStart: {
          lt: targetWeekStartDate,
        },
      },
      select: {
        weekStart: true,
      },
      orderBy: {
        weekStart: "desc",
      },
    });

    const template = await findRecurringTemplateSnapshot(weekStart);
    if (!template) {
      return;
    }

    const recurringSource = template.source.startsWith("recurring:")
      ? template.source
      : `recurring:${template.source}`;
    const sections = snapshotToRecurringSections(template);

    let cursorWeekStart = latestBefore
      ? addDays(latestBefore.weekStart.toISOString().slice(0, 10), 7)
      : weekStart;

    while (cursorWeekStart <= weekStart) {
      const cursorWeekEnd = getWeekBounds(cursorWeekStart).weekEnd;
      try {
        await createWeeklySnapshotFromTemplate(prisma, {
          weekStart: cursorWeekStart,
          weekEnd: cursorWeekEnd,
          source: recurringSource,
          sections,
        });
      } catch {
        const maybeExisting = await prisma.weeklyKpiSnapshot.findUnique({
          where: {
            weekStart: new Date(`${cursorWeekStart}T00:00:00.000Z`),
          },
          select: {
            id: true,
          },
        });

        if (!maybeExisting) {
          throw new Error(`Failed to ensure KPI snapshot for week ${cursorWeekStart}.`);
        }
      }

      cursorWeekStart = addDays(cursorWeekStart, 7);
    }
  });

  return { weekStart, weekEnd };
}

function summarizeRowsForViewer(
  sections: Array<{
    rawSectionLabel: string;
    mappedDepartment: DepartmentKey | null;
    rows: Array<{
      status: WeeklyKpiStatus;
      dueDate: Date | null;
    }>;
  }>,
  viewer: KpiViewerContext,
  weekStart: string
): KpiBoardSummary {
  const normalizedViewerDepartment = normalizeDepartmentScope(
    toDepartmentKey((viewer.department as string | null | undefined) ?? null)
  );
  const canSeeSection = (section: { rawSectionLabel: string; mappedDepartment: DepartmentKey | null }) => {
    const mapped = normalizeDepartmentScope(canonicalMappedDepartment(section.rawSectionLabel, section.mappedDepartment));
    if (viewer.role === "owner") {
      return true;
    }

    if ((viewer.role === "leader" || viewer.role === "lead") && normalizedViewerDepartment) {
      return mapped === normalizedViewerDepartment;
    }

    return false;
  };

  const start = new Date(`${weekStart}T00:00:00.000Z`).getTime();
  const horizon = start + 7 * 24 * 60 * 60 * 1000;
  let totalRows = 0;
  let scoredRows = 0;
  let onTrackRows = 0;
  let atRiskRows = 0;
  let dueSoonRows = 0;

  for (const section of sections) {
    if (!canSeeSection(section)) {
      continue;
    }

    for (const row of section.rows) {
      totalRows += 1;
      if (row.status !== WeeklyKpiStatus.no_score) {
        scoredRows += 1;
      }
      if (row.status === WeeklyKpiStatus.on_track) {
        onTrackRows += 1;
      }
      if (row.status === WeeklyKpiStatus.risk) {
        atRiskRows += 1;
      }
      if (row.dueDate) {
        const due = row.dueDate.getTime();
        if (due >= start && due <= horizon) {
          dueSoonRows += 1;
        }
      }
    }
  }

  return {
    totalRows,
    scoredRows,
    onTrackRows,
    onTrackPct: scoredRows > 0 ? Math.round((onTrackRows / scoredRows) * 1000) / 10 : 0,
    atRiskRows,
    dueSoonRows,
  };
}

function summarizeBoard(sections: KpiSectionDTO[], weekStart: string): KpiBoardSummary {
  const rows = sections.flatMap((section) => section.rows);
  const scoredRows = rows.filter((row) => row.status !== "no_score");
  const onTrackRows = rows.filter((row) => row.status === "on_track");
  const atRiskRows = rows.filter((row) => row.status === "risk");
  const dueSoonRows = rows.filter((row) => {
    if (!row.dueDate) {
      return false;
    }

    const due = new Date(`${row.dueDate}T00:00:00.000Z`).getTime();
    const start = new Date(`${weekStart}T00:00:00.000Z`).getTime();
    return due >= start && due <= start + 7 * 24 * 60 * 60 * 1000;
  });

  return {
    totalRows: rows.length,
    scoredRows: scoredRows.length,
    onTrackRows: onTrackRows.length,
    onTrackPct: scoredRows.length > 0 ? Math.round((onTrackRows.length / scoredRows.length) * 1000) / 10 : 0,
    atRiskRows: atRiskRows.length,
    dueSoonRows: dueSoonRows.length,
  };
}

function summarizeCoverage(sections: KpiSectionDTO[]): KpiBoardCoverage {
  const totalSections = sections.length;
  const mappedSections = sections.filter((section) => Boolean(section.mappedDepartment)).length;
  const ownerAssignedSections = sections.filter((section) => Boolean(section.ownerTeamLabel?.trim())).length;

  return {
    mappedSections,
    ownerAssignedSections,
    totalSections,
    mappedPct: totalSections > 0 ? Math.round((mappedSections / totalSections) * 1000) / 10 : 0,
    ownerAssignedPct: totalSections > 0 ? Math.round((ownerAssignedSections / totalSections) * 1000) / 10 : 0,
  };
}

function dedupeRowsForSectionDTO(rows: KpiSectionDTO["rows"]): KpiSectionDTO["rows"] {
  const deduped = new Map<string, KpiSectionDTO["rows"][number]>();
  for (const row of rows) {
    const dedupKey = normalizeTextForDedup(row.responsibility) || normalizeRowKey(row.rowKey) || row.id;
    const existing = deduped.get(dedupKey);
    if (!existing) {
      deduped.set(dedupKey, row);
      continue;
    }

    const preferred = row.sortOrder < existing.sortOrder ? row : existing;
    deduped.set(dedupKey, preferred);
  }

  return Array.from(deduped.values()).sort((a, b) => a.sortOrder - b.sortOrder);
}

function dedupeSectionsForBoard(sections: KpiSectionDTO[]): KpiSectionDTO[] {
  const deduped = new Map<string, KpiSectionDTO>();

  for (const section of sections) {
    const canonicalDepartment = canonicalMappedDepartment(section.rawSectionLabel, section.mappedDepartment);
    const dedupKey = `${canonicalDepartment ?? "unmapped"}|${normalizeTextForDedup(section.rawSectionLabel)}`;
    const existing = deduped.get(dedupKey);
    if (!existing) {
      deduped.set(dedupKey, {
        ...section,
        mappedDepartment: canonicalDepartment,
        rows: dedupeRowsForSectionDTO(section.rows),
      });
      continue;
    }

    deduped.set(dedupKey, {
      ...existing,
      rows: dedupeRowsForSectionDTO([...existing.rows, ...section.rows]),
      sortOrder: Math.min(existing.sortOrder, section.sortOrder),
      ownerTeamLabel: existing.ownerTeamLabel ?? section.ownerTeamLabel,
    });
  }

  return Array.from(deduped.values()).sort((a, b) => a.sortOrder - b.sortOrder);
}

function emptyBoard(weekStart: string, weekEnd: string): KpiBoardResponse {
  return {
    weekStart,
    weekEnd,
    source: "platform",
    lastUpdatedAt: null,
    coverage: {
      mappedSections: 0,
      ownerAssignedSections: 0,
      totalSections: 0,
      mappedPct: 0,
      ownerAssignedPct: 0,
    },
    sections: [],
    summary: {
      totalRows: 0,
      scoredRows: 0,
      onTrackRows: 0,
      onTrackPct: 0,
      atRiskRows: 0,
      dueSoonRows: 0,
    },
  };
}

export async function getWeeklyKpiBoard(input: {
  weekStartInput?: string | null;
  viewer: KpiViewerContext;
}): Promise<KpiBoardResponse> {
  const { weekStart, weekEnd } = await ensureWeeklyKpiSnapshotForWeek(input.weekStartInput);

  const snapshot = await prisma.weeklyKpiSnapshot.findUnique({
    where: {
      weekStart: new Date(`${weekStart}T00:00:00.000Z`),
    },
    include: {
      sections: {
        include: {
          rows: {
            orderBy: { sortOrder: "asc" },
          },
        },
        orderBy: { sortOrder: "asc" },
      },
    },
  });

  if (!snapshot) {
    return emptyBoard(weekStart, weekEnd);
  }

  const dtoSections = dedupeSectionsForBoard(snapshot.sections.map(sectionToDTO));
  const scopedSections = scopeSectionsForViewer(dtoSections, input.viewer);
  const summary = summarizeBoard(scopedSections, weekStart);
  const coverage = summarizeCoverage(scopedSections);
  const previousWeekStart = addDays(weekStart, -7);
  const previousSnapshot = await prisma.weeklyKpiSnapshot.findUnique({
    where: {
      weekStart: new Date(`${previousWeekStart}T00:00:00.000Z`),
    },
    select: {
      sections: {
        select: {
          rawSectionLabel: true,
          mappedDepartment: true,
          rows: {
            select: {
              status: true,
              dueDate: true,
            },
          },
        },
      },
    },
  });
  const previousWeekSummary = previousSnapshot
    ? summarizeRowsForViewer(previousSnapshot.sections, input.viewer, previousWeekStart)
    : undefined;

  return {
    weekStart,
    weekEnd: snapshot.weekEnd.toISOString().slice(0, 10),
    source: snapshot.source,
    lastUpdatedAt: snapshot.updatedAt.toISOString(),
    coverage,
    sections: scopedSections,
    summary,
    previousWeekSummary,
  };
}

export async function upsertWeeklyKpiFromPayload(payload: WeeklyKpiIngestPayload): Promise<KpiBoardResponse> {
  const { weekStart, weekEnd } = getWeekBounds(payload.weekStart);
  const source = payload.source?.trim() || "platform";

  const sections = dedupeSectionsInput(
    payload.sections
      .filter((section) => section.rawSectionLabel.trim().length > 0)
      .map((section, index) => ({
        ...section,
        sortOrder: section.sortOrder ?? index,
        rows: section.rows
          .filter((row) => row.responsibility.trim().length > 0)
          .map((row, rowIndex) => ({
            ...row,
            sortOrder: row.sortOrder ?? rowIndex,
          })),
      }))
      .filter((section) => section.rows.length > 0)
  );

  if (sections.length === 0) {
    throw new Error("At least one KPI section with rows is required.");
  }

  await prisma.$transaction(async (tx) => {
    await upsertWeeklySnapshot(tx, {
      weekStart,
      weekEnd: payload.weekEnd && /^\d{4}-\d{2}-\d{2}$/.test(payload.weekEnd) ? payload.weekEnd : weekEnd,
      source,
      sections,
    });
  });

  return getWeeklyKpiBoard({
    weekStartInput: weekStart,
    viewer: { role: "owner", department: null },
  });
}

export async function patchWeeklyKpiRow(
  rowId: string,
  payload: WeeklyKpiPatchPayload,
  viewer: KpiViewerContext
) {
  const existing = await prisma.weeklyKpiRow.findUnique({
    where: { id: rowId },
    include: {
      section: {
        include: {
          snapshot: true,
        },
      },
    },
  });

  if (!existing) {
    throw new Error("KPI row not found.");
  }

  const mappedDepartment = canonicalMappedDepartment(existing.section.rawSectionLabel, existing.section.mappedDepartment);

  if (!canEditWeeklyKpiRow(viewer, mappedDepartment)) {
    throw new Error("Forbidden");
  }

  const isOwner = viewer.role === "owner";
  if (!isOwner && (payload.responsibility !== undefined || payload.objectiveIndicator !== undefined)) {
    throw new Error("Only owner can modify core weekly KPI definitions.");
  }

  const objectiveIndicator = payload.objectiveIndicator ?? existing.objectiveIndicatorText;
  const quantityQuality = payload.quantityQuality ?? existing.quantityQualityText;
  const compliance = payload.compliance ?? existing.complianceText;
  const dueCommitment = payload.dueCommitment ?? existing.dueCommitmentText;
  const weekStart = existing.section.snapshot.weekStart.toISOString().slice(0, 10);

  const parsed = resolveParsedFields({
    objectiveIndicator,
    quantityQuality,
    compliance,
    dueCommitment,
    weekStart,
  });

  const nextResponsibility = payload.responsibility?.trim() || existing.responsibilityText;
  const rowKey =
    normalizeRowKey(buildRowKey([nextResponsibility])) ||
    normalizeRowKey(existing.rowKey) ||
    `row-${existing.sortOrder + 1}`;

  const updated = await prisma.$transaction(async (tx) => {
    const row = await tx.weeklyKpiRow.update({
      where: { id: rowId },
      data: {
        rowKey,
        responsibilityText: nextResponsibility,
        objectiveIndicatorText: objectiveIndicator?.trim() || null,
        quantityQualityText: quantityQuality?.trim() || null,
        complianceText: compliance?.trim() || null,
        dueCommitmentText: dueCommitment?.trim() || null,
        targetValue: parsed.targetValue,
        quantityValue: parsed.quantityValue,
        compliancePct: parsed.compliancePct,
        dueDate: parsed.dueDate,
        status: parsed.status,
      },
    });

    await tx.weeklyKpiSnapshot.update({
      where: { id: existing.section.snapshot.id },
      data: {
        updatedAt: new Date(),
      },
    });

    return row;
  });

  return {
    id: updated.id,
    rowKey: updated.rowKey,
    responsibility: updated.responsibilityText,
    objectiveIndicator: updated.objectiveIndicatorText,
    quantityQuality: updated.quantityQualityText,
    compliance: updated.complianceText,
    dueCommitment: updated.dueCommitmentText,
    targetValue: updated.targetValue,
    quantityValue: updated.quantityValue,
    compliancePct: updated.compliancePct,
    dueDate: toIsoDate(updated.dueDate),
    status: toRowStatus(updated.status),
    sortOrder: updated.sortOrder,
  };
}

export async function clearWeeklyKpisForDepartment(input: {
  weekStartInput?: string | null;
  department: DepartmentKey;
}): Promise<{
  weekStart: string;
  weekEnd: string;
  department: DepartmentKey;
  deletedSections: number;
  deletedRows: number;
}> {
  const normalizedDepartment = normalizeDepartmentScope(toDepartmentKey(input.department) ?? null);
  if (!normalizedDepartment) {
    throw new Error("Invalid department.");
  }

  const { weekStart, weekEnd } = await ensureWeeklyKpiSnapshotForWeek(input.weekStartInput);
  const snapshot = await prisma.weeklyKpiSnapshot.findUnique({
    where: {
      weekStart: new Date(`${weekStart}T00:00:00.000Z`),
    },
    select: {
      id: true,
      sections: {
        select: {
          id: true,
          rawSectionLabel: true,
          mappedDepartment: true,
          rows: {
            select: { id: true },
          },
        },
      },
    },
  });

  if (!snapshot) {
    return {
      weekStart,
      weekEnd,
      department: normalizedDepartment,
      deletedSections: 0,
      deletedRows: 0,
    };
  }

  const scopedSectionIds = snapshot.sections
    .filter(
      (section) =>
        normalizeDepartmentScope(canonicalMappedDepartment(section.rawSectionLabel, section.mappedDepartment)) ===
        normalizedDepartment
    )
    .map((section) => section.id);
  const deletedRows = snapshot.sections
    .filter((section) => scopedSectionIds.includes(section.id))
    .reduce((acc, section) => acc + section.rows.length, 0);

  if (scopedSectionIds.length > 0) {
    await prisma.$transaction([
      prisma.weeklyKpiSection.deleteMany({
        where: {
          id: {
            in: scopedSectionIds,
          },
        },
      }),
      prisma.weeklyKpiSnapshot.update({
        where: { id: snapshot.id },
        data: { updatedAt: new Date() },
      }),
    ]);
  }

  return {
    weekStart,
    weekEnd,
    department: normalizedDepartment,
    deletedSections: scopedSectionIds.length,
    deletedRows,
  };
}

export async function upsertWeeklyDepartmentKpiRow(input: {
  weekStartInput?: string | null;
  department: DepartmentKey;
  groupTitle: string;
  kpiName: string;
  objectiveIndicator?: string | null;
  quantityQuality?: string | null;
  compliance?: string | null;
  dueCommitment?: string | null;
  ownerTeamLabel?: string | null;
  source?: string;
}): Promise<KpiBoardResponse> {
  const normalizedDepartment = normalizeDepartmentScope(toDepartmentKey(input.department) ?? null);
  if (!normalizedDepartment) {
    throw new Error("Invalid department.");
  }

  const groupTitle = input.groupTitle.trim();
  const kpiName = input.kpiName.trim();
  if (!groupTitle) {
    throw new Error("groupTitle is required.");
  }
  if (!kpiName) {
    throw new Error("kpiName is required.");
  }

  return upsertWeeklyKpiFromPayload({
    weekStart: input.weekStartInput ?? undefined,
    source: input.source?.trim() || "owner-manual-kpi-v1",
    sections: [
      {
        sectionKey: normalizeRowKey(`${normalizedDepartment}-${groupTitle}`) || `${normalizedDepartment}-group`,
        rawSectionLabel: groupTitle,
        mappedDepartment: normalizedDepartment,
        ownerTeamLabel: input.ownerTeamLabel?.trim() || null,
        rows: [
          {
            rowKey: normalizeRowKey(buildRowKey([kpiName])) || "row-1",
            responsibility: kpiName,
            objectiveIndicator: input.objectiveIndicator?.trim() || null,
            quantityQuality: input.quantityQuality?.trim() || null,
            compliance: input.compliance?.trim() || null,
            dueCommitment: input.dueCommitment?.trim() || null,
            sortOrder: 0,
          },
        ],
      },
    ],
  });
}

export function defaultWeekStartForRequest(weekStartInput?: string | null): string {
  const currentMexicoDate = mexicoTodayIso();
  return getWeekBounds(weekStartInput ?? currentMexicoDate).weekStart;
}
