import { inArray } from "drizzle-orm";
import { db } from "@/app/db/db";
import { uploadedFiles } from "@/app/db/schema/uploaded_files";
import { getRequiredDocs } from "@/app/lib/measureDocumentRequirements";
import {
  EXPECTED_RETROFIT_ASSESSMENT_DOC_TYPES,
  SURVEY_ALL_DOC_TYPES,
} from "./types";
import type {
  HubspotDeal,
  DocStatus,
  DocStatusMap,
  MeasureDocProgress,
  ApprovalsByDeal,
} from "./types";

type DocEntry = { fileType: string; measureName: string | null };

export async function fetchDocsByDealId(
  deals: HubspotDeal[],
  dealIds: string[],
): Promise<Map<string, DocEntry[]>> {
  const docsByDealId = new Map<string, DocEntry[]>();

  if (dealIds.length === 0) return docsByDealId;

  const phase1Rows = await db
    .select({
      hubsotDealId: uploadedFiles.hubsotDealId,
      fileType: uploadedFiles.fileType,
      measureName: uploadedFiles.measureName,
    })
    .from(uploadedFiles)
    .where(inArray(uploadedFiles.hubsotDealId, dealIds));

  for (const row of phase1Rows) {
    if (!row.hubsotDealId || row.fileType === null) continue;
    if (!docsByDealId.has(row.hubsotDealId)) docsByDealId.set(row.hubsotDealId, []);
    docsByDealId.get(row.hubsotDealId)!.push({ fileType: row.fileType, measureName: row.measureName });
  }

  // Phase 2: UPRN fallback for deals that returned no results in phase 1
  const dealsWithoutDocs = deals.filter((d) => !docsByDealId.has(d.dealId));
  const fallbackUprns = dealsWithoutDocs
    .map((d) => d.uprn)
    .filter((u): u is string => !!u)
    .map((u) => {
      try { return BigInt(u); } catch { return null; }
    })
    .filter((u): u is bigint => u !== null);

  if (fallbackUprns.length > 0) {
    const phase2Rows = await db
      .select({
        uprn: uploadedFiles.uprn,
        fileType: uploadedFiles.fileType,
        measureName: uploadedFiles.measureName,
      })
      .from(uploadedFiles)
      .where(inArray(uploadedFiles.uprn, fallbackUprns));

    const uprnToDealId = new Map<string, string>(
      dealsWithoutDocs
        .filter((d) => d.uprn)
        .map((d) => {
          try { return [String(BigInt(d.uprn!)), d.dealId] as [string, string]; }
          catch { return null; }
        })
        .filter((e): e is [string, string] => e !== null),
    );

    for (const row of phase2Rows) {
      if (row.uprn === null || row.fileType === null) continue;
      const dealId = uprnToDealId.get(String(row.uprn));
      if (!dealId) continue;
      if (!docsByDealId.has(dealId)) docsByDealId.set(dealId, []);
      docsByDealId.get(dealId)!.push({ fileType: row.fileType, measureName: row.measureName });
    }
  }

  return docsByDealId;
}

export function computeDocStatusMap(
  deals: HubspotDeal[],
  docsByDealId: Map<string, Array<{ fileType: string; measureName: string | null }>>,
  approvalsByDeal: ApprovalsByDeal,
): DocStatusMap {
  const measuresByDealId = new Map<string, string[]>();
  for (const deal of deals) {
    const approved = approvalsByDeal[deal.dealId] ?? [];
    const measures =
      approved.length > 0
        ? approved
        : (deal.proposedMeasures ?? "")
            .split(/[,;]/)
            .map((m) => m.trim())
            .filter(Boolean);
    measuresByDealId.set(deal.dealId, measures);
  }

  const docStatusMap: DocStatusMap = {};

  for (const [dealId, docs] of docsByDealId) {
    const surveyDocs = docs.filter((d) => SURVEY_ALL_DOC_TYPES.has(d.fileType));
    const installDocs = docs.filter((d) => !SURVEY_ALL_DOC_TYPES.has(d.fileType));
    const surveyTypeSet = new Set(surveyDocs.map((d) => d.fileType));

    const measures = measuresByDealId.get(dealId) ?? [];

    const measureProgress: MeasureDocProgress[] = measures.map((measureName) => {
      const required = getRequiredDocs(measureName);
      const docsForMeasure = installDocs.filter((d) => d.measureName === measureName);
      const uploadedTypeSet = new Set(docsForMeasure.map((d) => d.fileType));
      const uploaded = required.filter((r) => uploadedTypeSet.has(r));
      return {
        measureName,
        required,
        uploaded,
        isComplete: uploaded.length === required.length,
        uploadedCount: uploaded.length,
        requiredCount: required.length,
      };
    });

    let installStatus: DocStatus["installStatus"] = "none";
    if (installDocs.length > 0) {
      if (measures.length === 0) {
        installStatus = "hasDocs";
      } else {
        installStatus = measureProgress.every((m) => m.isComplete)
          ? "all"
          : measureProgress.some((m) => m.uploadedCount > 0)
            ? "partial"
            : "none";
      }
    }

    docStatusMap[dealId] = {
      presentSurveyTypes: Array.from(surveyTypeSet),
      hasSurveyDocs: surveyDocs.length > 0,
      isSurveyComplete: EXPECTED_RETROFIT_ASSESSMENT_DOC_TYPES.every((t) =>
        surveyTypeSet.has(t),
      ),
      hasInstallDocs: installDocs.length > 0,
      installStatus,
      measureProgress,
    };
  }

  return docStatusMap;
}
