import { AppDataSource } from "../database/data-source";
import { PosMaster } from "../entities/PosMaster";

// Type definitions for position counts
export interface PositionCountData {
  totalCount: number;
  currentVacantCount: number;
  nextVacantCount: number;
}

export interface PositionCountsByNode {
  orgRootMap: Map<string, PositionCountData>;
  orgChild1Map: Map<string, PositionCountData>;
  orgChild2Map: Map<string, PositionCountData>;
  orgChild3Map: Map<string, PositionCountData>;
  orgChild4Map: Map<string, PositionCountData>;
}

// Optimized function using SQL aggregation with GROUP BY
// This is much more efficient than loading all records into memory
export async function getPositionCountsAggregated(orgRevisionId: string): Promise<PositionCountsByNode> {
  const posRepo = AppDataSource.getRepository(PosMaster);

  // Helper to build map from query results
  const buildMap = (results: any[]) => {
    const map = new Map<string, PositionCountData>();
    for (const row of results) {
      if (row.nodeId) {
        map.set(row.nodeId, {
          totalCount: parseInt(row.totalCount) || 0,
          currentVacantCount: parseInt(row.currentVacantCount) || 0,
          nextVacantCount: parseInt(row.nextVacantCount) || 0,
        });
      }
    }
    return map;
  };

  // Execute all aggregation queries in parallel
  const [
    orgRootResults,
    orgChild1Results,
    orgChild2Results,
    orgChild3Results,
    orgChild4Results,
  ] = await Promise.all([
    // Level 0: orgRoot
    posRepo
      .createQueryBuilder("pos")
      .select("pos.orgRootId", "nodeId")
      .addSelect("COUNT(*)", "totalCount")
      .addSelect("SUM(CASE WHEN pos.current_holderId IS NULL OR pos.current_holderId = '' THEN 1 ELSE 0 END)", "currentVacantCount")
      .addSelect("SUM(CASE WHEN pos.next_holderId IS NULL OR pos.next_holderId = '' THEN 1 ELSE 0 END)", "nextVacantCount")
      .where("pos.orgRevisionId = :orgRevisionId", { orgRevisionId })
      .andWhere("pos.orgRootId IS NOT NULL")
      .groupBy("pos.orgRootId")
      .getRawMany(),

    // Level 1: orgChild1
    posRepo
      .createQueryBuilder("pos")
      .select("pos.orgChild1Id", "nodeId")
      .addSelect("COUNT(*)", "totalCount")
      .addSelect("SUM(CASE WHEN pos.current_holderId IS NULL OR pos.current_holderId = '' THEN 1 ELSE 0 END)", "currentVacantCount")
      .addSelect("SUM(CASE WHEN pos.next_holderId IS NULL OR pos.next_holderId = '' THEN 1 ELSE 0 END)", "nextVacantCount")
      .where("pos.orgRevisionId = :orgRevisionId", { orgRevisionId })
      .andWhere("pos.orgChild1Id IS NOT NULL")
      .groupBy("pos.orgChild1Id")
      .getRawMany(),

    // Level 2: orgChild2
    posRepo
      .createQueryBuilder("pos")
      .select("pos.orgChild2Id", "nodeId")
      .addSelect("COUNT(*)", "totalCount")
      .addSelect("SUM(CASE WHEN pos.current_holderId IS NULL OR pos.current_holderId = '' THEN 1 ELSE 0 END)", "currentVacantCount")
      .addSelect("SUM(CASE WHEN pos.next_holderId IS NULL OR pos.next_holderId = '' THEN 1 ELSE 0 END)", "nextVacantCount")
      .where("pos.orgRevisionId = :orgRevisionId", { orgRevisionId })
      .andWhere("pos.orgChild2Id IS NOT NULL")
      .groupBy("pos.orgChild2Id")
      .getRawMany(),

    // Level 3: orgChild3
    posRepo
      .createQueryBuilder("pos")
      .select("pos.orgChild3Id", "nodeId")
      .addSelect("COUNT(*)", "totalCount")
      .addSelect("SUM(CASE WHEN pos.current_holderId IS NULL OR pos.current_holderId = '' THEN 1 ELSE 0 END)", "currentVacantCount")
      .addSelect("SUM(CASE WHEN pos.next_holderId IS NULL OR pos.next_holderId = '' THEN 1 ELSE 0 END)", "nextVacantCount")
      .where("pos.orgRevisionId = :orgRevisionId", { orgRevisionId })
      .andWhere("pos.orgChild3Id IS NOT NULL")
      .groupBy("pos.orgChild3Id")
      .getRawMany(),

    // Level 4: orgChild4
    posRepo
      .createQueryBuilder("pos")
      .select("pos.orgChild4Id", "nodeId")
      .addSelect("COUNT(*)", "totalCount")
      .addSelect("SUM(CASE WHEN pos.current_holderId IS NULL OR pos.current_holderId = '' THEN 1 ELSE 0 END)", "currentVacantCount")
      .addSelect("SUM(CASE WHEN pos.next_holderId IS NULL OR pos.next_holderId = '' THEN 1 ELSE 0 END)", "nextVacantCount")
      .where("pos.orgRevisionId = :orgRevisionId", { orgRevisionId })
      .andWhere("pos.orgChild4Id IS NOT NULL")
      .groupBy("pos.orgChild4Id")
      .getRawMany(),
  ]);

  return {
    orgRootMap: buildMap(orgRootResults),
    orgChild1Map: buildMap(orgChild1Results),
    orgChild2Map: buildMap(orgChild2Results),
    orgChild3Map: buildMap(orgChild3Results),
    orgChild4Map: buildMap(orgChild4Results),
  };
}

