import {
  STRATEGIC_DEFAULT_DATA,
  STRATEGIC_SECTION_KEYS,
  TEAM_AVAILABILITY_OPTIONS,
  type StrategicDiagnosticData,
  type StrategicDiagnosticScores,
  type StrategicSectionKey,
} from "@/lib/strategic-diagnostic/types";

function clamp(min: number, value: number, max: number) {
  return Math.max(min, Math.min(max, value));
}

function toScore(value: number) {
  return Math.round(clamp(0, value, 100) * 10) / 10;
}

function normalizeString(value: unknown) {
  return typeof value === "string" ? value.trim() : "";
}

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

  const seen = new Set<string>();
  const normalized: string[] = [];

  for (const item of value) {
    const cleaned = normalizeString(item);

    if (!cleaned) {
      continue;
    }

    const lower = cleaned.toLowerCase();

    if (!seen.has(lower)) {
      seen.add(lower);
      normalized.push(cleaned);
    }
  }

  return normalized;
}

function normalizeBoolean(value: unknown) {
  return value === true;
}

function normalizeLevel(value: unknown) {
  const numeric = typeof value === "number" ? value : Number.parseInt(String(value), 10);
  return Number.isNaN(numeric) ? 1 : clamp(1, numeric, 5);
}

function normalizeSectionKey<T extends Record<string, boolean>>(source: unknown, fallback: T) {
  if (!source || typeof source !== "object" || Array.isArray(source)) {
    return fallback;
  }

  const record = source as Record<string, unknown>;
  const result = { ...fallback };

  for (const key of Object.keys(fallback)) {
    (result as Record<string, boolean>)[key] = normalizeBoolean(record[key]);
  }

  return result;
}

export function sanitizeStrategicDiagnosticData(rawValue: unknown): StrategicDiagnosticData {
  if (!rawValue || typeof rawValue !== "object" || Array.isArray(rawValue)) {
    return STRATEGIC_DEFAULT_DATA;
  }

  const source = rawValue as Record<string, unknown>;
  const technicalSource = source.technical as Record<string, unknown> | undefined;
  const experienceSource = source.experience as Record<string, unknown> | undefined;
  const organizationSource = source.organization as Record<string, unknown> | undefined;
  const procurementSource = source.publicProcurement as Record<string, unknown> | undefined;

  return {
    technical: {
      productsServicesDescription: normalizeString(technicalSource?.productsServicesDescription),
      installedCapacityEmployees: normalizeString(technicalSource?.installedCapacityEmployees),
      equipment: normalizeString(technicalSource?.equipment),
      infrastructure: normalizeString(technicalSource?.infrastructure),
      coverageRegions: normalizeArray(technicalSource?.coverageRegions),
      responseTimes: normalizeString(technicalSource?.responseTimes),
      certifications: normalizeArray(technicalSource?.certifications),
    },
    experience: {
      hasGovernmentSectorExperience: normalizeBoolean(experienceSource?.hasGovernmentSectorExperience),
      hasPrivateSectorExperience: normalizeBoolean(experienceSource?.hasPrivateSectorExperience),
      sectorsServed: normalizeArray(experienceSource?.sectorsServed),
      contractTypes: normalizeArray(experienceSource?.contractTypes),
      approximateContractAmounts: normalizeString(experienceSource?.approximateContractAmounts),
      successCases: normalizeString(experienceSource?.successCases),
    },
    organization: {
      structureType: normalizeString(organizationSource?.structureType),
      roles: normalizeSectionKey(organizationSource?.roles, STRATEGIC_DEFAULT_DATA.organization.roles),
      compliance: normalizeSectionKey(organizationSource?.compliance, STRATEGIC_DEFAULT_DATA.organization.compliance),
    },
    publicProcurement: {
      processKnowledgeLevel: normalizeLevel(procurementSource?.processKnowledgeLevel),
      hasTenderBasesExperience: normalizeBoolean(procurementSource?.hasTenderBasesExperience),
      technicalProposalCapacityLevel: normalizeLevel(procurementSource?.technicalProposalCapacityLevel),
      administrativeRequirementsCapacityLevel: normalizeLevel(procurementSource?.administrativeRequirementsCapacityLevel),
      hasBiddingTeam: normalizeBoolean(procurementSource?.hasBiddingTeam),
      biddingTeamAvailability: TEAM_AVAILABILITY_OPTIONS.includes(normalizeString(procurementSource?.biddingTeamAvailability))
        ? normalizeString(procurementSource?.biddingTeamAvailability)
        : TEAM_AVAILABILITY_OPTIONS[0],
    },
  };
}

function countTruthy(values: boolean[]) {
  return values.reduce((sum, value) => sum + (value ? 1 : 0), 0);
}

function scoreByRatio(current: number, target: number) {
  if (target <= 0) {
    return 0;
  }

  return clamp(0, current / target, 1);
}

function getTeamAvailabilityScore(value: string) {
  switch (value) {
    case "Equipo parcial (segun proyecto)":
      return 35;
    case "Equipo dedicado medio tiempo":
      return 70;
    case "Equipo dedicado tiempo completo":
      return 100;
    default:
      return 0;
  }
}

