import { create } from 'zustand';
import axiosInstance from '@/utils/axios';
import {
  IDemandeAbsence,
  LeaveType,
  LeaveStatus,
  AbsenceType,
  DemandeType,
} from '@/shared/types/leave';
import { absenceEndpoints } from '../endpoints/absence';
import {
  IAbsence,
  EtatDemandeRecue,
  EtatDemandeTraitee,
  ReceivingMethod,
} from '@/shared/types/absence';
import { endpoints } from '../endpoints/absence-service';
import { mapSensToConcernedTrip } from '@/shared/types/absence';

// Match the backend's filter structure exactly
interface AgentAbsenceFilter {
  userId?: number;
  id?: string;
  submittedAt?: string;
  requestType?: 'LEAVE' | 'ABSENCE';
  startDate?: string;
  endDate?: string;
  state?: string;
  processedAt?: string;
  leaveType?: string;
  absenceType?: string;
  numberOfDays?: number;
  page?: number;
  size?: number;
  sort?: string[];
}

interface BackendAbsence {
  id: number;
  userId: number;
  user: {
    id: number;
    firstName: string;
    lastName: string;
  };
  requestType: 'LEAVE' | 'ABSENCE' | null;
  absenceType: string | null;
  leaveType: string | null;
  state: string;
  startDate: string;
  endDate: string;
  tripsIds: number[];
  observations: string;
  forecastDate: string;
  comment: string;
  receivingMethod: string;
  submittedAt: string;
  processedAt: string | null;
  numberOfDays: number;
  justificationFileUrl: string | null;
}

interface BackendResponse {
  content: BackendAbsence[];
  totalElements: number;
  totalPages: number;
  size: number;
  number: number;
}

interface Statistics {
  leaveBalance: {
    LEAVE_MALADIE: number;
    LEAVE_PAID: number;
    LEAVE_RTT: number;
  };
  totals: {
    absences: number;
    total: number;
    leaves: number;
  };
  byState: {
    Unprocessed: number;
    Approved: number;
    Rejected: number;
    Waiting: number;
  };
}

const getUserIdFromStorage = (): number | undefined => {
  const userId = localStorage.getItem('userId');
  return userId ? parseInt(userId) : undefined;
};

const mapBackendToFrontend = (backendAbsence: BackendAbsence): IDemandeAbsence => {
  if (!backendAbsence) {
    throw new Error('Invalid backend data');
  }

  // Map backend status to frontend status
  let status: LeaveStatus;
  switch (backendAbsence.state) {
    case 'Waiting':
    case 'Unprocessed':
      status = 'En attente';
      break;
    case 'Approved':
      status = 'Approuvée';
      break;
    case 'Rejected':
      status = 'Refusée';
      break;
    default:
      status = 'En attente';
  }

  // Determine request type
  let requestType: DemandeType;
  if (backendAbsence.requestType === 'LEAVE') {
    requestType = 'Congé';
  } else if (backendAbsence.requestType === 'ABSENCE') {
    requestType = 'Absence';
  } else {
    requestType = backendAbsence.leaveType ? 'Congé' : 'Absence';
  }

  // Map backend leave type to enum
  let leaveType: LeaveType | '' = '';
  if (backendAbsence.leaveType) {
    switch (backendAbsence.leaveType) {
      case 'LEAVE_SICK':
        leaveType = LeaveType.SICK;
        break;
      case 'LEAVE_PARENTAL':
        leaveType = LeaveType.PARENTAL;
        break;
      case 'LEAVE_PAID':
        leaveType = LeaveType.PAID;
        break;
      case 'LEAVE_UNPAID':
        leaveType = LeaveType.UNPAID;
        break;
      case 'LEAVE_RTT':
        leaveType = LeaveType.RTT;
        break;
    }
  }

  const mappedData = {
    id: backendAbsence.id?.toString() || '',
    isActive: true,
    requestNumber: backendAbsence.id ? `REQ-${backendAbsence.id}` : '',
    submissionDate: backendAbsence.submittedAt || new Date().toISOString().split('T')[0],
    requestType,
    leaveType,
    absenceType: backendAbsence.absenceType || '',
    startDate: backendAbsence.startDate || new Date().toISOString().split('T')[0],
    endDate: backendAbsence.endDate || new Date().toISOString().split('T')[0],
    status,
    processDate: backendAbsence.processedAt || '',
    reason: backendAbsence.observations || '',
    daysCount: backendAbsence.numberOfDays || 0,
    employeeId: backendAbsence.userId?.toString() || '',
    dateDebut: backendAbsence.startDate || new Date().toISOString().split('T')[0],
    dateFin: backendAbsence.endDate || new Date().toISOString().split('T')[0],
    dateSubmission: backendAbsence.submittedAt || new Date().toISOString().split('T')[0],
    dateTraitement: backendAbsence.processedAt || '',
  };

  return mappedData;
};

