import dayjs from "dayjs"
import {
  AmendmentPassengerScheduleDTO,
  PeriodOfDay,
  type AmendmentPassengerDetails,
  type PassengerScheduleWeekDTO,
} from "@/shared/types/ammendment-passenger"
import { FormikAmendmentPassengerDetails, WeeklyScheduleType } from "../types/formik-types"
import { DaysOfWeek, Representative, SensTrip, TransportSchedule } from "@/shared/types/passenger"
import { ScheduleDTO } from "@/shared/types/amendment"

interface AmendmentRepresentative extends Omit<Representative, 'transportSchedulesRepresentative'> {
  transportScheduleRepresentativeAmendments: Representative['transportSchedulesRepresentative'];
}
export const DAYS_OF_WEEK_FR = ["lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"]

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

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
}
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: createDefaultWeekSchedule(),
    impair: createDefaultWeekSchedule(),
  }

  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: dayjs().hour(8).minute(0) },
              evening: { retour: dayjs().hour(18).minute(0) },
            }
          }

          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: "",
    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 },
          evening: { 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>): Partial<AmendmentPassengerDetails> => {
    if (!payload.amendmentRepresentatives) {
        return payload;
    }

    const convertedRepresentatives: AmendmentRepresentative[] = payload.amendmentRepresentatives.map((representative: Representative) => {
        const { transportSchedulesRepresentative, ...rest } = representative;
        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
    };
};