export function computeStrategicDiagnosticScores(
  data: StrategicDiagnosticData,
  evidenceCountBySection: Record<StrategicSectionKey, number>,
): StrategicDiagnosticScores {
  const technicalScore = toScore(
    (data.technical.productsServicesDescription ? 20 : 0) +
      (data.technical.installedCapacityEmployees ? 10 : 0) +
      (data.technical.equipment ? 10 : 0) +
      (data.technical.infrastructure ? 10 : 0) +
      scoreByRatio(data.technical.coverageRegions.length, 4) * 15 +
      (data.technical.responseTimes ? 15 : 0) +
      scoreByRatio(data.technical.certifications.length, 4) * 10 +
      scoreByRatio(evidenceCountBySection.technical ?? 0, 3) * 10
  );

  const experienceScore = toScore(
    (data.experience.hasGovernmentSectorExperience ? 10 : 0) +
      (data.experience.hasPrivateSectorExperience ? 10 : 0) +
      scoreByRatio(data.experience.sectorsServed.length, 4) * 15 +
      scoreByRatio(data.experience.contractTypes.length, 4) * 20 +
      (data.experience.approximateContractAmounts ? 15 : 0) +
      (data.experience.successCases ? 20 : 0) +
      scoreByRatio(evidenceCountBySection.experience ?? 0, 3) * 10
  );

  const rolesCount = countTruthy(Object.values(data.organization.roles));
  const complianceCount = countTruthy(Object.values(data.organization.compliance));

  const organizationScore = toScore(
    (data.organization.structureType ? 20 : 0) + scoreByRatio(rolesCount, 4) * 40 + scoreByRatio(complianceCount, 3) * 40
  );

  const publicProcurementScore = toScore(
    ((data.publicProcurement.processKnowledgeLevel - 1) / 4) * 15 +
      (data.publicProcurement.hasTenderBasesExperience ? 10 : 0) +
      ((data.publicProcurement.technicalProposalCapacityLevel - 1) / 4) * 20 +
      ((data.publicProcurement.administrativeRequirementsCapacityLevel - 1) / 4) * 20 +
      (data.publicProcurement.hasBiddingTeam ? 15 : 0) +
      (getTeamAvailabilityScore(data.publicProcurement.biddingTeamAvailability) / 100) * 10 +
      scoreByRatio(evidenceCountBySection.publicProcurement ?? 0, 3) * 10
  );

  const sectionScores: Record<StrategicSectionKey, number> = {
    technical: technicalScore,
    experience: experienceScore,
    organization: organizationScore,
    publicProcurement: publicProcurementScore,
  };

  const sectionCompletion: Record<StrategicSectionKey, boolean> = {
    technical: Boolean(
      data.technical.productsServicesDescription &&
        data.technical.installedCapacityEmployees &&
        data.technical.equipment &&
        data.technical.infrastructure &&
        data.technical.responseTimes
    ),
    experience: Boolean(
      (data.experience.hasGovernmentSectorExperience || data.experience.hasPrivateSectorExperience) &&
        data.experience.contractTypes.length > 0 &&
        data.experience.approximateContractAmounts &&
        data.experience.successCases
    ),
    organization: Boolean(data.organization.structureType && rolesCount > 0 && complianceCount > 0),
    publicProcurement: Boolean(
      data.publicProcurement.processKnowledgeLevel >= 1 &&
        data.publicProcurement.technicalProposalCapacityLevel >= 1 &&
        data.publicProcurement.administrativeRequirementsCapacityLevel >= 1 &&
        data.publicProcurement.biddingTeamAvailability
    ),
  };

  const completedSections = STRATEGIC_SECTION_KEYS.reduce((total, key) => total + (sectionCompletion[key] ? 1 : 0), 0);
  const totalSections = STRATEGIC_SECTION_KEYS.length;

  const overallScore = toScore(
    technicalScore * 0.3 + experienceScore * 0.25 + organizationScore * 0.2 + publicProcurementScore * 0.25
  );

  const labelBySection: Record<StrategicSectionKey, string> = {
    technical: "Capacidades tecnicas",
    experience: "Experiencia",
    organization: "Organizacion",
    publicProcurement: "Contratacion publica",
  };

  const strengths = STRATEGIC_SECTION_KEYS.filter((key) => sectionScores[key] >= 75).map((key) => labelBySection[key]);
  const gaps = STRATEGIC_SECTION_KEYS.filter((key) => sectionScores[key] < 75).map((key) => labelBySection[key]);

  const risks: string[] = [];

  if (!data.organization.compliance.fiscal || !data.organization.compliance.labor) {
    risks.push("Cumplimiento normativo incompleto (fiscal/laboral).");
  }

  if (!data.publicProcurement.hasBiddingTeam) {
    risks.push("No hay equipo de licitaciones dedicado.");
  }

  if (data.publicProcurement.processKnowledgeLevel <= 2) {
    risks.push("Conocimiento bajo del proceso de licitacion.");
  }

  if (!data.experience.hasGovernmentSectorExperience) {
    risks.push("Experiencia limitada en sector gobierno.");
  }

  return {
    sectionScores,
    sectionCompletion,
    completedSections,
    totalSections,
    overallScore,
    strengths,
    gaps,
    risks: risks.slice(0, 3),
  };
}
