import type { PrismaClient } from "@capakraken/db";

export interface SkillGapRow {
  skill: string;
  demand: number;
  supply: number;
  gap: number;
}

interface SkillEntry {
  name: string;
  level?: number;
}

export interface SkillGapSummaryRoleGap {
  role: string;
  needed: number;
  filled: number;
  gap: number;
  fillRate: number;
}

export interface SkillSupplySummaryRow {
  skill: string;
  resourceCount: number;
}

export interface ResourcesByRoleSummaryRow {
  role: string;
  count: number;
}

export interface DashboardSkillGapSummary {
  roleGaps: SkillGapSummaryRoleGap[];
  totalOpenPositions: number;
  skillSupplyTop10: SkillSupplySummaryRow[];
  resourcesByRole: ResourcesByRoleSummaryRow[];
}

export async function getDashboardSkillGaps(
  db: PrismaClient,
): Promise<SkillGapRow[]> {
  // Count open demand requirements grouped by required skill (from role name)
  const openDemands = await db.demandRequirement.findMany({
    where: {
      status: { in: ["PROPOSED", "CONFIRMED"] },
      project: { status: "ACTIVE" },
    },
    select: {
      role: true,
      roleId: true,
      roleEntity: { select: { name: true } },
      headcount: true,
      metadata: true,
    },
  });

  // Build demand map by skill/role name
  const demandMap = new Map<string, number>();

  for (const d of openDemands) {
    // Try to extract required skills from metadata
    const meta = d.metadata as Record<string, unknown> | null;
    const requiredSkills = Array.isArray(meta?.requiredSkills)
      ? (meta.requiredSkills as string[])
      : [];

    if (requiredSkills.length > 0) {
      for (const skill of requiredSkills) {
        const normalized = skill.trim();
        if (normalized) {
          demandMap.set(normalized, (demandMap.get(normalized) ?? 0) + d.headcount);
        }
      }
    } else {
      // Fall back to role name as the "skill"
      const roleName = d.roleEntity?.name ?? d.role;
      if (roleName) {
        demandMap.set(roleName, (demandMap.get(roleName) ?? 0) + d.headcount);
      }
    }
  }

  if (demandMap.size === 0) return [];

  // Count active resources with each skill at proficiency >= 3
  const resources = await db.resource.findMany({
    where: { isActive: true },
    select: { skills: true },
  });

  const supplyMap = new Map<string, number>();
  for (const r of resources) {
    const skills = (r.skills ?? []) as unknown as SkillEntry[];
    if (!Array.isArray(skills)) continue;
    for (const skill of skills) {
      if (!skill.name) continue;
      if ((skill.level ?? 0) >= 3) {
        supplyMap.set(skill.name, (supplyMap.get(skill.name) ?? 0) + 1);
      }
    }
  }

  // Build gap rows for demanded skills
  const rows: SkillGapRow[] = [];
  for (const [skill, demand] of demandMap) {
    const supply = supplyMap.get(skill) ?? 0;
    rows.push({ skill, demand, supply, gap: supply - demand });
  }

  // Sort by largest shortage first (most negative gap), take top 10
  rows.sort((a, b) => a.gap - b.gap);
  return rows.slice(0, 10);
}

export async function getDashboardSkillGapSummary(
  db: PrismaClient,
): Promise<DashboardSkillGapSummary> {
  const now = new Date();

  const demands = await db.demandRequirement.findMany({
    where: {
      project: { status: { in: ["ACTIVE", "DRAFT"] } },
      status: { not: "CANCELLED" },
      endDate: { gte: now },
    },
    select: {
      role: true,
      headcount: true,
      roleEntity: { select: { name: true } },
      _count: { select: { assignments: true } },
    },
  });

  const demandByRole = new Map<string, { needed: number; filled: number }>();
  for (const demand of demands) {
    const roleName = demand.roleEntity?.name ?? demand.role ?? "Unknown";
    const existing = demandByRole.get(roleName) ?? { needed: 0, filled: 0 };
    existing.needed += demand.headcount;
    existing.filled += Math.min(demand._count.assignments, demand.headcount);
    demandByRole.set(roleName, existing);
  }

  const resources = await db.resource.findMany({
    where: { isActive: true },
    select: {
      skills: true,
      areaRole: { select: { name: true } },
    },
  });

  const skillSupply = new Map<string, number>();
  const supplyByRole = new Map<string, number>();

  for (const resource of resources) {
    const rawSkills = Array.isArray(resource.skills)
      ? resource.skills as Array<Record<string, unknown>>
      : [];
    for (const entry of rawSkills) {
      const skillName = typeof entry.skill === "string"
        ? entry.skill
        : typeof entry.name === "string"
          ? entry.name
          : null;
      if (!skillName) continue;
      skillSupply.set(skillName.toLowerCase(), (skillSupply.get(skillName.toLowerCase()) ?? 0) + 1);
    }

    const roleName = resource.areaRole?.name;
    if (roleName) {
      supplyByRole.set(roleName, (supplyByRole.get(roleName) ?? 0) + 1);
    }
  }

  const roleGaps = [...demandByRole.entries()]
    .map(([role, { needed, filled }]) => ({
      role,
      needed,
      filled,
      gap: needed - filled,
      fillRate: needed > 0 ? Math.round((filled / needed) * 100) : 100,
    }))
    .filter((gap) => gap.gap > 0)
    .sort((left, right) => right.gap - left.gap);

  return {
    roleGaps,
    totalOpenPositions: roleGaps.reduce((sum, gap) => sum + gap.gap, 0),
    skillSupplyTop10: [...skillSupply.entries()]
      .sort((left, right) => right[1] - left[1])
      .slice(0, 10)
      .map(([skill, resourceCount]) => ({ skill, resourceCount })),
    resourcesByRole: [...supplyByRole.entries()]
      .sort((left, right) => right[1] - left[1])
      .map(([role, count]) => ({ role, count })),
  };
}
