import dayjs from 'dayjs';
import {
  ActionType,
  AmendmentPassengerScheduleDTO,
  IdWithTransportScheduleAmendmentsDTO,
  PeriodOfDay,
  TransportScheduleAmendmentDTO,
  type AmendmentPassengerDetails,
  type PassengerScheduleWeekDTO,
} from '@/shared/types/ammendment-passenger';
import { FormikAmendmentPassengerDetails, WeeklyScheduleType } from '../types/formik-types';
import {
  DaysOfWeek,
  EstablishmentTransportScheduleDTO,
  Representative,
  SensTrip,
  TransportSchedule,
  WeekType,
} from '@/shared/types/passenger';
import { ScheduleDTO } from '@/shared/types/amendment';
import { dayOfWeekLabelsFr } from '@/shared/types/establishment';

export const DAYS_OF_WEEK_FR = [
  'lundi',
  'mardi',
  'mercredi',
  'jeudi',
  'vendredi',
  'samedi',
  'dimanche',
];

export const DAY_MAPPING: Record<string, string> = {
  MONDAY: 'lundi',
  TUESDAY: 'mardi',
  WEDNESDAY: 'mercredi',
  THURSDAY: 'jeudi',
  FRIDAY: 'vendredi',
  SATURDAY: 'samedi',
  SUNDAY: 'dimanche',
};

export const REVERSE_DAY_MAPPING: Record<string, string> = {
  lundi: 'MONDAY',
  mardi: 'TUESDAY',
  mercredi: 'WEDNESDAY',
  jeudi: 'THURSDAY',
  vendredi: 'FRIDAY',
  samedi: 'SATURDAY',
  dimanche: 'SUNDAY',
};

/**
 * Extrait les IDs existants des semaines depuis AmendmentPassengerDetails
 */
export const extractExistingWeekIds = (
  amendment: AmendmentPassengerDetails
): { pair?: string | null; impair?: string | null } => {
  const existingWeekIds: { pair?: string | null; impair?: string | null } = {};

  if (amendment.passengerScheduleWeeks?.length > 0) {
    amendment.passengerScheduleWeeks.forEach((week) => {
      const weekType = week.weekType === 'EVEN' ? 'pair' : 'impair';

      if (weekType === 'pair') {
        existingWeekIds.pair = week.id;
      } else {
        existingWeekIds.impair = week.id;
      }
    });
  }

  return existingWeekIds;
};
export const createDefaultWeekSchedule = () => {
  return Object.fromEntries(
    DAYS_OF_WEEK_FR.map((day) => [
      day,
      {
        morning: {
          aller: dayjs().hour(8).minute(0).second(0),
        },
        evening: {
          retour: dayjs().hour(18).minute(0).second(0),
        },
      },
    ])
  );
};

export const deepCloneSchedules = (schedules: any): any => {
  if (schedules === null || typeof schedules !== 'object') {
    return schedules;
  }
  if (schedules.$d !== undefined && typeof schedules.isValid === 'function') {
    return schedules;
  }

  if (Array.isArray(schedules)) {
    return schedules.map(deepCloneSchedules);
  }

  const cloned: any = {};
  for (const key in schedules) {
    if (schedules.hasOwnProperty(key)) {
      cloned[key] = deepCloneSchedules(schedules[key]);
    }
  }
  return cloned;
};
/**
 * Convertit AmendmentPassengerDetails vers FormikAmendmentPassengerDetails
 * (Backend → Formik)
 */
export const convertToFormikValues = (
  amendment: AmendmentPassengerDetails
): FormikAmendmentPassengerDetails => {
  const weeklySchedules: WeeklyScheduleType = {
    pair: {},
    impair: {},
  };

  if (amendment.passengerScheduleWeeks?.length > 0) {
    amendment.passengerScheduleWeeks.forEach((week) => {
      const weekType = week.weekType === 'EVEN' ? 'pair' : 'impair';

      week.amendmentPassengerSchedule?.forEach((schedule) => {
        const dayKey = DAY_MAPPING[schedule.dayOfWeek]?.toLowerCase();
        if (!dayKey) return;

        const time = dayjs(schedule.scheduledTime, 'HH:mm:ss');
        const isAller = schedule.sensTrip === SensTrip.DEPARTURE;

        if (!weeklySchedules[weekType][dayKey]) {
          weeklySchedules[weekType][dayKey] = {
            morning: {
              aller: null,
              retour: null,
            },
            evening: {
              aller: null,
              retour: null,
            },
          };
        }

        if (schedule.periodOfDay === PeriodOfDay.MORNING) {
          if (isAller) {
            weeklySchedules[weekType][dayKey].morning.aller = time;
          } else {
            weeklySchedules[weekType][dayKey].morning.retour = time;
          }
        } else {
          if (isAller) {
            weeklySchedules[weekType][dayKey].evening.aller = time;
          } else {
            weeklySchedules[weekType][dayKey].evening.retour = time;
          }
        }
      });
    });
  }

  const { passengerScheduleWeeks, ...restAmendment } = amendment;

  return {
    ...restAmendment,
    weeklySchedules,
  };
};

