import "server-only";

import type { Prisma } from "@prisma/client";
import { prisma } from "@/lib/prisma";
import { getM7DatasetForUser } from "@/lib/compliance/server";
import { scoreAuditResponses } from "@/lib/audits/scoring";
import type {
  AuditKpiSnapshot,
  AuditSimulationCreateInput,
  AuditSimulationScoreInput,
  AuditSimulationView,
  InstitutionalDossierFreshness,
  InstitutionalDossierLoadStrategy,
  InstitutionalDossierPayload,
} from "@/lib/audits/types";

function parseStringArray(value: unknown) {
  if (!Array.isArray(value)) {
    return [];
  }

  return value.filter((item): item is string => typeof item === "string" && item.trim().length > 0);
}

const DEFAULT_DOSSIER_STALE_MINUTES = 180;

function resolveDossierStaleMinutes() {
  const raw = process.env.DOSSIER_STALE_MINUTES?.trim();

  if (!raw) {
    return DEFAULT_DOSSIER_STALE_MINUTES;
  }

  const parsed = Number.parseInt(raw, 10);
  if (!Number.isFinite(parsed) || parsed <= 0) {
    return DEFAULT_DOSSIER_STALE_MINUTES;
  }

  return parsed;
}

export const INSTITUTIONAL_DOSSIER_STALE_MINUTES = resolveDossierStaleMinutes();

function computeDossierFreshness(args: {
  generatedAt: string;
  source: "live" | "snapshot";
  strategy: InstitutionalDossierLoadStrategy;
  snapshotId: string | null;
}) {
  const generatedMs = new Date(args.generatedAt).getTime();
  const thresholdMs = INSTITUTIONAL_DOSSIER_STALE_MINUTES * 60 * 1000;
  const isStale = !Number.isFinite(generatedMs) || Date.now() - generatedMs > thresholdMs;

  return {
    generatedAt: args.generatedAt,
    isStale,
    source: args.source,
    strategy: args.strategy,
    snapshotId: args.snapshotId,
    staleAfterMinutes: INSTITUTIONAL_DOSSIER_STALE_MINUTES,
  } satisfies InstitutionalDossierFreshness;
}

function syncPayloadGeneratedAt(payload: InstitutionalDossierPayload, generatedAt: string): InstitutionalDossierPayload {
  return {
    ...payload,
    generatedAt,
  };
}

function mapSimulation(row: {
  id: string;
  name: string;
  auditType: string;
  status: "DRAFT" | "COMPLETED";
  overallScore: number | null;
  completedAt: Date | null;
  createdAt: Date;
  updatedAt: Date;
  sections: Array<{
    id: string;
    simulationId: string;
    key: string;
    score: number | null;
    status: "READY" | "WARNING" | "CRITICAL";
    findingsJson: Prisma.JsonValue | null;
    recommendationsJson: Prisma.JsonValue | null;
  }>;
  responses: Array<{
    id: string;
    simulationId: string;
    questionKey: string;
    answer: string;
    evidenceRefsJson: Prisma.JsonValue | null;
  }>;
}): AuditSimulationView {
  return {
    id: row.id,
    name: row.name,
    auditType: row.auditType,
    status: row.status,
    overallScore: row.overallScore,
    completedAt: row.completedAt ? row.completedAt.toISOString() : null,
    createdAt: row.createdAt.toISOString(),
    updatedAt: row.updatedAt.toISOString(),
    sections: row.sections.map((section) => ({
      id: section.id,
      simulationId: section.simulationId,
      key: section.key as AuditSimulationView["sections"][number]["key"],
      score: section.score,
      status: section.status,
      findings: parseStringArray(section.findingsJson),
      recommendations: parseStringArray(section.recommendationsJson),
    })),
    responses: row.responses.map((response) => ({
      id: response.id,
      simulationId: response.simulationId,
      questionKey: response.questionKey,
      answer: response.answer,
      evidenceRefs: parseStringArray(response.evidenceRefsJson),
    })),
  };
}

export async function listAuditSimulationsForUser(userId: string) {
  const rows = await prisma.auditSimulation.findMany({
    where: { userId },
    include: {
      sections: {
        orderBy: [{ key: "asc" }],
      },
      responses: {
        orderBy: [{ createdAt: "desc" }],
      },
    },
    orderBy: [{ updatedAt: "desc" }],
    take: 100,
  });

  return rows.map((row) => mapSimulation(row));
}