const mapFrontendToBackend = (frontendAbsence: IDemandeAbsence): Partial<BackendAbsence> => {
  const backendRequestType: 'LEAVE' | 'ABSENCE' =
    frontendAbsence.requestType === 'Congé' ? 'LEAVE' : 'ABSENCE';

  let backendState: string;
  switch (frontendAbsence.status) {
    case 'En attente':
      backendState = 'Waiting';
      break;
    case 'Approuvée':
      backendState = 'Approved';
      break;
    case 'Refusée':
      backendState = 'Rejected';
      break;
    default:
      backendState = 'Waiting';
  }

  const userId = getUserIdFromStorage();
  if (!userId) {
    throw new Error('User ID not found in storage');
  }

  const mappedData = {
    userId: userId,
    requestType: backendRequestType,
    absenceType: backendRequestType === 'ABSENCE' ? frontendAbsence.absenceType : null,
    leaveType: backendRequestType === 'LEAVE' ? frontendAbsence.leaveType : null,
    state: backendState,
    startDate: frontendAbsence.startDate,
    endDate: frontendAbsence.endDate,
    tripsIds: [],
    observations: frontendAbsence.reason || '',
    forecastDate: frontendAbsence.startDate,
    comment: '',
    receivingMethod: 'APPLICATION',
    submittedAt: new Date().toISOString().split('T')[0],
    processedAt: null,
    numberOfDays: parseInt(frontendAbsence.daysCount.toString()),
  };

  return mappedData;
};

type AbsenceStoree = {
  absences: IDemandeAbsence[];
  userAbsences: IDemandeAbsence[];
  loading: boolean;
  error: string | null;
  pagination: {
    page: number;
    size: number;
    total: number;
  };
  statistics: Statistics | null;

  searchAbsences: (
    filter: AgentAbsenceFilter,
    page: number,
    size: number,
    sort?: string[]
  ) => Promise<void>;
  getUserAbsences: (userId: number) => Promise<void>;
  createAbsence: (
    absence: IDemandeAbsence,
    file?: File
  ) => Promise<{ success: boolean; data: IDemandeAbsence; message: string }>;
  updateAbsence: (
    id: string,
    absence: IDemandeAbsence,
    file?: File
  ) => Promise<{ success: boolean; data: IDemandeAbsence; message: string }>;
  setLoading: (loading: boolean) => void;
  setError: (error: string | null) => void;
  fetchStatistics: (userId?: number) => Promise<void>;
};

