/* eslint-disable spellcheck/spell-checker */
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client';

import React, { useState, useCallback, useEffect } from 'react';
import { Steps, Card, Button, Form, Alert, Tag, Divider, Space, Typography, Spin, notification, Row, Col } from 'antd';
import { useRouter } from 'next/navigation';
import { useSession } from 'next-auth/react';
import { useRoles } from '@/hooks/useRoles';
import { useFeatureFlag } from '@/hooks/useFeatureFlag';
import dayjs, { Dayjs } from 'dayjs';
import {
  validatePromotion,
  executePromotion,
  ValidatePromotionResponse,
  ValidationVacancy,
  PromotionChainEntry,
  ReplacementEntry,
  LeadTransferEntry,
  NewCreatedLaunchEntry,
  ExecutePromotionResponse,
} from './service';
import axiosInstance from '@/utills/axiosInstance';
import type {
  EmployeeOption, ChainMember, VacancyResolution, LeadTransferConfig,
  LeadTransferGroup, TeamMemberStrandedRow, PendingNewLaunch,
} from './types';
import Step1SelectEmployee from './steps/Step1SelectEmployee';
import Step2NewAssignment from './steps/Step2NewAssignment';
import Step3FillVacancies from './steps/Step3FillVacancies';
import Step4LeadTransfer from './steps/Step4LeadTransfer';
import Step5ReviewExecute from './steps/Step5ReviewExecute';

const { Text } = Typography;

const STEPS = [
  { title: 'Select Employee' },
  { title: 'New Assignment' },
  { title: 'Fill Vacancies' },
  { title: 'Lead Transfer' },
  { title: 'Review & Execute' },
];

// ── API helpers ──────────────────────────────────────────────────────────────

async function fetchEmployees(
  search?: string,
  roleId?: number,
  launchId?: number,
  fromRoleId?: number,
  toRoleId?: number,
  mode?: 'existing' | 'promote',
  cityId?: number,
): Promise<EmployeeOption[]> {
  try {
    const params: Record<string, string> = {};
    if (search)     params.search       = search;
    if (roleId)     params.role_id      = String(roleId);
    if (launchId)   params.launch_id    = String(launchId);
    if (fromRoleId) params.from_role_id = String(fromRoleId);
    if (toRoleId)   params.to_role_id   = String(toRoleId);
    if (mode)       params.mode         = mode;
    if (cityId)     params.city_id      = String(cityId);
    const res = await axiosInstance.get('/api/v1/hierarchy/promotions/employees', { params });
    const data = res.data?.data ?? res.data;
    return Array.isArray(data) ? data : [];
  } catch {
    return [];
  }
}

async function fetchLaunches(): Promise<{ id: number; name: string }[]> {
  try {
    const res = await axiosInstance.get('/api/v1/launches', { params: { status: 'On', pageSize: 200 } });
    const data = res.data?.data ?? res.data;
    const list = Array.isArray(data) ? data : data?.launches ?? data?.rows ?? [];
    return list.map((l: any) => ({ id: l.id, name: l.name ?? l.launch_name }));
  } catch {
    return [];
  }
}

async function fetchDesignations(roleId: number): Promise<{ id: number; name: string }[]> {
  try {
    const res = await axiosInstance.get('/api/v1/masters/designations', {
      params: { role_id: roleId, status: 'active', pageSize: 200 },
    });
    const data = res.data?.data ?? res.data;
    return Array.isArray(data) ? data.map((d: any) => ({ id: d.id, name: d.name })) : [];
  } catch {
    return [];
  }
}

async function fetchBulkCandidates(
  userId: number,
  roleId: number,
  mode: 'existing' | 'promote',
): Promise<EmployeeOption[]> {
  try {
    const res = await axiosInstance.get('/api/v1/hierarchy/promotions/bulk-vacancy-candidates', {
      params: { user_id: userId, role_id: roleId, mode },
    });
    const data = res.data?.data ?? res.data;
    return Array.isArray(data) ? data : [];
  } catch {
    return [];
  }
}

async function fetchSubstituteEmployees(employeeIds: number[]): Promise<Record<number, EmployeeOption[]>> {
  if (employeeIds.length === 0) return {};
  try {
    const res = await axiosInstance.get('/api/v1/hierarchy/promotions/getSubstituteEmployees', {
      params: { employeeIds: employeeIds.join(',') },
    });
    const data = res.data?.data ?? res.data;
    return data && typeof data === 'object' ? data : {};
  } catch {
    return {};
  }
}

async function fetchSubstituteLaunches(employeeId: number): Promise<{ id: number; name: string }[]> {
  try {
    const res = await axiosInstance.get('/api/v1/hierarchy/promotions/getSubstituteLaunches', {
      params: { employeeId },
    });
    const data = res.data?.data ?? res.data;
    return Array.isArray(data) ? data : [];
  } catch {
    return [];
  }
}

async function fetchFieldSalesByLaunches(launchIds: number[], excludeUserId?: number): Promise<EmployeeOption[]> {
  if (launchIds.length === 0) return [];
  try {
    const res = await axiosInstance.get('/api/v1/hierarchy/promotions/getFieldSalesByLaunches', {
      params: { launchIds: launchIds.join(','), excludeUserId },
    });
    const data = res.data?.data ?? res.data;
    return Array.isArray(data) ? data : [];
  } catch {
    return [];
  }
}

// ── Component ────────────────────────────────────────────────────────────────