/**
 * Convertit FormikAmendmentPassengerDetails vers AmendmentPassengerDetails
 * (Formik → Backend)
 */
export const convertFromFormikValues = (
  formikValues: FormikAmendmentPassengerDetails,
  existingWeekIds?: { pair?: string | null; impair?: string | null }
): AmendmentPassengerDetails => {
  const passengerScheduleWeeks: PassengerScheduleWeekDTO[] = [];

  const pairSchedules = processWeekData(formikValues.weeklySchedules.pair);
  if (pairSchedules.length > 0) {
    passengerScheduleWeeks.push({
      id: existingWeekIds?.pair || null,
      weekType: 'EVEN',
      amendmentPassengerSchedule: pairSchedules,
    });
  }

  const impairSchedules = processWeekData(formikValues.weeklySchedules.impair);
  if (impairSchedules.length > 0) {
    passengerScheduleWeeks.push({
      id: existingWeekIds?.impair || null,
      weekType: 'ODD',
      amendmentPassengerSchedule: impairSchedules,
    });
  }

  const { weeklySchedules, ...restFormikValues } = formikValues;

  return {
    ...restFormikValues,
    passengerScheduleWeeks,
  };
};
function processWeekData(
  weekData: WeeklyScheduleType['pair'] | WeeklyScheduleType['impair']
): AmendmentPassengerScheduleDTO[] {
  const schedules: AmendmentPassengerScheduleDTO[] = [];

  Object.entries(weekData).forEach(([dayKey, dayData]) => {
    const dayOfWeek = REVERSE_DAY_MAPPING[dayKey];
    if (!dayOfWeek) return;

    if (dayData.morning?.aller) {
      schedules.push({
        sensTrip: SensTrip.DEPARTURE,
        dayOfWeek: dayOfWeek as DaysOfWeek,
        periodOfDay: PeriodOfDay.MORNING,
        scheduledTime: extractTime(dayData.morning.aller.toISOString()),
      });
    }

    if (dayData.morning?.retour) {
      schedules.push({
        sensTrip: SensTrip.RETURN,
        dayOfWeek: dayOfWeek as DaysOfWeek,
        periodOfDay: PeriodOfDay.MORNING,
        scheduledTime: extractTime(dayData.morning.retour.toISOString()),
      });
    }

    if (dayData.evening?.aller) {
      schedules.push({
        sensTrip: SensTrip.DEPARTURE,
        dayOfWeek: dayOfWeek as DaysOfWeek,
        periodOfDay: PeriodOfDay.EVENING,
        scheduledTime: extractTime(dayData.evening.aller.toISOString()),
      });
    }

    if (dayData.evening?.retour) {
      schedules.push({
        sensTrip: SensTrip.RETURN,
        dayOfWeek: dayOfWeek as DaysOfWeek,
        periodOfDay: PeriodOfDay.EVENING,
        scheduledTime: extractTime(dayData.evening.retour.toISOString()),
      });
    }
  });

  return schedules;
}
export const extractTime = (dateString: string): string => {
  return dayjs(dateString).format('HH:mm:ss');
};
/**
 * Crée des valeurs initiales vides pour un nouveau formulaire
 */
export const createEmptyFormikValues = (passengerId: number): FormikAmendmentPassengerDetails => {
  return {
    id: '0',
    subject: '',
    observation: '',
    creationType: '',
    actionType: ActionType.ADD_PASSENGER,
    startDate: '',
    endDate: '',
    amendmentType: '',
    specificity: '',
    passengerId,
    triggeredAmendmentCircuitId: '',
    establishmentWeekPassengerMap: [],
    amendmentRepresentatives: [],
    weeklySchedules: {
      pair: {},
      impair: {},
    },
  };
};