export const useAbsenceStoree = create<AbsenceStoree>((set, get) => ({
  absences: [],
  userAbsences: [],
  loading: false,
  error: null,
  pagination: {
    page: 0,
    size: 10,
    total: 0,
  },
  statistics: null,

  setLoading: (loading: boolean) => set({ loading }),
  setError: (error: string | null) => set({ error }),

  searchAbsences: async (
    filter: AgentAbsenceFilter,
    page: number,
    size: number,
    sort?: string[]
  ) => {
    set({ loading: true, error: null });
    try {
      const userId = getUserIdFromStorage();
      if (!userId) {
        throw new Error('User ID not found in storage');
      }

      const params = {
        ...filter,
        userId,
        page,
        size,
        sort: sort ? sort.join(',') : undefined,
      };

      const response = await axiosInstance.get<BackendResponse>(absenceEndpoints.agent.search, {
        params,
      });

      const mappedData = response.data.content.map(mapBackendToFrontend);

      set({
        absences: mappedData,
        pagination: {
          page: response.data.number,
          size: response.data.size,
          total: response.data.totalElements,
        },
        loading: false,
      });
    } catch (error: any) {
      set({ error: error.message || 'Failed to fetch absences', loading: false });
    }
  },

  getUserAbsences: async (userId: number) => {
    set({ loading: true, error: null });
    try {
      const params = {
        userId: userId,
        page: 0,
        size: 10,
      };

      const response = await axiosInstance.get<BackendResponse>(absenceEndpoints.common.search, {
        params,
      });
      set({ userAbsences: response.data.content.map(mapBackendToFrontend), loading: false });
    } catch (error: any) {
      set({ error: error.message || 'Failed to fetch user absences', loading: false });
    }
  },

  createAbsence: async (absence: IDemandeAbsence, file?: File) => {
    set({ loading: true, error: null });
    try {
      const backendData = mapFrontendToBackend({
        ...absence,
        id: '',
        requestNumber: '',
        processDate: '',
        dateTraitement: '',
      });

      const formData = new FormData();
      formData.append('dto', JSON.stringify(backendData));

      if (file) {
        formData.append('file', file);
      }

      const response = await axiosInstance.post<BackendAbsence>(
        absenceEndpoints.agent.create,
        formData,
        {
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        }
      );

      if (response.data) {
        const mappedResponse = mapBackendToFrontend(response.data);

        set({ loading: false });

        return {
          success: true,
          data: mappedResponse,
          message: "Demande d'absence ajoutée avec succès",
        };
      }

      throw new Error('No data received from server');
    } catch (error: any) {
      set({ error: error.message || 'Failed to create absence', loading: false });
      return {
        success: false,
        data: absence,
        message: error.response?.data?.message || 'Échec de la création de la demande',
      };
    }
  },

  updateAbsence: async (id: string, absence: IDemandeAbsence, file?: File) => {
    set({ loading: true, error: null });
    try {
      const formData = new FormData();
      const backendData = mapFrontendToBackend(absence);
      formData.append('dto', JSON.stringify(backendData));

      if (file) {
        formData.append('file', file);
      }

      const response = await axiosInstance.put<BackendAbsence>(
        absenceEndpoints.agent.update(id),
        formData,
        {
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        }
      );

      if (response.data) {
        const mappedResponse = mapBackendToFrontend(response.data);

        set({ loading: false });
        return {
          success: true,
          data: mappedResponse,
          message: "Demande d'absence mise à jour avec succès",
        };
      }

      throw new Error('No data received from server');
    } catch (error: any) {
      set({ error: error.message || 'Failed to update absence', loading: false });
      return {
        success: false,
        data: absence,
        message: error.response?.data?.message || 'Échec de la mise à jour de la demande',
      };
    }
  },

  fetchStatistics: async (userId?: number) => {
    set({ loading: true, error: null });
    try {
      const effectiveUserId = userId || getUserIdFromStorage();
      if (!effectiveUserId) {
        throw new Error('User ID not found');
      }

      const response = await axiosInstance.get<Statistics>(
        absenceEndpoints.agent.statistics(effectiveUserId)
      );

      set({
        statistics: response.data,
        loading: false,
      });
    } catch (error: any) {
      set({
        error: error.message || 'Failed to fetch statistics',
        loading: false,
      });
    }
  },
}));

interface AbsencePageFilterParams {
  page?: number;
  size?: number;
  sortBy?: string;
  sortDirection?: 'asc' | 'desc';
  state?: string[];
  passengerId?: number;
  receivingMethod?: string;
  establishment?: string;
  departmentName?: string;
  lastName?: string;
  firstName?: string;
  startDateFrom?: string;
  startDateTo?: string;
}

interface PassengerResponse {
  content: Array<{
    id: number;
    firstName: string;
    lastName: string;
    establishmentName: string;
    departmentName: string;
  }>;
}

interface ExportFilters {
  firstName?: string;
  lastName?: string;
  state?: string;
  startDate?: string;
  endDate?: string;
  reportedBy?: string;
  userId?: string;
}

// Map frontend state to backend state
const mapEtatToBackendState = (etat: string): string => {
  switch (etat) {
    case 'En cours':
      return 'In_progress';
    case 'Non traité':
      return 'Unprocessed';
    case 'Annulé':
      return 'Cancelled';
    case 'Traité':
      return 'Processed';
    default:
      return 'Unprocessed';
  }
};

type AbsenceStore = {
  loading: boolean;
  error: string | null;
  unprocessedAbsences: IAbsence[];
  processedAbsences: IAbsence[];
  passengers: {
    id: number;
    value: string;
    label: string;
    establishmentName: string;
    departmentName: string;
  }[];
  fetchUnprocessedAbsences: (params?: AbsencePageFilterParams) => Promise<void>;
  fetchProcessedAbsences: (params?: AbsencePageFilterParams) => Promise<void>;
  fetchPassengers: () => Promise<void>;
  absences: IAbsence[];
  fetchAbsences: () => Promise<void>;
  absenceById: IAbsence | null;
  getAbsenceById: (id: string) => Promise<IAbsence>;
  updateAbsence: (id: number, absence: Partial<IAbsence>) => Promise<void>;
  createAbsence: (createData: any) => Promise<IAbsence>;
  startExportAbsences: (params?: any) => Promise<any>;
};