const PromotionWizardPage: React.FC = () => {
  const router = useRouter();
  const { data: session } = useSession();
  const promotionEnabled = useFeatureFlag('promotion-module');
  const { config, getRoleByName, isLeafRole } = useRoles();

  // ── Route guard ───────────────────────────────────────────────────────────
  const sessionUser = session?.user;
  const isAllowedRole =
    sessionUser?.role?.id === getRoleByName('super_admin')?.id ||
    sessionUser?.role?.id === getRoleByName('city_head')?.id;

  useEffect(() => {
    if (session === null || (!promotionEnabled || !isAllowedRole)) {
      router.replace('/permission-denied');
    }
  }, [promotionEnabled, isAllowedRole, session, router]);

  // ── Wizard state ─────────────────────────────────────────────────────────
  const [currentStep, setCurrentStep] = useState(0);
  const [loading, setLoading] = useState(false);
  const [executionResult, setExecutionResult] = useState<ExecutePromotionResponse | null>(null);

  // Step 1
  const [selectedCityId, setSelectedCityId] = useState<number | null>(null);
  const [employees, setEmployees] = useState<EmployeeOption[]>([]);
  const [empLoading, setEmpLoading] = useState(false);
  const [selectedEmployee, setSelectedEmployee] = useState<EmployeeOption | null>(null);
  const [targetRoleId, setTargetRoleId] = useState<number | null>(null);
  const [targetDesignationId, setTargetDesignationId] = useState<number | null>(null);
  const [validation, setValidation] = useState<ValidatePromotionResponse | null>(null);
  const [validationLoading, setValidationLoading] = useState(false);
  const [designationsByRole, setDesignationsByRole] = useState<Record<number, { id: number; name: string }[]>>({});
  const [designationsLoadingRoleIds, setDesignationsLoadingRoleIds] = useState<Record<number, boolean>>({});
  const [step1Form] = Form.useForm();

  // Step 2
  const [launches, setLaunches] = useState<{ id: number; name: string }[]>([]);
  const [selectedLaunchIds, setSelectedLaunchIds] = useState<number[]>([]);
  const [lockedLaunchIds, setLockedLaunchIds] = useState<number[]>([]);
  const [preselectedLaunchIds, setPreselectedLaunchIds] = useState<number[]>([]);
  const [reportsToUserId, setReportsToUserId] = useState<number | undefined>();
  const [effectiveDate, setEffectiveDate] = useState<Dayjs | null>(dayjs());
  const [newLaunchFormOwnerId, setNewLaunchFormOwnerId] = useState<number | null>(null);
  const [newLaunchForm] = Form.useForm();
  const [pendingNewLaunches, setPendingNewLaunches] = useState<PendingNewLaunch[]>([]);
  const [teamMemberCandidates, setTeamMemberCandidates] = useState<EmployeeOption[]>([]);
  const [teamMemberCandidatesLoading, setTeamMemberCandidatesLoading] = useState(false);
  const [selectedTeamMemberIds, setSelectedTeamMemberIds] = useState<number[]>([]);
  const [transferLeadsEnabled, setTransferLeadsEnabled] = useState(false);

  // Step 3
  const [vacancyResolutions, setVacancyResolutions] = useState<VacancyResolution[]>([]);
  const [subChainMembers, setSubChainMembers] = useState<ChainMember[]>([]);
  // Composite key `roleId-launchId-mode` prevents employee list bleed across vacancies of the same role.
  const [subEmployees, setSubEmployees] = useState<Record<string, EmployeeOption[]>>({});
  const [subValidations, setSubValidations] = useState<Record<number, ValidatePromotionResponse>>({});
  const [subTeamMemberCandidates, setSubTeamMemberCandidates] = useState<Record<number, EmployeeOption[]>>({});
  const [subTeamMemberCandidatesLoading, setSubTeamMemberCandidatesLoading] = useState<Record<number, boolean>>({});
  const [subSelectedTeamMemberIds, setSubSelectedTeamMemberIds] = useState<Record<number, number[]>>({});
  const [bulkAssignMode, setBulkAssignMode] = useState(false);
  const [groupModes, setGroupModes] = useState<Record<number, 'existing' | 'promote' | undefined>>({});
  const [groupCandidates, setGroupCandidates] = useState<Record<number, EmployeeOption[]>>({});
  const [groupCandidatesLoading, setGroupCandidatesLoading] = useState<Record<number, boolean>>({});
  const [groupSelectedUserId, setGroupSelectedUserId] = useState<Record<number, number | undefined>>({});
  const [groupSelectedDesignationId, setGroupSelectedDesignationId] = useState<Record<number, number | undefined>>({});
  const [showAllLaunchesByGroup, setShowAllLaunchesByGroup] = useState<Record<number, boolean>>({});
  const [showAllSelectedLaunches, setShowAllSelectedLaunches] = useState(false);

  // Step 4
  const [leadTransfers, setLeadTransfers] = useState<LeadTransferConfig[]>([]);
  const [substituteEmployeesByUser, setSubstituteEmployeesByUser] = useState<Record<number, EmployeeOption[]>>({});
  const [substituteEmployeesLoadingByUser, setSubstituteEmployeesLoadingByUser] = useState<Record<number, boolean>>({});
  const [substituteLaunchesByUser, setSubstituteLaunchesByUser] = useState<Record<number, { id: number; name: string }[]>>({});
  const [teamMemberStrandedRows, setTeamMemberStrandedRows] = useState<TeamMemberStrandedRow[]>([]);
  const [teamMemberStrandedLoading, setTeamMemberStrandedLoading] = useState(false);

  // ── Derived flags ─────────────────────────────────────────────────────────

  const fieldSalesRoleId = getRoleByName('field_sales')?.id;
  const teamLeadRoleId = getRoleByName('team_lead')?.id;
  const isFsToTlPromotion = validation?.currentRoleName === 'field_sales' && validation?.toRoleName === 'team_lead';
  const isTlToCmPromotion = validation?.currentRoleName === 'team_lead' && validation?.toRoleName === 'cluster_manager';
  const isTreePromotion = validation?.promotionType === 'tree';

  const isBulkEligiblePromotion =
    !!validation &&
    ((validation.currentRoleName === 'team_lead' && validation.toRoleName === 'cluster_manager') ||
      (validation.currentRoleName === 'cluster_manager' && validation.toRoleName === 'gm_sales') ||
      (validation.currentRoleName === 'gm_sales' && validation.toRoleName === 'growth_head'));

  const holdsLeads = (roleName?: string) => roleName === 'field_sales' || roleName === 'team_lead';

  const needsLaunchAssignment = validation?.needsLaunchAssignment ?? false;
  const hasVacancies = (validation?.vacancies?.length ?? 0) > 0;
  const needsLeadTransfer =
    (holdsLeads(validation?.currentRoleName) && (validation?.needsLeadTransfer ?? false)) ||
    subChainMembers.some((m) => holdsLeads(m.fromRoleName) && m.validation?.needsLeadTransfer) ||
    (isFsToTlPromotion && selectedTeamMemberIds.length > 0);

  const visibleSteps: typeof STEPS = [STEPS[0]];
  if (needsLaunchAssignment) visibleSteps.push(STEPS[1]);
  if (hasVacancies) visibleSteps.push(STEPS[2]);
  if (needsLeadTransfer) visibleSteps.push(STEPS[3]);
  visibleSteps.push(STEPS[4]);

  const isLeadTransferStepActive = visibleSteps[currentStep]?.title === 'Lead Transfer';

  const promotionChainUserIds = new Set<number>([
    ...(selectedEmployee ? [selectedEmployee.id] : []),
    ...subChainMembers.map((m) => m.userId),
  ]);

  // ── Effects ───────────────────────────────────────────────────────────────

  // Batch-fetch substitute recipients when the Lead Transfer step opens.
  useEffect(() => {
    if (!isLeadTransferStepActive) return;
    const ownerIds = Array.from(new Set(leadTransfers.map((t) => t.fromUserId)));
    if (isFsToTlPromotion && reportsToUserId && !substituteEmployeesByUser[reportsToUserId]) {
      ownerIds.push(reportsToUserId);
    }
    const toFetch = ownerIds.filter((id) => !substituteEmployeesByUser[id] && !substituteEmployeesLoadingByUser[id]);
    if (toFetch.length === 0) return;
    setSubstituteEmployeesLoadingByUser((prev) => {
      const next = { ...prev };
      toFetch.forEach((id) => {
        next[id] = true;
      });
      return next;
    });
    fetchSubstituteEmployees(toFetch).then((byOwner) => {
      setSubstituteEmployeesByUser((prev) => {
        const next = { ...prev };
        toFetch.forEach((id) => {
          next[id] = byOwner[id] ?? [];
        });
        return next;
      });
      setSubstituteEmployeesLoadingByUser((prev) => {
        const next = { ...prev };
        toFetch.forEach((id) => {
          next[id] = false;
        });
        return next;
      });
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isLeadTransferStepActive, leadTransfers, isFsToTlPromotion, reportsToUserId]);

  // FS→TL: find which selected team members have leads outside the new TL's target launches.
  useEffect(() => {
    if (!isLeadTransferStepActive || !isFsToTlPromotion || selectedTeamMemberIds.length === 0) {
      setTeamMemberStrandedRows([]);
      return;
    }
    setTeamMemberStrandedLoading(true);
    const defaultToLaunch = selectedLaunchIds[0] ?? pendingNewLaunches[0]?.id;
    Promise.all(
      selectedTeamMemberIds.map(async(memberId) => {
        const member = teamMemberCandidates.find((c) => c.id === memberId);
        if (!member) return [];
        const memberLaunches = await fetchSubstituteLaunches(memberId);
        return memberLaunches
          .filter((l) => !selectedLaunchIds.includes(l.id))
          .map((l) => ({ memberId, memberName: member.name, fromLaunchId: l.id, fromLaunchName: l.name, toLaunchId: defaultToLaunch }));
      }),
    ).then((all) => {
      setTeamMemberStrandedRows(all.flat());
      setTeamMemberStrandedLoading(false);
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isLeadTransferStepActive, isFsToTlPromotion]);

  // Refresh team member candidates when launch selection or promotion type changes.
  useEffect(() => {
    if (isFsToTlPromotion) {
      const hasRemovedAllPreselectedLaunches = preselectedLaunchIds.length > 0 && selectedLaunchIds.length === 0;
      if (selectedLaunchIds.length === 0 && !hasRemovedAllPreselectedLaunches) {
        setTeamMemberCandidates([]);
        setSelectedTeamMemberIds([]);
        return;
      }
      setTeamMemberCandidatesLoading(true);
      if (hasRemovedAllPreselectedLaunches && selectedEmployee) {
        fetchSubstituteEmployees([selectedEmployee.id]).then((byUserId) => {
          const colleagues = (byUserId[selectedEmployee.id] ?? []).filter(
            (e) => e.roleName === 'field_sales' || e.roleName === 'pre_sales',
          );
          setTeamMemberCandidates(colleagues);
          const validIds = new Set(colleagues.map((e) => e.id));
          setSelectedTeamMemberIds((prev) => prev.filter((id) => validIds.has(id)));
          setTeamMemberCandidatesLoading(false);
        });
      } else {
        fetchFieldSalesByLaunches(selectedLaunchIds, selectedEmployee?.id).then((list) => {
          const filtered = list.filter((e) => e.roleName === 'field_sales' || e.roleName === 'pre_sales');
          setTeamMemberCandidates(filtered);
          const validIds = new Set(filtered.map((e) => e.id));
          setSelectedTeamMemberIds((prev) => prev.filter((id) => validIds.has(id)));
          setTeamMemberCandidatesLoading(false);
        });
      }
      return;
    }
    if (isTreePromotion && selectedEmployee) {
      setTeamMemberCandidatesLoading(true);
      fetchBulkCandidates(selectedEmployee.id, selectedEmployee.roleId, 'existing').then((list) => {
        setTeamMemberCandidates(list);
        const validIds = new Set(list.map((e) => e.id));
        setSelectedTeamMemberIds((prev) => prev.filter((id) => validIds.has(id)));
        setTeamMemberCandidatesLoading(false);
      });
      return;
    }
    setTeamMemberCandidates([]);
    setSelectedTeamMemberIds([]);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isFsToTlPromotion, isTreePromotion, selectedLaunchIds, preselectedLaunchIds, selectedEmployee?.id, selectedEmployee?.roleId]);

  // ── Helpers ───────────────────────────────────────────────────────────────

  const rolesAboveCurrent = useCallback(
    (currentRoleId: number) => {
      const { hierarchy, roles } = config;
      const directParentIds = hierarchy
        .filter((h) => h.child_role_id === currentRoleId)
        .map((h) => h.parent_role_id);
      return roles.filter((r) => directParentIds.includes(r.id) && r.name !== 'super_admin');
    },
    [config],
  );

  const loadDesignationsForRole = useCallback(
    async(roleId: number) => {
      if (designationsByRole[roleId] || designationsLoadingRoleIds[roleId]) return;
      setDesignationsLoadingRoleIds((prev) => ({ ...prev, [roleId]: true }));
      const list = await fetchDesignations(roleId);
      setDesignationsByRole((prev) => ({ ...prev, [roleId]: list }));
      setDesignationsLoadingRoleIds((prev) => ({ ...prev, [roleId]: false }));
    },
    [designationsByRole, designationsLoadingRoleIds],
  );

  const groupKeyOf = (r: VacancyResolution): number => r.sourceUserId ?? 0;

  const vacancyGroupKeys: number[] = [];
  {
    const seen = new Set<number>();
    for (const r of vacancyResolutions) {
      const key = groupKeyOf(r);
      if (!seen.has(key)) {
        seen.add(key);
        vacancyGroupKeys.push(key);
      }
    }
  }

  const groupVacatedByUserId = (groupKey: number): number | undefined =>
    groupKey === 0 ? selectedEmployee?.id : groupKey;

  const clearChainMember = (userId: number | undefined) => {
    if (userId == null) return;
    setSubChainMembers((prev) => prev.filter((m) => m.userId !== userId));
    setSubValidations((prev) => {
      const next = { ...prev };
      delete next[userId];
      return next;
    });
    setLeadTransfers((prev) => prev.filter((l) => l.fromUserId !== userId));
    setVacancyResolutions((prev) => prev.filter((r) => r.sourceUserId !== userId));
    setSubTeamMemberCandidates((prev) => {
      const next = { ...prev };
      delete next[userId];
      return next;
    });
    setSubTeamMemberCandidatesLoading((prev) => {
      const next = { ...prev };
      delete next[userId];
      return next;
    });
    setSubSelectedTeamMemberIds((prev) => {
      const next = { ...prev };
      delete next[userId];
      return next;
    });
    setPendingNewLaunches((prev) => prev.filter((l) => l.forUserId !== userId));
    setNewLaunchFormOwnerId((prev) => (prev === userId ? null : prev));
  };

  const mergeNestedVacancies = (sourceUserId: number, vacancies: ValidationVacancy[] | undefined) => {
    setVacancyResolutions((prev) => {
      const withoutStale = prev.filter((r) => r.sourceUserId !== sourceUserId);
      if (!vacancies || vacancies.length === 0) return withoutStale;
      const existingKeys = new Set(withoutStale.map((r) => `${r.vacancy.roleId}-${r.vacancy.launchId}-${r.vacancy.type}`));
      const additions: VacancyResolution[] = vacancies
        .filter((v) => !existingKeys.has(`${v.roleId}-${v.launchId}-${v.type}`))
        .map((v) => ({ vacancy: v, mode: undefined, sourceUserId }));
      return [...withoutStale, ...additions];
    });
  };

  const fetchSubTeamMembers = async(empId: number, launchIds: number[]) => {
    setSubTeamMemberCandidatesLoading((prev) => ({ ...prev, [empId]: true }));
    const list = await fetchFieldSalesByLaunches(launchIds, empId);
    setSubTeamMemberCandidates((prev) => ({ ...prev, [empId]: list }));
    setSubSelectedTeamMemberIds((prev) => ({ ...prev, [empId]: [] }));
    setSubTeamMemberCandidatesLoading((prev) => ({ ...prev, [empId]: false }));
  };

  const getOwnerRoleNames = (ownerId: number): { fromRoleName?: string; toRoleName?: string } => {
    if (ownerId === selectedEmployee?.id) {
      return { fromRoleName: validation?.currentRoleName, toRoleName: validation?.toRoleName };
    }
    const member = subChainMembers.find((m) => m.userId === ownerId);
    return { fromRoleName: member?.fromRoleName, toRoleName: member?.toRoleName };
  };

  const isOwnerTlToCm = (ownerId: number) => {
    const { fromRoleName, toRoleName } = getOwnerRoleNames(ownerId);
    return fromRoleName === 'team_lead' && toRoleName === 'cluster_manager';
  };

  const isOwnerFsToTl = (ownerId: number) => {
    const { fromRoleName, toRoleName } = getOwnerRoleNames(ownerId);
    return fromRoleName === 'field_sales' && toRoleName === 'team_lead';
  };

  const getLeadRecipientOptions = (ownerId: number) => {
    if (isOwnerFsToTl(ownerId)) {
      const recipients: Array<{ id: number; name: string; roleName: string }> = [];
      if (selectedEmployee) {
        recipients.push({ id: selectedEmployee.id, name: selectedEmployee.name, roleName: 'team_lead' });
      }
      const fsTeamMembers = selectedTeamMemberIds
        .map((id) => teamMemberCandidates.find((c) => c.id === id))
        .filter(Boolean)
        .map((emp) => ({ id: emp!.id, name: emp!.name, roleName: emp!.roleName }));
      recipients.push(...fsTeamMembers);
      const fsColleagues = (substituteEmployeesByUser[ownerId] ?? [])
        .filter((e) => (e.roleName === 'field_sales' || e.roleName === 'pre_sales') && e.id !== selectedEmployee?.id)
        .map((e) => ({ id: e.id, name: e.name, roleName: e.roleName }));
      recipients.push(...fsColleagues);
      const tlColleagues = (reportsToUserId ? (substituteEmployeesByUser[reportsToUserId] ?? []) : [])
        .filter((e) => e.roleName === 'team_lead' && e.id !== selectedEmployee?.id)
        .map((e) => ({ id: e.id, name: e.name, roleName: e.roleName }));
      recipients.push(...tlColleagues);
      const seenIds = new Set<number>();
      return recipients.filter((r) => {
        if (seenIds.has(r.id)) return false;
        seenIds.add(r.id);
        return true;
      });
    }

    const recipients = isOwnerTlToCm(ownerId)
      ? (substituteEmployeesByUser[ownerId] ?? []).map((e) => ({ id: e.id, name: e.name, roleName: e.roleName }))
      : [
          ...vacancyResolutions.filter((r) => r.userId).map((r) => {
            const cacheKey = `${r.vacancy.roleId}-${r.vacancy.launchId}-${r.mode}`;
            return {
              id: r.userId!,
              name: employees.find((e) => e.id === r.userId)?.name
                ?? (subEmployees[cacheKey] ?? []).find((e) => e.id === r.userId)?.name
                ?? `User #${r.userId}`,
              roleName: r.vacancy.roleName,
            };
          }),
          ...subChainMembers.map((m) => ({ id: m.userId, name: m.userName, roleName: m.toRoleName })),
          ...(substituteEmployeesByUser[ownerId] ?? []).map((e) => ({ id: e.id, name: e.name, roleName: e.roleName })),
        ];

    const seenIds = new Set<number>();
    const unique = recipients.filter((r) => {
 if (seenIds.has(r.id)) return false; seenIds.add(r.id); return true;
});
    return isTreePromotion ? unique.filter((r) => !promotionChainUserIds.has(r.id)) : unique;
  };

  const flatLeadRecipientOptions = !isTreePromotion
    ? getLeadRecipientOptions(leadTransfers[0]?.fromUserId ?? selectedEmployee?.id ?? 0)
    : [];

  const leadTransferGroups: LeadTransferGroup[] = [];
  if (isTreePromotion) {
    const groupsByUser = new Map<number, LeadTransferGroup>();
    leadTransfers.forEach((transfer, index) => {
      let group = groupsByUser.get(transfer.fromUserId);
      if (!group) {
        const { fromRoleName, toRoleName } = getOwnerRoleNames(transfer.fromUserId);
        group = {
          userId: transfer.fromUserId,
          userName: transfer.fromUserName,
          toRoleLabel: toRoleName ?? '',
          fromRoleLabel: fromRoleName ?? '',
          isTlToCm: isOwnerTlToCm(transfer.fromUserId),
          rows: [],
        };
        groupsByUser.set(transfer.fromUserId, group);
        leadTransferGroups.push(group);
      }
      group.rows.push({ transfer, index });
    });
  }

  // ── Step 1 handlers ───────────────────────────────────────────────────────

  const handleEmployeeSearch = async(search: string) => {
    if (!selectedCityId) return;
    setEmpLoading(true);
    const list = await fetchEmployees(search, undefined, undefined, undefined, undefined, undefined, selectedCityId);
    setEmployees(list);
    setEmpLoading(false);
  };

  const handleCitySelect = async(cityId: number | string) => {
    const id = Number(cityId);
    setSelectedCityId(id);
    setSelectedEmployee(null);
    setTargetRoleId(null);
    setTargetDesignationId(null);
    setValidation(null);
    setEmployees([]);
    resetStep2();
    setEmpLoading(true);
    const list = await fetchEmployees(undefined, undefined, undefined, undefined, undefined, undefined, id);
    setEmployees(list);
    setEmpLoading(false);
  };

  const handleCityClear = () => {
    setSelectedCityId(null);
    setSelectedEmployee(null);
    setTargetRoleId(null);
    setTargetDesignationId(null);
    setValidation(null);
    setEmployees([]);
    resetStep2();
  };

  const resetStep2 = () => {
    setEffectiveDate(dayjs());
    setReportsToUserId(undefined);
    setSelectedLaunchIds([]);
    setLockedLaunchIds([]);
    setPreselectedLaunchIds([]);
    setNewLaunchFormOwnerId(null);
    setPendingNewLaunches([]);
    setVacancyResolutions([]);
    setSubChainMembers([]);
    setLeadTransfers([]);
    setSubEmployees({});
    setSubValidations({});
    setSubTeamMemberCandidates({});
    setSubTeamMemberCandidatesLoading({});
    setSubSelectedTeamMemberIds({});
    setBulkAssignMode(false);
    setGroupModes({});
    setGroupCandidates({});
    setGroupCandidatesLoading({});
    setGroupSelectedUserId({});
    setGroupSelectedDesignationId({});
    setSubstituteEmployeesByUser({});
    setSubstituteEmployeesLoadingByUser({});
    setSubstituteLaunchesByUser({});
    setTeamMemberCandidates([]);
    setSelectedTeamMemberIds([]);
    setTransferLeadsEnabled(false);
    setTeamMemberStrandedRows([]);
    setTeamMemberStrandedLoading(false);
  };

  const handleEmployeeSelect = (empId: number) => {
    const emp = employees.find((e) => e.id === empId) ?? null;
    setSelectedEmployee(emp);
    setTargetRoleId(null);
    setTargetDesignationId(null);
    setValidation(null);
    resetStep2();
  };

  const handleEmployeeClear = async() => {
    setSelectedEmployee(null);
    setTargetRoleId(null);
    setTargetDesignationId(null);
    setValidation(null);
    resetStep2();
    if (!selectedCityId) return;
    setEmpLoading(true);
    const list = await fetchEmployees(undefined, undefined, undefined, undefined, undefined, undefined, selectedCityId);
    setEmployees(list);
    setEmpLoading(false);
  };

  const handleTargetRoleSelect = (roleId: number) => {
    setTargetRoleId(roleId);
    setTargetDesignationId(null);
    setValidation(null);
    resetStep2();
    loadDesignationsForRole(roleId);
  };

  const handleTargetDesignationSelect = async(designationId: number) => {
    setTargetDesignationId(designationId);
    resetStep2();
    if (!selectedEmployee || !targetRoleId) return;
    setValidationLoading(true);
    try {
      const result = await validatePromotion({
        userId: selectedEmployee.id,
        toRoleId: targetRoleId,
        toDesignationId: designationId,
      });
      setValidation(result);
      if (result.defaultManagerId != null) {
        setReportsToUserId(result.defaultManagerId);
      }
      if (result.needsLaunchAssignment && result.currentLaunchIds?.length) {
        const availableIds = new Set((result.availableLaunches ?? []).map((l) => l.id));
        const preselectedIds = Array.from(new Set(result.currentLaunchIds.filter((id) => availableIds.has(id))));
        if (preselectedIds.length) {
          setSelectedLaunchIds(preselectedIds);
          setPreselectedLaunchIds(preselectedIds);
        }
      }
      if (result.leads?.length) {
        setLeadTransfers(result.leads.map((l) => ({
          fromUserId: l.userId,
          fromUserName: l.userName,
          fromLaunchId: l.launchId,
          fromLaunchName: l.launchName,
          leadCount: l.leadCount,
        })));
      }
      if (result.vacancies?.length) {
        setVacancyResolutions(result.vacancies.map((v) => ({ vacancy: v, mode: undefined })));
      }
    } catch (err: any) {
      notification.error({
        message: 'Validation failed',
        description: err?.response?.data?.message ?? 'Could not validate promotion.',
      });
    } finally {
      setValidationLoading(false);
    }
  };

  // ── Step 2 handlers ───────────────────────────────────────────────────────

  const handleLoadLaunches = async() => {
    if (launches.length === 0) {
      const list = await fetchLaunches();
      setLaunches(list);
    }
  };

  const handleCreateNewLaunch = async(forUserId: number, forUserName: string) => {
    try {
      const values = await newLaunchForm.validateFields();
      setPendingNewLaunches((prev) => [
        ...prev,
        { id: -Date.now(), name: values.name, location: values.location, cityId: values.city_id, forUserId, forUserName },
      ]);
      newLaunchForm.resetFields();
      setNewLaunchFormOwnerId(null);
    } catch (err: any) {
      if (err?.errorFields) return;
    }
  };

  const handleSubTeamMembersChange = (empId: number, ids: number[]) => {
    setSubSelectedTeamMemberIds((prev) => ({ ...prev, [empId]: ids }));
  };

  // ── Step 3 handlers ───────────────────────────────────────────────────────

  const handleVacancyModeChange = async(vacancyIndex: number, mode: 'existing' | 'promote') => {
    clearChainMember(vacancyResolutions[vacancyIndex].userId);
    const updated = [...vacancyResolutions];
    updated[vacancyIndex] = { ...updated[vacancyIndex], mode, userId: undefined, chainMemberId: undefined, designationId: undefined };
    setVacancyResolutions(updated);

    const vacancy = vacancyResolutions[vacancyIndex].vacancy;
    const cacheKey = `${vacancy.roleId}-${vacancy.launchId}-${mode}`;
    if (!subEmployees[cacheKey]) {
      setEmpLoading(true);
      const list = await fetchEmployees(undefined, vacancy.roleId, vacancy.launchId, selectedEmployee?.roleId, targetRoleId ?? undefined, mode);
      let filtered = list.filter((e) => e.id !== selectedEmployee?.id);

      // TL self-fill: if the promoted FS→TL is taking over this launch, they can fill their own old FS seat.
      if (
        mode === 'promote' && filtered.length === 0 && selectedEmployee &&
        vacancy.roleId === fieldSalesRoleId && targetRoleId === teamLeadRoleId &&
        selectedLaunchIds.includes(vacancy.launchId)
      ) {
        filtered = [{
          id: selectedEmployee.id,
          name: selectedEmployee.name,
          roleName: 'team_lead',
          roleId: teamLeadRoleId!,
          launchId: vacancy.launchId,
          launchName: vacancy.launchName,
        }];
      }

      setSubEmployees((prev) => ({ ...prev, [cacheKey]: filtered }));
      setEmpLoading(false);
    }
  };

  const handleVacancyUserSelect = (vacancyIndex: number, userId: number) => {
    const updated = [...vacancyResolutions];
    updated[vacancyIndex] = { ...updated[vacancyIndex], userId };
    setVacancyResolutions(updated);
  };

  const handleVacancyPromoteEmployeeSelect = (vacancyIndex: number, empId: number) => {
    const vacancy = vacancyResolutions[vacancyIndex].vacancy;
    const previousUserId = vacancyResolutions[vacancyIndex].userId;
    if (previousUserId != null && previousUserId !== empId) clearChainMember(previousUserId);
    const updated = [...vacancyResolutions];
    updated[vacancyIndex] = { ...updated[vacancyIndex], userId: empId, chainMemberId: undefined, designationId: undefined };
    setVacancyResolutions(updated);
    loadDesignationsForRole(vacancy.roleId);
  };

  const handleVacancyPromoteDesignationSelect = async(vacancyIndex: number, designationId: number) => {
    const vacancy = vacancyResolutions[vacancyIndex].vacancy;
    const toRoleId = vacancy.roleId;
    const empId = vacancyResolutions[vacancyIndex].userId;
    if (empId == null) return;
    const cacheKey = `${toRoleId}-${vacancy.launchId}-promote`;
    const emp = (subEmployees[cacheKey] ?? employees).find((e) => e.id === empId);
    if (!emp) return;
    setVacancyResolutions((prev) => {
      const next = [...prev];
      next[vacancyIndex] = { ...next[vacancyIndex], designationId };
      return next;
    });
    try {
      const result = await validatePromotion({
        userId: empId,
        toRoleId,
        toDesignationId: designationId,
        primaryPromotionUserId: selectedEmployee?.id,
        primaryPromotionToRoleId: targetRoleId ?? undefined,
      });
      setSubValidations((prev) => ({ ...prev, [empId]: result }));
      const vacatedByUserId = vacancyResolutions[vacancyIndex].sourceUserId ?? selectedEmployee?.id;
      const newMember: ChainMember = {
        userId: empId,
        userName: emp.name,
        fromRoleId: emp.roleId,
        fromRoleName: emp.roleName,
        toRoleId,
        toRoleName: config.roles.find((r) => r.id === toRoleId)?.name ?? '',
        toDesignationId: designationId,
        toDesignationName: designationsByRole[toRoleId]?.find((d) => d.id === designationId)?.name,
        launchIds: [vacancy.launchId],
        reportsToUserId: vacatedByUserId,
        previousHolderUserId: vacatedByUserId,
        validation: result,
      };
      setSubChainMembers((prev) => [...prev.filter((m) => m.userId !== empId), newMember]);
      if (result.leads?.length) {
        setLeadTransfers((prev) => {
          const existingIds = new Set(prev.map((l) => `${l.fromUserId}-${l.fromLaunchId}`));
          const newEntries = result.leads.filter((l) => !existingIds.has(`${l.userId}-${l.launchId}`))
            .map((l) => ({
              fromUserId: l.userId,
              fromUserName: l.userName,
              fromLaunchId: l.launchId,
              fromLaunchName: l.launchName,
              leadCount: l.leadCount,
            }));
          return [...prev, ...newEntries];
        });
      }
      setVacancyResolutions((prev) => {
        const next = [...prev];
        next[vacancyIndex] = { ...next[vacancyIndex], userId: empId, chainMemberId: empId, designationId };
        return next;
      });
      mergeNestedVacancies(empId, result.vacancies);
      if (toRoleId === teamLeadRoleId) fetchSubTeamMembers(empId, [vacancy.launchId]);
    } catch (err: any) {
      notification.error({ message: 'Sub-promotion validation failed', description: err?.response?.data?.message });
    }
  };

  const handleBulkAssignToggle = (checked: boolean) => {
    Object.values(groupSelectedUserId).forEach(clearChainMember);
    setBulkAssignMode(checked);
    setGroupModes({});
    setGroupCandidates({});
    setGroupCandidatesLoading({});
    setGroupSelectedUserId({});
    setGroupSelectedDesignationId({});
    setVacancyResolutions((prev) => prev.map((r) => ({ vacancy: r.vacancy, mode: undefined, sourceUserId: r.sourceUserId })));
  };

  const handleGroupModeChange = async(groupKey: number, mode: 'existing' | 'promote') => {
    clearChainMember(groupSelectedUserId[groupKey]);
    setGroupModes((prev) => ({ ...prev, [groupKey]: mode }));
    setGroupSelectedUserId((prev) => ({ ...prev, [groupKey]: undefined }));
    setGroupSelectedDesignationId((prev) => ({ ...prev, [groupKey]: undefined }));
    setVacancyResolutions((prev) => prev.map((r) => (groupKeyOf(r) === groupKey ? { ...r, mode: undefined, userId: undefined } : r)));
    const groupRows = vacancyResolutions.filter((r) => groupKeyOf(r) === groupKey);
    const vacatedByUserId = groupVacatedByUserId(groupKey);
    if (vacatedByUserId == null || groupRows.length === 0) return;
    setGroupCandidatesLoading((prev) => ({ ...prev, [groupKey]: true }));
    const list = await fetchBulkCandidates(vacatedByUserId, groupRows[0].vacancy.roleId, mode);
    setGroupCandidates((prev) => ({ ...prev, [groupKey]: list }));
    setGroupCandidatesLoading((prev) => ({ ...prev, [groupKey]: false }));
  };

  const handleGroupEmployeeSelect = (groupKey: number, empId: number) => {
    const emp = (groupCandidates[groupKey] ?? []).find((e) => e.id === empId);
    if (!emp) return;
    const previousUserId = groupSelectedUserId[groupKey];
    if (previousUserId !== empId) clearChainMember(previousUserId);
    setGroupSelectedUserId((prev) => ({ ...prev, [groupKey]: empId }));
    setGroupSelectedDesignationId((prev) => ({ ...prev, [groupKey]: undefined }));
    if (groupModes[groupKey] === 'promote') {
      const groupRows = vacancyResolutions.filter((r) => groupKeyOf(r) === groupKey);
      const toRoleId = groupRows[0]?.vacancy.roleId;
      if (toRoleId != null) loadDesignationsForRole(toRoleId);
    } else {
      setVacancyResolutions((prev) => prev.map((r) => (groupKeyOf(r) === groupKey ? { ...r, mode: 'existing', userId: empId } : r)));
    }
  };

  const handleGroupDesignationSelect = async(groupKey: number, designationId: number) => {
    const empId = groupSelectedUserId[groupKey];
    if (empId == null) return;
    const emp = (groupCandidates[groupKey] ?? []).find((e) => e.id === empId);
    const groupRows = vacancyResolutions.filter((r) => groupKeyOf(r) === groupKey);
    const toRoleId = groupRows[0]?.vacancy.roleId;
    if (!emp || toRoleId == null) return;
    setGroupSelectedDesignationId((prev) => ({ ...prev, [groupKey]: designationId }));
    try {
      const result = await validatePromotion({
        userId: empId,
        toRoleId,
        toDesignationId: designationId,
        primaryPromotionUserId: selectedEmployee?.id,
        primaryPromotionToRoleId: targetRoleId ?? undefined,
      });
      setSubValidations((prev) => ({ ...prev, [empId]: result }));
      const vacatedByUserId = groupVacatedByUserId(groupKey);
      const newMember: ChainMember = {
        userId: empId,
        userName: emp.name,
        fromRoleId: emp.roleId,
        fromRoleName: emp.roleName,
        toRoleId,
        toRoleName: config.roles.find((r) => r.id === toRoleId)?.name ?? '',
        toDesignationId: designationId,
        toDesignationName: designationsByRole[toRoleId]?.find((d) => d.id === designationId)?.name,
        launchIds: groupRows.map((v) => v.vacancy.launchId),
        reportsToUserId: vacatedByUserId,
        previousHolderUserId: vacatedByUserId,
        validation: result,
      };
      setSubChainMembers((prev) => [...prev.filter((m) => m.userId !== empId), newMember]);
      if (result.leads?.length) {
        setLeadTransfers((prev) => {
          const existingIds = new Set(prev.map((l) => `${l.fromUserId}-${l.fromLaunchId}`));
          const newEntries = result.leads.filter((l) => !existingIds.has(`${l.userId}-${l.launchId}`))
            .map((l) => ({
              fromUserId: l.userId,
              fromUserName: l.userName,
              fromLaunchId: l.launchId,
              fromLaunchName: l.launchName,
              leadCount: l.leadCount,
            }));
          return [...prev, ...newEntries];
        });
      }
      setVacancyResolutions((prev) => prev.map((r) => (groupKeyOf(r) === groupKey
        ? { ...r, mode: 'promote', userId: empId, chainMemberId: empId }
        : r)));
      mergeNestedVacancies(empId, result.vacancies);
      if (toRoleId === teamLeadRoleId) fetchSubTeamMembers(empId, groupRows.map((v) => v.vacancy.launchId));
    } catch (err: any) {
      notification.error({ message: 'Sub-promotion validation failed', description: err?.response?.data?.message });
    }
  };

  // ── Step 4 handlers ───────────────────────────────────────────────────────

  const handleLeadTransferChange = (index: number, field: 'toUserId' | 'toLaunchId', value: number | undefined) => {
    const updated = [...leadTransfers];
    updated[index] = { ...updated[index], [field]: value, ...(field === 'toUserId' ? { toLaunchId: undefined } : {}) };
    setLeadTransfers(updated);
    if (field === 'toUserId' && value && !substituteLaunchesByUser[value]) {
      fetchSubstituteLaunches(value).then((list) => {
        setSubstituteLaunchesByUser((prev) => ({ ...prev, [value]: list }));
      });
    }
  };

  const handleBulkAssignLeads = (toUserId?: number) => {
    setLeadTransfers((prev) => prev.map((l) => ({ ...l, toUserId, toLaunchId: undefined })));
    if (toUserId && !substituteLaunchesByUser[toUserId]) {
      fetchSubstituteLaunches(toUserId).then((list) => {
        setSubstituteLaunchesByUser((prev) => ({ ...prev, [toUserId]: list }));
      });
    }
  };

  // ── Navigation ────────────────────────────────────────────────────────────

  const canProceedFromStep = (step: number): boolean => {
    const logicalStep = visibleSteps[step];
    if (logicalStep?.title === 'Select Employee') {
      return !!(selectedEmployee && targetRoleId && targetDesignationId && validation);
    }
    if (logicalStep?.title === 'New Assignment') {
      const launchesOk = !validation?.needsLaunchAssignment || selectedLaunchIds.length > 0 || pendingNewLaunches.length > 0;
      const hasManagers = (validation?.availableManagers?.length ?? 0) > 0;
      return !!(effectiveDate && launchesOk && (!hasManagers || !!reportsToUserId));
    }
    if (logicalStep?.title === 'Fill Vacancies') {
      return vacancyResolutions.every(
        (r) => r.vacancy.roleId === fieldSalesRoleId || (r.mode && r.userId && (r.mode !== 'promote' || r.chainMemberId)),
      );
    }
    if (logicalStep?.title === 'Lead Transfer') {
      const ownLeadsOk = leadTransfers.every((l) => {
        if (isOwnerFsToTl(l.fromUserId)) return true;
        if (isOwnerTlToCm(l.fromUserId)) return !!l.toUserId;
        return !!l.toUserId || !!l.toLaunchId;
      });
      const strandedOk = teamMemberStrandedRows.every((r) => !!r.toLaunchId);
      return ownLeadsOk && strandedOk;
    }
    return true;
  };

  const handleNext = () => {
    if (!canProceedFromStep(currentStep)) {
      notification.warning({ message: 'Please complete all required fields before proceeding.' });
      return;
    }
    setCurrentStep((s) => Math.min(s + 1, visibleSteps.length - 1));
  };

  const handleBack = () => setCurrentStep((s) => Math.max(s - 1, 0));

  // ── Execute ───────────────────────────────────────────────────────────────

  const handleExecute = async() => {
    if (!selectedEmployee || !targetRoleId || !targetDesignationId || !effectiveDate) {
      notification.error({ message: 'Missing required promotion details.' });
      return;
    }

    const chain: PromotionChainEntry[] = [
      {
        userId: selectedEmployee.id,
        fromRoleId: selectedEmployee.roleId,
        toRoleId: targetRoleId,
        toDesignationId: targetDesignationId,
        targetLaunchIds: selectedLaunchIds.length ? selectedLaunchIds : undefined,
        reportsToUserId,
        teamMemberIds: (isFsToTlPromotion || isTreePromotion) && selectedTeamMemberIds.length ? selectedTeamMemberIds : undefined,
      },
      ...subChainMembers.map((m) => ({
        userId: m.userId,
        fromRoleId: m.fromRoleId,
        toRoleId: m.toRoleId,
        toDesignationId: m.toDesignationId,
        targetLaunchIds: m.launchIds,
        reportsToUserId: m.reportsToUserId,
        previousHolderUserId: m.previousHolderUserId,
        teamMemberIds: subSelectedTeamMemberIds[m.userId]?.length ? subSelectedTeamMemberIds[m.userId] : undefined,
      })),
    ];

    const replacements: ReplacementEntry[] = vacancyResolutions
      .filter((r) => r.mode === 'existing' && r.userId)
      .map((r) => ({
        launchId: r.vacancy.launchId,
        roleId: r.vacancy.roleId,
        userId: r.userId!,
        slotType: r.vacancy.type,
        reportsToUserId: r.vacancy.type === 'launch_managers' ? (r.sourceUserId ?? selectedEmployee?.id) : undefined,
      }));

    const pendingLaunchIds = new Set(pendingNewLaunches.map((l) => l.id));

    const ownLeadTransfers: LeadTransferEntry[] =
      isFsToTlPromotion && !transferLeadsEnabled
        ? []
        : leadTransfers.filter((l) => l.toUserId || l.toLaunchId).map((l) => ({
            fromUserId: l.fromUserId,
            fromLaunchId: l.fromLaunchId,
            toUserId: l.toUserId,
            toLaunchId: l.toLaunchId,
            newlyCreateLaunchTransfer: l.toLaunchId != null && pendingLaunchIds.has(l.toLaunchId),
          }));

    const teamMemberLeadTransfers: LeadTransferEntry[] = teamMemberStrandedRows
      .filter((r) => r.toLaunchId && r.toLaunchId !== r.fromLaunchId)
      .map((r) => ({
        fromUserId: r.memberId,
        fromLaunchId: r.fromLaunchId,
        toLaunchId: r.toLaunchId,
        newlyCreateLaunchTransfer: r.toLaunchId != null && pendingLaunchIds.has(r.toLaunchId),
      }));

    const newCreatedLaunch: NewCreatedLaunchEntry[] = pendingNewLaunches.map((l) => {
      const teamMembersForLaunch = l.forUserId === selectedEmployee?.id
        ? selectedTeamMemberIds
        : subSelectedTeamMemberIds[subChainMembers.find((m) => m.userId === l.forUserId)?.userId ?? 0] ?? [];
      return {
        id: l.id,
        name: l.name,
        location: l.location,
        cityId: l.cityId,
        userId: l.forUserId,
        teamMemberIds: teamMembersForLaunch,
      };
    });

    setLoading(true);
    try {
      const result = await executePromotion({
        effectiveDate: effectiveDate.format('YYYY-MM-DD'),
        chain,
        replacements,
        leadTransfers: [...ownLeadTransfers, ...teamMemberLeadTransfers],
        newCreatedLaunch,
      });
      setExecutionResult(result);
      if (result.success) {
        notification.success({ message: result.message ?? 'Promotion executed successfully.' });
      } else {
        notification.error({ message: result.message ?? 'Promotion execution failed.' });
      }
    } catch (err: unknown) {
      const errorObj = err as Record<string, unknown>;
      const errorData = (errorObj?.response as Record<string, unknown>)?.data as Record<string, unknown> | undefined;
      notification.error({
        message: 'Execution error',
        description: (errorData?.message as string | undefined) ?? 'An unexpected error occurred.',
      });
    } finally {
      setLoading(false);
    }
  };

  const handleReset = () => {
    setCurrentStep(0);
    setSelectedCityId(null);
    step1Form.setFieldsValue({ city_id: null });
    setEmployees([]);
    setSelectedEmployee(null);
    setTargetRoleId(null);
    setTargetDesignationId(null);
    setValidation(null);
    setSelectedLaunchIds([]);
    setLockedLaunchIds([]);
    setPreselectedLaunchIds([]);
    setReportsToUserId(undefined);
    setEffectiveDate(dayjs());
    setVacancyResolutions([]);
    setSubChainMembers([]);
    setLeadTransfers([]);
    setExecutionResult(null);
    setSubEmployees({});
    setSubValidations({});
    setSubTeamMemberCandidates({});
    setSubTeamMemberCandidatesLoading({});
    setSubSelectedTeamMemberIds({});
    setNewLaunchFormOwnerId(null);
    setPendingNewLaunches([]);
    setBulkAssignMode(false);
    setGroupModes({});
    setGroupCandidates({});
    setGroupCandidatesLoading({});
    setGroupSelectedUserId({});
    setGroupSelectedDesignationId({});
    setShowAllLaunchesByGroup({});
    setSubstituteEmployeesByUser({});
    setSubstituteEmployeesLoadingByUser({});
    setSubstituteLaunchesByUser({});
    setShowAllSelectedLaunches(false);
    setTeamMemberCandidates([]);
    setSelectedTeamMemberIds([]);
    setTransferLeadsEnabled(false);
    setTeamMemberStrandedRows([]);
    setTeamMemberStrandedLoading(false);
  };

  // ── Render ────────────────────────────────────────────────────────────────

  if (executionResult) {
    return (
      <Card title="Promotion Result">
        <Alert
          type={executionResult.success ? 'success' : 'error'}
          message={executionResult.message}
          style={{ marginBottom: 16 }}
        />
        {executionResult.errors?.length ? (
          <Alert
            type="warning"
            message="Warnings"
            description={
              <ul style={{ margin: 0, paddingLeft: 16 }}>
                {executionResult.errors.map((e, i) => <li key={i}>{e}</li>)}
              </ul>
            }
            style={{ marginTop: 16 }}
          />
        ) : null}
        <Button type="primary" onClick={handleReset} style={{ marginTop: 24 }}>
          Start New Promotion
        </Button>
      </Card>
    );
  }

  const targetRoles = selectedEmployee ? rolesAboveCurrent(selectedEmployee.roleId) : [];

  return (
    <Card title="Promotion Wizard">
      <Steps
        current={currentStep}
        style={{ marginBottom: 24 }}
        items={visibleSteps.map((s) => ({ title: s.title }))}
      />

      {validation && (
        <Alert
          type="info"
          showIcon
          style={{ marginBottom: 24 }}
          message={
            <span>
              <Text strong>{validation.userName}</Text> is currently a{' '}
              <Tag color="default">{validation.currentRoleName}</Tag> — promoting to{' '}
              <Tag color="blue">{validation.toRoleName}</Tag>
            </span>
          }
        />
      )}

      <Spin spinning={loading || validationLoading}>
        {currentStep === 0 && (
          <Step1SelectEmployee
            selectedCityId={selectedCityId}
            employees={employees}
            empLoading={empLoading}
            selectedEmployee={selectedEmployee}
            targetRoleId={targetRoleId}
            targetDesignationId={targetDesignationId}
            validation={validation}
            validationLoading={validationLoading}
            designationsByRole={designationsByRole}
            designationsLoadingRoleIds={designationsLoadingRoleIds}
            targetRoles={targetRoles as any}
            step1Form={step1Form}
            onCitySelect={handleCitySelect}
            onCityClear={handleCityClear}
            onEmployeeSearch={handleEmployeeSearch}
            onEmployeeSelect={handleEmployeeSelect}
            onEmployeeClear={handleEmployeeClear}
            onTargetRoleSelect={handleTargetRoleSelect}
            onTargetRoleClear={() => {
              setTargetRoleId(null);
              setTargetDesignationId(null);
              setValidation(null);
            }}
            onTargetDesignationSelect={handleTargetDesignationSelect}
            onTargetDesignationClear={() => {
              setTargetDesignationId(null);
              setValidation(null);
            }}
          />
        )}

        {visibleSteps[currentStep]?.title === 'New Assignment' && (
          <Step2NewAssignment
            effectiveDate={effectiveDate}
            setEffectiveDate={setEffectiveDate}
            reportsToUserId={reportsToUserId}
            setReportsToUserId={setReportsToUserId}
            validation={validation}
            selectedLaunchIds={selectedLaunchIds}
            setSelectedLaunchIds={setSelectedLaunchIds}
            lockedLaunchIds={lockedLaunchIds}
            preselectedLaunchIds={preselectedLaunchIds}
            launches={launches}
            isFsToTlPromotion={isFsToTlPromotion}
            isTreePromotion={isTreePromotion}
            teamMemberCandidates={teamMemberCandidates}
            teamMemberCandidatesLoading={teamMemberCandidatesLoading}
            selectedTeamMemberIds={selectedTeamMemberIds}
            setSelectedTeamMemberIds={setSelectedTeamMemberIds}
            selectedEmployee={selectedEmployee}
            selectedCityId={selectedCityId}
            newLaunchForm={newLaunchForm}
            newLaunchFormOwnerId={newLaunchFormOwnerId}
            setNewLaunchFormOwnerId={setNewLaunchFormOwnerId}
            pendingNewLaunches={pendingNewLaunches}
            setPendingNewLaunches={setPendingNewLaunches}
            onLoadLaunches={handleLoadLaunches}
            onCreateNewLaunch={handleCreateNewLaunch}
            disabledDate={(current) => current && current < dayjs().startOf('day')}
          />
        )}

        {visibleSteps[currentStep]?.title === 'Fill Vacancies' && (
          <Step3FillVacancies
            isBulkEligiblePromotion={isBulkEligiblePromotion}
            bulkAssignMode={bulkAssignMode}
            onBulkAssignToggle={handleBulkAssignToggle}
            vacancyResolutions={vacancyResolutions}
            vacancyGroupKeys={vacancyGroupKeys}
            groupModes={groupModes}
            groupCandidates={groupCandidates}
            groupCandidatesLoading={groupCandidatesLoading}
            groupSelectedUserId={groupSelectedUserId}
            groupSelectedDesignationId={groupSelectedDesignationId}
            showAllLaunchesByGroup={showAllLaunchesByGroup}
            setShowAllLaunchesByGroup={setShowAllLaunchesByGroup}
            subEmployees={subEmployees}
            empLoading={empLoading}
            subValidations={subValidations}
            subTeamMemberCandidates={subTeamMemberCandidates}
            subTeamMemberCandidatesLoading={subTeamMemberCandidatesLoading}
            subSelectedTeamMemberIds={subSelectedTeamMemberIds}
            designationsByRole={designationsByRole}
            designationsLoadingRoleIds={designationsLoadingRoleIds}
            fieldSalesRoleId={fieldSalesRoleId}
            teamLeadRoleId={teamLeadRoleId}
            selectedEmployee={selectedEmployee}
            selectedLaunchIds={selectedLaunchIds}
            newLaunchForm={newLaunchForm}
            newLaunchFormOwnerId={newLaunchFormOwnerId}
            setNewLaunchFormOwnerId={setNewLaunchFormOwnerId}
            pendingNewLaunches={pendingNewLaunches}
            setPendingNewLaunches={setPendingNewLaunches}
            selectedCityId={selectedCityId}
            isLeafRole={isLeafRole}
            onGroupModeChange={handleGroupModeChange}
            onGroupEmployeeSelect={handleGroupEmployeeSelect}
            onGroupDesignationSelect={handleGroupDesignationSelect}
            onVacancyModeChange={handleVacancyModeChange}
            onVacancyUserSelect={handleVacancyUserSelect}
            onVacancyPromoteEmployeeSelect={handleVacancyPromoteEmployeeSelect}
            onVacancyPromoteDesignationSelect={handleVacancyPromoteDesignationSelect}
            onSubTeamMembersChange={handleSubTeamMembersChange}
            onCreateNewLaunch={handleCreateNewLaunch}
            groupKeyOf={groupKeyOf}
          />
        )}

        {visibleSteps[currentStep]?.title === 'Lead Transfer' && (
          <Step4LeadTransfer
            isFsToTlPromotion={isFsToTlPromotion}
            isTlToCmPromotion={isTlToCmPromotion}
            isTreePromotion={isTreePromotion}
            transferLeadsEnabled={transferLeadsEnabled}
            setTransferLeadsEnabled={setTransferLeadsEnabled}
            leadTransfers={leadTransfers}
            leadTransferGroups={leadTransferGroups}
            flatLeadRecipientOptions={flatLeadRecipientOptions}
            substituteEmployeesLoadingByUser={substituteEmployeesLoadingByUser}
            substituteLaunchesByUser={substituteLaunchesByUser}
            teamMemberStrandedRows={teamMemberStrandedRows}
            setTeamMemberStrandedRows={setTeamMemberStrandedRows}
            teamMemberStrandedLoading={teamMemberStrandedLoading}
            selectedLaunchIds={selectedLaunchIds}
            pendingNewLaunches={pendingNewLaunches}
            launches={launches}
            validation={validation}
            getLeadRecipientOptions={getLeadRecipientOptions}
            onLeadTransferChange={handleLeadTransferChange}
            onBulkAssignLeads={handleBulkAssignLeads}
            onLoadLaunches={handleLoadLaunches}
          />
        )}

        {visibleSteps[currentStep]?.title === 'Review & Execute' && (
          <Step5ReviewExecute
            validation={validation}
            effectiveDate={effectiveDate}
            selectedLaunchIds={selectedLaunchIds}
            launches={launches}
            pendingNewLaunches={pendingNewLaunches}
            reportsToUserId={reportsToUserId}
            subChainMembers={subChainMembers}
            vacancyResolutions={vacancyResolutions}
            leadTransfers={leadTransfers}
            showAllSelectedLaunches={showAllSelectedLaunches}
            setShowAllSelectedLaunches={setShowAllSelectedLaunches}
            employees={employees}
            subEmployees={subEmployees}
            groupCandidates={groupCandidates}
            getLeadRecipientOptions={getLeadRecipientOptions}
          />
        )}
      </Spin>

      <Divider />
      <Row justify="space-between">
        <Col>
          {currentStep > 0 && (
            <Button onClick={handleBack} disabled={loading}>Back</Button>
          )}
        </Col>
        <Col>
          <Space>
            <Button onClick={handleReset} disabled={loading}>Reset</Button>
            {currentStep < visibleSteps.length - 1 ? (
              <Button type="primary" onClick={handleNext} disabled={loading || validationLoading}>
                Next
              </Button>
            ) : (
              <Button type="primary" danger onClick={handleExecute} loading={loading} disabled={!validation}>
                Execute Promotion
              </Button>
            )}
          </Space>
        </Col>
      </Row>
    </Card>
  );
};

export default PromotionWizardPage;