export async function createAuditSimulationForUser(userId: string, input: AuditSimulationCreateInput) {
  const created = await prisma.auditSimulation.create({
    data: {
      userId,
      name: input.name,
      auditType: input.auditType,
      status: "DRAFT",
    },
    include: {
      sections: true,
      responses: true,
    },
  });

  return mapSimulation(created);
}

export async function scoreAuditSimulationForUser(userId: string, simulationId: string, input: AuditSimulationScoreInput) {
  const owned = await prisma.auditSimulation.findFirst({
    where: {
      id: simulationId,
      userId,
    },
    select: { id: true },
  });

  if (!owned) {
    return null;
  }

  const scoring = scoreAuditResponses(input.responses);

  await prisma.$transaction([
    prisma.auditChecklistResponse.deleteMany({
      where: {
        simulationId,
      },
    }),
    prisma.auditChecklistResponse.createMany({
      data: input.responses.map((item) => ({
        simulationId,
        questionKey: item.questionKey,
        answer: item.answer,
        evidenceRefsJson: (item.evidenceRefs ?? []) as Prisma.InputJsonValue,
      })),
    }),
    ...scoring.sections.map((section) =>
      prisma.auditSimulationSection.upsert({
        where: {
          simulationId_key: {
            simulationId,
            key: section.key,
          },
        },
        update: {
          score: section.score,
          status: section.status,
          findingsJson: section.findings as Prisma.InputJsonValue,
          recommendationsJson: section.recommendations as Prisma.InputJsonValue,
        },
        create: {
          simulationId,
          key: section.key,
          score: section.score,
          status: section.status,
          findingsJson: section.findings as Prisma.InputJsonValue,
          recommendationsJson: section.recommendations as Prisma.InputJsonValue,
        },
      }),
    ),
    prisma.auditSimulation.update({
      where: { id: simulationId },
      data: {
        overallScore: scoring.overallScore,
        status: "COMPLETED",
        completedAt: new Date(),
      },
    }),
  ]);

  const updated = await prisma.auditSimulation.findUnique({
    where: { id: simulationId },
    include: {
      sections: {
        orderBy: [{ key: "asc" }],
      },
      responses: {
        orderBy: [{ createdAt: "desc" }],
      },
    },
  });

  return updated ? mapSimulation(updated) : null;
}

export async function getAuditSimulationDetail(userId: string, simulationId: string) {
  const row = await prisma.auditSimulation.findFirst({
    where: {
      id: simulationId,
      userId,
    },
    include: {
      sections: {
        orderBy: [{ key: "asc" }],
      },
      responses: {
        orderBy: [{ createdAt: "desc" }],
      },
    },
  });

  return row ? mapSimulation(row) : null;
}

export async function getAuditKpisForUser(userId: string): Promise<AuditKpiSnapshot> {
  const [completedAudits, latestCompleted, contractsCount, contractPdfs, proposalPdfs] = await Promise.all([
    prisma.auditSimulation.count({
      where: {
        userId,
        status: "COMPLETED",
      },
    }),
    prisma.auditSimulation.findFirst({
      where: {
        userId,
        status: "COMPLETED",
      },
      orderBy: [{ completedAt: "desc" }],
      select: {
        overallScore: true,
      },
    }),
    prisma.contractRecord.count({
      where: {
        userId,
      },
    }),
    prisma.contractDocument.count({
      where: {
        contract: {
          userId,
        },
      },
    }),
    prisma.proposalDocument.count({
      where: {
        userId,
      },
    }),
  ]);

  return {
    completedAudits,
    lastScore: latestCompleted?.overallScore ?? null,
    registeredContracts: contractsCount,
    securedPdfs: contractPdfs + proposalPdfs,
  };
}

