import "server-only";

import { StrategicDiagnosticEvidenceSection } from "@prisma/client";
import { prisma } from "@/lib/prisma";
import { computeStrategicDiagnosticScores, sanitizeStrategicDiagnosticData } from "@/lib/strategic-diagnostic/scoring";
import {
  STRATEGIC_SECTION_KEYS,
  type StrategicDiagnosticData,
  type StrategicDiagnosticSnapshot,
  type StrategicEvidenceDocumentView,
  type StrategicSectionKey,
} from "@/lib/strategic-diagnostic/types";

function mapSectionEnumToKey(section: StrategicDiagnosticEvidenceSection): StrategicSectionKey {
  if (section === StrategicDiagnosticEvidenceSection.TECHNICAL) {
    return "technical";
  }

  if (section === StrategicDiagnosticEvidenceSection.EXPERIENCE) {
    return "experience";
  }

  if (section === StrategicDiagnosticEvidenceSection.ORGANIZATION) {
    return "organization";
  }

  return "publicProcurement";
}

export function mapSectionKeyToEnum(section: StrategicSectionKey): StrategicDiagnosticEvidenceSection {
  if (section === "technical") {
    return StrategicDiagnosticEvidenceSection.TECHNICAL;
  }

  if (section === "experience") {
    return StrategicDiagnosticEvidenceSection.EXPERIENCE;
  }

  if (section === "organization") {
    return StrategicDiagnosticEvidenceSection.ORGANIZATION;
  }

  return StrategicDiagnosticEvidenceSection.PUBLIC_PROCUREMENT;
}

function createEvidenceAccumulator() {
  return {
    technical: [],
    experience: [],
    organization: [],
    publicProcurement: [],
  } as Record<StrategicSectionKey, StrategicEvidenceDocumentView[]>;
}

function createEvidenceCountAccumulator() {
  return {
    technical: 0,
    experience: 0,
    organization: 0,
    publicProcurement: 0,
  } as Record<StrategicSectionKey, number>;
}

async function getOrganizationRecord(userId: string) {
  return prisma.organization.findUnique({
    where: { userId },
    select: {
      id: true,
      strategicDiagnosticData: true,
      strategicDiagnosticCompletedAt: true,
      strategicDiagnosticEvidenceDocs: {
        orderBy: [{ createdAt: "desc" }],
        select: {
          id: true,
          section: true,
          category: true,
          fileName: true,
          filePath: true,
          mimeType: true,
          sizeBytes: true,
          createdAt: true,
        },
      },
    },
  });
}

function getEvidenceBySection(
  rows: {
    id: string;
    section: StrategicDiagnosticEvidenceSection;
    category: string;
    fileName: string;
    filePath: string;
    mimeType: string;
    sizeBytes: number;
    createdAt: Date;
  }[],
) {
  const evidenceBySection = createEvidenceAccumulator();
  const evidenceCountBySection = createEvidenceCountAccumulator();

  for (const row of rows) {
    const sectionKey = mapSectionEnumToKey(row.section);

    evidenceBySection[sectionKey].push({
      id: row.id,
      section: sectionKey,
      category: row.category,
      fileName: row.fileName,
      filePath: row.filePath,
      mimeType: row.mimeType,
      sizeBytes: row.sizeBytes,
      createdAt: row.createdAt.toISOString(),
    });

    evidenceCountBySection[sectionKey] += 1;
  }

  return {
    evidenceBySection,
    evidenceCountBySection,
  };
}

export async function getStrategicDiagnosticSnapshot(userId: string): Promise<StrategicDiagnosticSnapshot | null> {
  const organization = await getOrganizationRecord(userId);

  if (!organization) {
    return null;
  }

  const data = sanitizeStrategicDiagnosticData(organization.strategicDiagnosticData);
  const { evidenceBySection, evidenceCountBySection } = getEvidenceBySection(organization.strategicDiagnosticEvidenceDocs);
  const scores = computeStrategicDiagnosticScores(data, evidenceCountBySection);

  return {
    organizationId: organization.id,
    data,
    scores,
    evidenceBySection,
    completedAt: organization.strategicDiagnosticCompletedAt?.toISOString() ?? null,
  };
}

export async function saveStrategicDiagnosticData(
  userId: string,
  rawData: unknown,
  options?: { forceCompleted?: boolean },
): Promise<StrategicDiagnosticSnapshot | null> {
  const organization = await getOrganizationRecord(userId);

  if (!organization) {
    return null;
  }

  const data = sanitizeStrategicDiagnosticData(rawData);
  const { evidenceBySection, evidenceCountBySection } = getEvidenceBySection(organization.strategicDiagnosticEvidenceDocs);
  const scores = computeStrategicDiagnosticScores(data, evidenceCountBySection);
  const isCompleted = options?.forceCompleted || scores.completedSections === STRATEGIC_SECTION_KEYS.length;

  await prisma.organization.update({
    where: { id: organization.id },
    data: {
      strategicDiagnosticData: data,
      strategicDiagnosticScores: scores,
      strategicDiagnosticCompletedAt: isCompleted ? new Date() : null,
    },
  });

  return {
    organizationId: organization.id,
    data,
    scores,
    evidenceBySection,
    completedAt: isCompleted ? new Date().toISOString() : null,
  };
}

export async function recomputeStrategicDiagnosticFromStoredData(userId: string, forceCompleted?: boolean) {
  const organization = await getOrganizationRecord(userId);

  if (!organization) {
    return null;
  }

  return saveStrategicDiagnosticData(userId, organization.strategicDiagnosticData, { forceCompleted });
}

export function extractStrategicDataOrDefault(value: unknown): StrategicDiagnosticData {
  return sanitizeStrategicDiagnosticData(value);
}
