import {
  DISPO_PROJECT_REFERENCE_SHEET,
  DISPO_REFERENCE_SHEET,
  type ParsedClientReference,
  type ParsedCountryReference,
  type ParsedManagementLevelGroupReference,
  type ParsedOrgUnitReference,
  type ParsedReferenceWorkbook,
  findSectionRow,
  getCountryReferenceConfig,
  normalizeClientCode,
  normalizeNullableWorkbookValue,
  normalizeText,
  sanitizeClientName,
} from "./shared.js";
import { readWorksheetMatrix, toColumnLetter } from "./read-workbook.js";

function isTerminalSectionName(value: string | null, names: readonly string[]): boolean {
  if (!value) {
    return false;
  }

  const normalizedValue = value.toLowerCase();
  return names.some((name) => name.toLowerCase() === normalizedValue);
}

function parseCountryReferences(
  rows: Awaited<ReturnType<typeof readWorksheetMatrix>>,
): { countries: ParsedCountryReference[]; warnings: string[] } {
  const warnings: string[] = [];
  const countries: ParsedCountryReference[] = [];
  const startRow = findSectionRow(rows, "Country/Territory") + 1;

  for (let rowNumber = startRow; rowNumber <= rows.length; rowNumber += 1) {
    const row = rows[rowNumber - 1] ?? [];
    const firstCell = normalizeText(row[0]);
    if (!firstCell) {
      continue;
    }

    if (isTerminalSectionName(firstCell, ["Org Unit Level 5"])) {
      break;
    }

    const config = getCountryReferenceConfig(firstCell);
    if (!config) {
      warnings.push(`Unsupported country reference "${firstCell}" in EID-Attr row ${rowNumber}`);
      continue;
    }

    const metroCities = row
      .slice(1)
      .map((value) => normalizeNullableWorkbookValue(value))
      .filter((value): value is string => Boolean(value));

    countries.push({
      sourceRow: rowNumber,
      countryCode: config.code,
      name: firstCell,
      dailyWorkingHours: config.dailyWorkingHours,
      metroCities,
      ...("scheduleRules" in config ? { scheduleRules: config.scheduleRules } : {}),
    });
  }

  return { countries, warnings };
}

function parseOrgUnitReferences(
  rows: Awaited<ReturnType<typeof readWorksheetMatrix>>,
): { orgUnits: ParsedOrgUnitReference[]; warnings: string[] } {
  const warnings: string[] = [];
  const orgUnits: ParsedOrgUnitReference[] = [];

  const levelFiveHeaderRow = findSectionRow(rows, "Org Unit Level 5");
  const levelSixHeaderRow = findSectionRow(rows, "Org Unit Level 6");
  const managementLevelRow = findSectionRow(rows, "Management Level Group");

  for (let rowNumber = levelFiveHeaderRow + 1; rowNumber < levelSixHeaderRow; rowNumber += 1) {
    const row = rows[rowNumber - 1] ?? [];
    const levelFiveName = normalizeNullableWorkbookValue(row[0]);
    if (!levelFiveName) {
      continue;
    }

    const secondCell = normalizeText(row[1]);
    if (secondCell?.includes("wird nicht mehr benötigt")) {
      warnings.push(`Ignored deprecated org unit row "${levelFiveName}" in EID-Attr row ${rowNumber}`);
      continue;
    }

    orgUnits.push({
      sourceRow: rowNumber,
      level: 5,
      name: levelFiveName,
      parentName: null,
      sortOrder: orgUnits.filter((entry) => entry.level === 5).length + 1,
    });

    row
      .slice(1)
      .map((value) => normalizeNullableWorkbookValue(value))
      .filter((value): value is string => Boolean(value))
      .forEach((levelSixName, index) => {
        orgUnits.push({
          sourceRow: rowNumber,
          level: 6,
          name: levelSixName,
          parentName: levelFiveName,
          sortOrder: index + 1,
        });
      });
  }

  for (let rowNumber = levelSixHeaderRow + 1; rowNumber < managementLevelRow; rowNumber += 1) {
    const row = rows[rowNumber - 1] ?? [];
    const levelSixName = normalizeNullableWorkbookValue(row[0]);
    if (!levelSixName) {
      continue;
    }

    row
      .slice(1)
      .map((value) => normalizeNullableWorkbookValue(value))
      .filter((value): value is string => Boolean(value))
      .forEach((levelSevenName, index) => {
        orgUnits.push({
          sourceRow: rowNumber,
          level: 7,
          name: levelSevenName,
          parentName: levelSixName,
          sortOrder: index + 1,
        });
      });
  }

  return { orgUnits, warnings };
}