export const useAbsenceStore = create<AbsenceStore>((set, get) => ({
  loading: false,
  error: null,
  unprocessedAbsences: [],
  processedAbsences: [],
  passengers: [],
  fetchPassengers: async () => {
    try {
      const response = await axiosInstance.get<PassengerResponse>(endpoints.User.searchPassengers);
      set((state) => ({
        ...state,
        passengers: response.data.content.map((passenger) => ({
          id: passenger.id,
          value: passenger.id.toString(),
          label: `${passenger.firstName} ${passenger.lastName}`,
          establishmentName: passenger.establishmentName,
          departmentName: passenger.departmentName,
        })),
      }));
    } catch (error) {
      console.error('Failed to fetch passengers:', error);
    }
  },
  fetchUnprocessedAbsences: async (params?: AbsencePageFilterParams) => {
    set({ loading: true, error: null });
    try {
      const response = await axiosInstance.get(endpoints.Absence.getAbsences, {
        params: {
          ...params,
          state: ['Unprocessed', 'In_progress'],
        },
        paramsSerializer: {
          indexes: null, // This will format arrays as repeated params instead of using brackets
        },
      });
      set({
        unprocessedAbsences: response.data.content,
        loading: false,
      });
    } catch (error: any) {
      set({
        error: error.message || 'Failed to fetch unprocessed absences',
        loading: false,
      });
    }
  },
  fetchProcessedAbsences: async (params?: AbsencePageFilterParams) => {
    set({ loading: true, error: null });
    try {
      const response = await axiosInstance.get(endpoints.Absence.getAbsences, {
        params: {
          ...params,
          state: ['Processed', 'Cancelled'],
        },
        paramsSerializer: {
          indexes: null, // This will format arrays as repeated params instead of using brackets
        },
      });
      set({
        processedAbsences: response.data.content,
        loading: false,
      });
    } catch (error: any) {
      set({
        error: error.message || 'Failed to fetch processed absences',
        loading: false,
      });
    }
  },
  absences: [],
  fetchAbsences: async () => {
    set({ loading: true, error: null });
    try {
      const response = await axiosInstance.get(endpoints.Absence.getAbsences);
      set({
        absences: response.data.content,
        loading: false,
      });
    } catch (error: any) {
      set({
        error: error.message || 'Failed to fetch absences',
        loading: false,
      });
    }
  },
  absenceById: null,
  getAbsenceById: async (id: string) => {
    set({ loading: true, error: null });
    try {
      const response = await axiosInstance.get(endpoints.Absence.getAbsenceById(id));
      set({
        absenceById: response.data,
        loading: false,
      });
      return response.data;
    } catch (error: any) {
      set({
        error: error.message || 'Failed to get absence by id',
        loading: false,
      });
    }
  },
  updateAbsence: async (id: number, absence: Partial<IAbsence>) => {
    set({ loading: true, error: null });
    try {
      await axiosInstance.put(endpoints.Absence.updateAbsence(id), absence);

      await Promise.all([get().fetchUnprocessedAbsences(), get().fetchProcessedAbsences()]);

      set({ loading: false });
    } catch (error: any) {
      set({
        error: error.message || 'Failed to update absence',
        loading: false,
      });
      throw error;
    }
  },
  createAbsence: async (createData: any) => {
    set({ loading: true, error: null });
    try {
      const response = await axiosInstance.post(endpoints.Absence.createAbsence, createData);
      await Promise.all([get().fetchUnprocessedAbsences(), get().fetchProcessedAbsences()]);

      set({ loading: false });
      return response.data;
    } catch (error: any) {
      console.error('Error creating absence:', error); // Debug log
      set({
        error: error.message || 'Failed to create absence',
        loading: false,
      });
      throw error;
    }
  },
  startExportAbsences: async (params?: any) => {
    set({ loading: true, error: null });
    try {
      const response = await axiosInstance.post(endpoints.Absence.exportAbsence, { params });
      set({ loading: false });
      return response.data;
    } catch (error: any) {
      set({
        error: error.message || 'Failed to start export absences',
        loading: false,
      });
      throw error;
    }
  },
}));