// Legacy function - kept for backward compatibility
// DEPRECATED: Use getPositionCountsAggregated instead
export async function getPositionCounts(orgRevisionId: string) {
  // Query all posMaster data for this revision with aggregation
  const rawData = await AppDataSource.getRepository(PosMaster)
    .createQueryBuilder("pos")
    .select([
      "pos.orgRootId",
      "pos.orgChild1Id",
      "pos.orgChild2Id",
      "pos.orgChild3Id",
      "pos.orgChild4Id",
      "pos.current_holderId",
      "pos.next_holderId",
    ])
    .where("pos.orgRevisionId = :orgRevisionId", { orgRevisionId })
    .getMany();

  // Helper to check if value is null or empty string
  const isNull = (val: any) => val === null || val === "";

  // Build maps for each level
  const orgRootMap = new Map<string, any>();
  const orgChild1Map = new Map<string, any>();
  const orgChild2Map = new Map<string, any>();
  const orgChild3Map = new Map<string, any>();
  const orgChild4Map = new Map<string, any>();

  // Nested maps for root positions (positions at specific levels)
  const rootPosMap = new Map<string, any>();

  for (const pos of rawData) {
    const orgRootId = pos.orgRootId || "NULL";
    const orgChild1Id = pos.orgChild1Id || "NULL";
    const orgChild2Id = pos.orgChild2Id || "NULL";
    const orgChild3Id = pos.orgChild3Id || "NULL";
    const orgChild4Id = pos.orgChild4Id || "NULL";

    // Level 0 (orgRoot) counts
    if (!orgRootMap.has(orgRootId)) {
      orgRootMap.set(orgRootId, {
        totalPosition: 0,
        totalPositionCurrentUse: 0,
        totalPositionCurrentVacant: 0,
        totalPositionNextUse: 0,
        totalPositionNextVacant: 0,
      });
    }
    const rootCounts = orgRootMap.get(orgRootId);
    rootCounts.totalPosition++;

    if (!isNull(pos.current_holderId)) rootCounts.totalPositionCurrentUse++;
    else rootCounts.totalPositionCurrentVacant++;

    if (!isNull(pos.next_holderId)) rootCounts.totalPositionNextUse++;
    else rootCounts.totalPositionNextVacant++;

    // Level 1 (orgChild1) counts
    if (!isNull(pos.orgChild1Id)) {
      if (!orgChild1Map.has(orgChild1Id)) {
        orgChild1Map.set(orgChild1Id, {
          totalPosition: 0,
          totalPositionCurrentUse: 0,
          totalPositionCurrentVacant: 0,
          totalPositionNextUse: 0,
          totalPositionNextVacant: 0,
        });
      }
      const child1Counts = orgChild1Map.get(orgChild1Id);
      child1Counts.totalPosition++;
      if (!isNull(pos.current_holderId)) child1Counts.totalPositionCurrentUse++;
      else child1Counts.totalPositionCurrentVacant++;
      if (!isNull(pos.next_holderId)) child1Counts.totalPositionNextUse++;
      else child1Counts.totalPositionNextVacant++;
    }

    // Level 2 (orgChild2) counts
    if (!isNull(pos.orgChild2Id)) {
      if (!orgChild2Map.has(orgChild2Id)) {
        orgChild2Map.set(orgChild2Id, {
          totalPosition: 0,
          totalPositionCurrentUse: 0,
          totalPositionCurrentVacant: 0,
          totalPositionNextUse: 0,
          totalPositionNextVacant: 0,
        });
      }
      const child2Counts = orgChild2Map.get(orgChild2Id);
      child2Counts.totalPosition++;
      if (!isNull(pos.current_holderId)) child2Counts.totalPositionCurrentUse++;
      else child2Counts.totalPositionCurrentVacant++;
      if (!isNull(pos.next_holderId)) child2Counts.totalPositionNextUse++;
      else child2Counts.totalPositionNextVacant++;
    }

    // Level 3 (orgChild3) counts
    if (!isNull(pos.orgChild3Id)) {
      if (!orgChild3Map.has(orgChild3Id)) {
        orgChild3Map.set(orgChild3Id, {
          totalPosition: 0,
          totalPositionCurrentUse: 0,
          totalPositionCurrentVacant: 0,
          totalPositionNextUse: 0,
          totalPositionNextVacant: 0,
        });
      }
      const child3Counts = orgChild3Map.get(orgChild3Id);
      child3Counts.totalPosition++;
      if (!isNull(pos.current_holderId)) child3Counts.totalPositionCurrentUse++;
      else child3Counts.totalPositionCurrentVacant++;
      if (!isNull(pos.next_holderId)) child3Counts.totalPositionNextUse++;
      else child3Counts.totalPositionNextVacant++;
    }

    // Level 4 (orgChild4) counts
    if (!isNull(pos.orgChild4Id)) {
      if (!orgChild4Map.has(orgChild4Id)) {
        orgChild4Map.set(orgChild4Id, {
          totalPosition: 0,
          totalPositionCurrentUse: 0,
          totalPositionCurrentVacant: 0,
          totalPositionNextUse: 0,
          totalPositionNextVacant: 0,
        });
      }
      const child4Counts = orgChild4Map.get(orgChild4Id);
      child4Counts.totalPosition++;
      if (!isNull(pos.current_holderId)) child4Counts.totalPositionCurrentUse++;
      else child4Counts.totalPositionCurrentVacant++;
      if (!isNull(pos.next_holderId)) child4Counts.totalPositionNextUse++;
      else child4Counts.totalPositionNextVacant++;
    }

    // Root position counts (positions at specific hierarchy levels)
    // For orgRoot level (all children are null)
    const rootLevelKey = orgRootId;
    if (!rootPosMap.has(rootLevelKey)) {
      rootPosMap.set(rootLevelKey, {
        totalRootPosition: 0,
        totalRootPositionCurrentUse: 0,
        totalRootPositionCurrentVacant: 0,
        totalRootPositionNextUse: 0,
        totalRootPositionNextVacant: 0,
      });
    }
    if (isNull(pos.orgChild1Id) && isNull(pos.orgChild2Id) && isNull(pos.orgChild3Id) && isNull(pos.orgChild4Id)) {
      const rootLevelCounts = rootPosMap.get(rootLevelKey);
      rootLevelCounts.totalRootPosition++;
      if (!isNull(pos.current_holderId)) rootLevelCounts.totalRootPositionCurrentUse++;
      else rootLevelCounts.totalRootPositionCurrentVacant++;
      if (!isNull(pos.next_holderId)) rootLevelCounts.totalRootPositionNextUse++;
      else rootLevelCounts.totalRootPositionNextVacant++;
    }

    // For orgChild1 level
    if (!isNull(pos.orgChild1Id)) {
      const child1LevelKey = `${orgRootId}-${pos.orgChild1Id}`;
      if (!rootPosMap.has(child1LevelKey)) {
        rootPosMap.set(child1LevelKey, {
          totalRootPosition: 0,
          totalRootPositionCurrentUse: 0,
          totalRootPositionCurrentVacant: 0,
          totalRootPositionNextUse: 0,
          totalRootPositionNextVacant: 0,
        });
      }
      if (isNull(pos.orgChild2Id) && isNull(pos.orgChild3Id) && isNull(pos.orgChild4Id)) {
        const child1LevelCounts = rootPosMap.get(child1LevelKey);
        child1LevelCounts.totalRootPosition++;
        if (!isNull(pos.current_holderId)) child1LevelCounts.totalRootPositionCurrentUse++;
        else child1LevelCounts.totalRootPositionCurrentVacant++;
        if (!isNull(pos.next_holderId)) child1LevelCounts.totalRootPositionNextUse++;
        else child1LevelCounts.totalRootPositionNextVacant++;
      }
    }

    // For orgChild2 level
    if (!isNull(pos.orgChild2Id)) {
      const child2LevelKey = `${orgRootId}-${pos.orgChild1Id}-${pos.orgChild2Id}`;
      if (!rootPosMap.has(child2LevelKey)) {
        rootPosMap.set(child2LevelKey, {
          totalRootPosition: 0,
          totalRootPositionCurrentUse: 0,
          totalRootPositionCurrentVacant: 0,
          totalRootPositionNextUse: 0,
          totalRootPositionNextVacant: 0,
        });
      }
      if (isNull(pos.orgChild3Id) && isNull(pos.orgChild4Id)) {
        const child2LevelCounts = rootPosMap.get(child2LevelKey);
        child2LevelCounts.totalRootPosition++;
        if (!isNull(pos.current_holderId)) child2LevelCounts.totalRootPositionCurrentUse++;
        else child2LevelCounts.totalRootPositionCurrentVacant++;
        if (!isNull(pos.next_holderId)) child2LevelCounts.totalRootPositionNextUse++;
        else child2LevelCounts.totalRootPositionNextVacant++;
      }
    }

    // For orgChild3 level
    if (!isNull(pos.orgChild3Id)) {
      const child3LevelKey = `${orgRootId}-${pos.orgChild1Id}-${pos.orgChild2Id}-${orgChild3Id}`;
      if (!rootPosMap.has(child3LevelKey)) {
        rootPosMap.set(child3LevelKey, {
          totalRootPosition: 0,
          totalRootPositionCurrentUse: 0,
          totalRootPositionCurrentVacant: 0,
          totalRootPositionNextUse: 0,
          totalRootPositionNextVacant: 0,
        });
      }
      if (isNull(pos.orgChild4Id)) {
        const child3LevelCounts = rootPosMap.get(child3LevelKey);
        child3LevelCounts.totalRootPosition++;
        if (!isNull(pos.current_holderId)) child3LevelCounts.totalRootPositionCurrentUse++;
        else child3LevelCounts.totalRootPositionCurrentVacant++;
        if (!isNull(pos.next_holderId)) child3LevelCounts.totalRootPositionNextUse++;
        else child3LevelCounts.totalRootPositionNextVacant++;
      }
    }

    // For orgChild4 level
    if (!isNull(pos.orgChild4Id)) {
      const child4LevelKey = `${orgRootId}-${pos.orgChild1Id}-${pos.orgChild2Id}-${orgChild3Id}-${pos.orgChild4Id}`;
      if (!rootPosMap.has(child4LevelKey)) {
        rootPosMap.set(child4LevelKey, {
          totalRootPosition: 0,
          totalRootPositionCurrentUse: 0,
          totalRootPositionCurrentVacant: 0,
          totalRootPositionNextUse: 0,
          totalRootPositionNextVacant: 0,
        });
      }
      const child4LevelCounts = rootPosMap.get(child4LevelKey);
      child4LevelCounts.totalRootPosition++;
      if (!isNull(pos.current_holderId)) child4LevelCounts.totalRootPositionCurrentUse++;
      else child4LevelCounts.totalRootPositionCurrentVacant++;
      if (!isNull(pos.next_holderId)) child4LevelCounts.totalRootPositionNextUse++;
      else child4LevelCounts.totalRootPositionNextVacant++;
    }
  }

  return {
    orgRootMap,
    orgChild1Map,
    orgChild2Map,
    orgChild3Map,
    orgChild4Map,
    rootPosMap,
  };
}

// Helper function to get counts from maps with defaults
export function getCounts(map: Map<string, any>, key: string) {
  return (
    map.get(key) || {
      totalPosition: 0,
      totalPositionCurrentUse: 0,
      totalPositionCurrentVacant: 0,
      totalPositionNextUse: 0,
      totalPositionNextVacant: 0,
    }
  );
}

// Helper function to get root position counts
export function getRootCounts(map: Map<string, any>, key: string) {
  return (
    map.get(key) || {
      totalRootPosition: 0,
      totalRootPositionCurrentUse: 0,
      totalRootPositionCurrentVacant: 0,
      totalRootPositionNextUse: 0,
      totalRootPositionNextVacant: 0,
    }
  );
}

// Helper function to get position counts from aggregated maps with defaults
export function getPositionCount(
  map: Map<string, PositionCountData>,
  key: string | null | undefined
): PositionCountData {
  if (!key) return { totalCount: 0, currentVacantCount: 0, nextVacantCount: 0 };
  return (
    map.get(key) || {
      totalCount: 0,
      currentVacantCount: 0,
      nextVacantCount: 0,
    }
  );
}