function parseManagementLevelReferences(
  rows: Awaited<ReturnType<typeof readWorksheetMatrix>>,
): { managementLevelGroups: ParsedManagementLevelGroupReference[]; warnings: string[] } {
  const warnings: string[] = [];
  const managementLevelGroups: ParsedManagementLevelGroupReference[] = [];
  const startRow = findSectionRow(rows, "Management Level Group") + 1;

  for (let rowNumber = startRow; rowNumber <= rows.length; rowNumber += 1) {
    const row = rows[rowNumber - 1] ?? [];
    const groupName = normalizeNullableWorkbookValue(row[0]);
    if (!groupName) {
      continue;
    }

    if (isTerminalSectionName(groupName, ["FTE"])) {
      break;
    }

    const targetPercentage = typeof row[1] === "number" ? row[1] : null;
    if (targetPercentage === null) {
      warnings.push(`Missing target percentage for management level group "${groupName}" in row ${rowNumber}`);
      continue;
    }

    const levels = row
      .slice(2)
      .map((value) => normalizeNullableWorkbookValue(value))
      .filter((value): value is string => Boolean(value));

    managementLevelGroups.push({
      sourceRow: rowNumber,
      name: groupName,
      targetPercentage,
      sortOrder: managementLevelGroups.length + 1,
      levels,
    });
  }

  return { managementLevelGroups, warnings };
}

function parseClientReferences(
  rows: Awaited<ReturnType<typeof readWorksheetMatrix>>,
): { clients: ParsedClientReference[]; warnings: string[] } {
  const warnings: string[] = [];
  const clients: ParsedClientReference[] = [];
  const startRow = findSectionRow(rows, "WBS Master Client") + 1;

  for (let rowNumber = startRow; rowNumber <= rows.length; rowNumber += 1) {
    const row = rows[rowNumber - 1] ?? [];
    const masterClientName = normalizeNullableWorkbookValue(row[0]);
    if (!masterClientName) {
      continue;
    }

    const normalizedMasterName = sanitizeClientName(masterClientName);
    const masterClientCode = normalizeClientCode(normalizedMasterName);

    clients.push({
      sourceRow: rowNumber,
      sourceColumn: "A",
      clientCode: masterClientCode,
      name: normalizedMasterName,
      parentClientCode: null,
      parentName: null,
      sortOrder: clients.filter((entry) => entry.parentName === null).length + 1,
    });

    row
      .slice(1)
      .map((value) => normalizeNullableWorkbookValue(value))
      .filter((value): value is string => Boolean(value))
      .forEach((childName, index) => {
        clients.push({
          sourceRow: rowNumber,
          sourceColumn: toColumnLetter(index + 2),
          clientCode: null,
          name: sanitizeClientName(childName),
          parentClientCode: masterClientCode,
          parentName: normalizedMasterName,
          sortOrder: index + 1,
        });
      });
  }

  return { clients, warnings };
}

export async function parseMandatoryDispoReferenceWorkbook(
  workbookPath: string,
): Promise<ParsedReferenceWorkbook> {
  const eidAttrRows = await readWorksheetMatrix(workbookPath, DISPO_REFERENCE_SHEET);
  const projectAttrRows = await readWorksheetMatrix(workbookPath, DISPO_PROJECT_REFERENCE_SHEET);

  const countryResult = parseCountryReferences(eidAttrRows);
  const orgUnitResult = parseOrgUnitReferences(eidAttrRows);
  const managementLevelResult = parseManagementLevelReferences(eidAttrRows);
  const clientResult = parseClientReferences(projectAttrRows);

  return {
    countries: countryResult.countries,
    orgUnits: orgUnitResult.orgUnits,
    managementLevelGroups: managementLevelResult.managementLevelGroups,
    clients: clientResult.clients,
    warnings: [
      ...countryResult.warnings,
      ...orgUnitResult.warnings,
      ...managementLevelResult.warnings,
      ...clientResult.warnings,
    ],
  };
}