export async function buildInstitutionalDossier(userId: string): Promise<InstitutionalDossierPayload> {
  const [proposals, contracts, legalCases, m7] = await Promise.all([
    prisma.proposal.findMany({
      where: { userId },
      include: {
        documents: {
          orderBy: [{ createdAt: "desc" }],
          select: {
            id: true,
            fileName: true,
            mimeType: true,
            sizeBytes: true,
            createdAt: true,
          },
        },
      },
      orderBy: [{ updatedAt: "desc" }],
      take: 200,
    }),
    prisma.contractRecord.findMany({
      where: { userId },
      include: {
        sourceProposal: {
          select: {
            id: true,
            title: true,
          },
        },
        documents: {
          orderBy: [{ createdAt: "desc" }],
          select: {
            id: true,
            fileName: true,
            mimeType: true,
            sizeBytes: true,
            kind: true,
            createdAt: true,
          },
        },
        deliverables: {
          select: { id: true },
        },
        payments: {
          select: { id: true },
        },
      },
      orderBy: [{ updatedAt: "desc" }],
      take: 200,
    }),
    prisma.legalCase.findMany({
      where: { userId },
      include: {
        documents: {
          orderBy: [{ createdAt: "desc" }],
          select: {
            id: true,
            title: true,
            templateKey: true,
            createdAt: true,
          },
        },
      },
      orderBy: [{ updatedAt: "desc" }],
      take: 200,
    }),
    getM7DatasetForUser(userId),
  ]);

  const proposalDocuments = proposals.reduce((sum, item) => sum + item.documents.length, 0);
  const contractDocuments = contracts.reduce((sum, item) => sum + item.documents.length, 0);
  const legalDocuments = legalCases.reduce((sum, item) => sum + item.documents.length, 0);
  const highRiskAlerts = m7.tabs.alertas.filter((item) => item.severity === "high").length;

  const components = [
    {
      key: "m5" as const,
      label: "M5 Propuestas",
      totalItems: proposals.length,
      withEvidence: proposals.filter((item) => item.documents.length > 0).length,
    },
    {
      key: "m8" as const,
      label: "M8 Contratos",
      totalItems: contracts.length,
      withEvidence: contracts.filter((item) => item.documents.length > 0).length,
    },
    {
      key: "m9" as const,
      label: "M9 Casos Legales",
      totalItems: legalCases.length,
      withEvidence: legalCases.filter((item) => item.documents.length > 0).length,
    },
    {
      key: "m7" as const,
      label: "M7 Alertas",
      totalItems: m7.tabs.alertas.length,
      withEvidence: Math.max(0, m7.tabs.alertas.length - highRiskAlerts),
    },
  ].map((component) => ({
    ...component,
    status: component.totalItems > 0 && component.withEvidence >= component.totalItems ? "completo" as const : "incompleto" as const,
  }));

  return {
    generatedAt: new Date().toISOString(),
    summary: {
      contracts: contracts.length,
      contractDocuments,
      proposals: proposals.length,
      proposalDocuments,
      legalCases: legalCases.length,
      legalDocuments,
      highRiskAlerts,
    },
    components,
    contracts: contracts.map((item) => ({
      id: item.id,
      title: item.title,
      status: item.status,
      deliverables: item.deliverables.length,
      payments: item.payments.length,
      documents: item.documents.length,
      links: [
        {
          label: "Abrir contrato en M8",
          href: `/gestion-contratos?contractId=${encodeURIComponent(item.id)}`,
        },
        {
          label: "Abrir continuidad legal en M9",
          href: `/proteccion-legal?contractId=${encodeURIComponent(item.id)}`,
        },
        ...(item.sourceProposal
          ? [
              {
                label: "Ver propuesta fuente en M5",
                href: `/gestion-licitaciones/${encodeURIComponent(item.sourceProposal.id)}`,
              },
            ]
          : []),
      ],
      documentEntries: item.documents.map((document) => ({
        id: document.id,
        fileName: document.fileName,
        kind: document.kind,
        mimeType: document.mimeType,
        sizeBytes: document.sizeBytes,
        createdAt: document.createdAt.toISOString(),
        traceLabel: item.sourceProposal
          ? `Evidencia contractual de M8 ligada a propuesta M5: ${item.sourceProposal.title}`
          : "Evidencia contractual cargada en M8.",
        links: [
          {
            label: "Ver contexto en M8",
            href: `/gestion-contratos?contractId=${encodeURIComponent(item.id)}&documentId=${encodeURIComponent(document.id)}`,
          },
          {
            label: "Descargar evidencia",
            href: `/api/contracts/${encodeURIComponent(item.id)}/documents/${encodeURIComponent(document.id)}`,
          },
          ...(item.sourceProposal
            ? [
                {
                  label: "Rastrear origen en M5",
                  href: `/gestion-licitaciones/${encodeURIComponent(item.sourceProposal.id)}`,
                },
              ]
            : []),
        ],
      })),
    })),
    proposals: proposals.map((item) => ({
      id: item.id,
      title: item.title,
      status: item.status,
      documents: item.documents.length,
      links: [
        {
          label: "Abrir propuesta en M5",
          href: `/gestion-licitaciones/${encodeURIComponent(item.id)}`,
        },
        ...(item.sourceLicitationId
          ? [
              {
                label: "Ver licitacion origen",
                href: `/licitations/${encodeURIComponent(item.sourceLicitationId)}`,
              },
            ]
          : []),
      ],
      documentEntries: item.documents.map((document) => ({
        id: document.id,
        fileName: document.fileName,
        kind: null,
        mimeType: document.mimeType,
        sizeBytes: document.sizeBytes,
        createdAt: document.createdAt.toISOString(),
        traceLabel: item.sourceLicitationId
          ? `Documento de propuesta M5 vinculado a licitacion ${item.sourceLicitationId}.`
          : "Documento de propuesta cargado en M5.",
        links: [
          {
            label: "Ver contexto en M5",
            href: `/gestion-licitaciones/${encodeURIComponent(item.id)}?documentId=${encodeURIComponent(document.id)}`,
          },
          {
            label: "Descargar evidencia",
            href: `/api/proposals/${encodeURIComponent(item.id)}/documents/${encodeURIComponent(document.id)}`,
          },
        ],
      })),
    })),
    legal: legalCases.map((item) => ({
      id: item.id,
      caseType: item.caseType,
      severity: item.severity,
      status: item.status,
      documents: item.documents.length,
      links: [
        {
          label: "Abrir caso en M9",
          href: item.contractId
            ? `/proteccion-legal?contractId=${encodeURIComponent(item.contractId)}`
            : "/proteccion-legal",
        },
        ...(item.contractId
          ? [
              {
                label: "Ver contrato asociado en M8",
                href: `/gestion-contratos?contractId=${encodeURIComponent(item.contractId)}`,
              },
            ]
          : []),
      ],
      documentEntries: item.documents.map((document) => ({
        id: document.id,
        fileName: document.title,
        kind: document.templateKey,
        mimeType: "text/plain",
        sizeBytes: 0,
        createdAt: document.createdAt.toISOString(),
        traceLabel: item.contractId
          ? `Escrito legal de M9 vinculado al contrato ${item.contractId}.`
          : "Escrito legal generado en M9.",
        links: [
          {
            label: "Ver contexto en M9",
            href: item.contractId
              ? `/proteccion-legal?contractId=${encodeURIComponent(item.contractId)}`
              : "/proteccion-legal",
          },
        ],
      })),
    })),
  };
}