export const mapSchedulesToWeeklySchedule = (schedules: ScheduleDTO[]): WeeklyScheduleType => {
  const weeklySchedule: WeeklyScheduleType = {
    pair: {},
    impair: {},
  };

  const dayMapping: Record<string, string> = {
    MONDAY: 'lundi',
    TUESDAY: 'mardi',
    WEDNESDAY: 'mercredi',
    THURSDAY: 'jeudi',
    FRIDAY: 'vendredi',
    SATURDAY: 'samedi',
    SUNDAY: 'dimanche',
  };

  schedules.forEach((schedule) => {
    const dayKey = dayMapping[schedule.day];
    if (!dayKey) return;

    const departureTime = dayjs(schedule.departureTime, 'HH:mm');
    const arrivalTime = dayjs(schedule.arrivalTime, 'HH:mm');
    const isMorning = departureTime.hour() < 12;
    const period = isMorning ? 'morning' : 'evening';
    const isAller = schedule.direction === 'OUTBOUND';
    const type = isAller ? 'aller' : 'retour';
    ['pair', 'impair'].forEach((weekType) => {
      if (!weeklySchedule[weekType as keyof WeeklyScheduleType][dayKey]) {
        weeklySchedule[weekType as keyof WeeklyScheduleType][dayKey] = {
          morning: { aller: null, retour: null },
          evening: { aller: null, retour: null },
        };
      }

      if (period === 'morning') {
        weeklySchedule[weekType as keyof WeeklyScheduleType][dayKey].morning[type as 'aller'] =
          departureTime;
      } else {
        weeklySchedule[weekType as keyof WeeklyScheduleType][dayKey].evening[type as 'retour'] =
          arrivalTime;
      }
    });
  });

  return weeklySchedule;
};

export const getDayName = (dayOfWeek: string): string => {
  const dayNames: Record<string, string> = {
    monday: 'Lundi',
    tuesday: 'Mardi',
    wednesday: 'Mercredi',
    thursday: 'Jeudi',
    friday: 'Vendredi',
    saturday: 'Samedi',
    sunday: 'Dimanche',
  };
  return dayNames[dayOfWeek.toLowerCase()] || dayOfWeek;
};

export const checkScheduleConflict = (
  currentTabId: number,
  newSchedules: TransportSchedule[],
  representatives: Representative[]
): {
  hasConflict: boolean;
  conflictDetails: string[];
} => {
  const conflictDetails: string[] = [];


  const otherRepresentatives = representatives.filter((_, index) => index !== currentTabId);


  for (const newSchedule of newSchedules) {
    for (const newDaySchedule of newSchedule.dayScheduleRepresentative) {
      if (!newDaySchedule.checked) continue;

      for (let repIndex = 0; repIndex < otherRepresentatives.length; repIndex++) {
        const otherRep = otherRepresentatives[repIndex];
        const actualRepIndex = representatives.findIndex((rep) => rep === otherRep);

        for (const otherSchedule of otherRep.transportSchedulesRepresentative || []) {
          if (otherSchedule.weekType === newSchedule.weekType) {
            for (const otherDaySchedule of otherSchedule.dayScheduleRepresentative) {
              if (
                otherDaySchedule.dayOfWeek === newDaySchedule.dayOfWeek &&
                otherDaySchedule.sens === newDaySchedule.sens
              ) {
                const dayName = getDayName(newDaySchedule.dayOfWeek);
                const weekName = newSchedule.weekType === 'EVEN' ? 'paire' : 'impaire';
                const sensName = newDaySchedule.sens === 'DEPARTURE' ? 'aller' : 'retour';

                conflictDetails.push(
                  `${dayName} ${sensName} (semaine ${weekName}) est déjà sélectionné dans l'Adresse ${actualRepIndex + 1}`
                );
              }
            }
          }
        }
      }
    }
  }

  return {
    hasConflict: conflictDetails.length > 0,
    conflictDetails,
  };
};

export const convertAmendmentPayload = (
  payload: Partial<AmendmentPassengerDetails>,
  isUpdate: boolean
): Partial<AmendmentPassengerDetails> => {
  if (!payload.amendmentRepresentatives) {
    return payload;
  }

  const convertedRepresentatives = payload.amendmentRepresentatives.map(
    (representative: Representative) => {
      const { transportSchedulesRepresentative, ...rest } = representative;

      if (!isUpdate) {
        const { id, ...restWithoutId } = rest;
        return {
          ...restWithoutId,
          transportScheduleRepresentativeAmendments: transportSchedulesRepresentative,
        };
      }

      return {
        ...rest,
        transportScheduleRepresentativeAmendments: transportSchedulesRepresentative,
      };
    }
  );

  return {
    ...payload,
    amendmentRepresentatives: convertedRepresentatives as any,
  };
};

export const convertAmendmentPayloadInverse = (
  payload: Partial<AmendmentPassengerDetails>
): Partial<AmendmentPassengerDetails> => {
  if (!payload.amendmentRepresentatives) {
    return payload;
  }

  const convertedRepresentatives = payload.amendmentRepresentatives.map((representative: any) => {
    const { transportScheduleRepresentativeAmendments, ...rest } = representative;
    return {
      ...rest,
      transportSchedulesRepresentative: transportScheduleRepresentativeAmendments,
    };
  });

  return {
    ...payload,
    amendmentRepresentatives: convertedRepresentatives,
  };
};