export async function refreshInstitutionalDossierSnapshot(userId: string) {
  const payload = await buildInstitutionalDossier(userId);

  const snapshot = await prisma.institutionalDossierSnapshot.create({
    data: {
      userId,
      payloadJson: payload as Prisma.InputJsonValue,
    },
  });

  const generatedAt = snapshot.generatedAt.toISOString();
  const syncedPayload = syncPayloadGeneratedAt(payload, generatedAt);

  return {
    id: snapshot.id,
    generatedAt,
    payload: syncedPayload,
    freshness: computeDossierFreshness({
      generatedAt,
      source: "snapshot",
      strategy: "snapshot",
      snapshotId: snapshot.id,
    }),
  };
}

export async function getLatestInstitutionalDossierSnapshot(userId: string) {
  const latest = await prisma.institutionalDossierSnapshot.findFirst({
    where: {
      userId,
    },
    orderBy: [{ generatedAt: "desc" }],
    select: {
      id: true,
      generatedAt: true,
      payloadJson: true,
    },
  });

  if (!latest) {
    return null;
  }

  const generatedAt = latest.generatedAt.toISOString();
  const payload = syncPayloadGeneratedAt(latest.payloadJson as InstitutionalDossierPayload, generatedAt);

  return {
    id: latest.id,
    generatedAt,
    payload,
  };
}

export async function getInstitutionalDossierForUser(userId: string, strategy: InstitutionalDossierLoadStrategy = "live") {
  if (strategy === "snapshot") {
    const latest = await getLatestInstitutionalDossierSnapshot(userId);

    if (latest) {
      return {
        dossier: latest.payload,
        freshness: computeDossierFreshness({
          generatedAt: latest.generatedAt,
          source: "snapshot",
          strategy,
          snapshotId: latest.id,
        }),
      };
    }
  }

  const live = await buildInstitutionalDossier(userId);
  const generatedAt = live.generatedAt;

  return {
    dossier: live,
    freshness: computeDossierFreshness({
      generatedAt,
      source: "live",
      strategy,
      snapshotId: null,
    }),
  };
}
