import {
  CalculateLeaveDaysPayload,
  CalculateLeaveDaysResult,
  CreateLeaveRequestPayload,
  DeleteLeaveRequestPayload,
  LeaveRequest,
  LeaveRequestActionResponse,
  LeaveRequestFilters,
  LeaveRequestListResponse,
  LeaveRequestSingleResponse,
} from '@/interface/LeaveRequest';
import { RejectedActionPayload, ServerErrorResponse } from '@/interface/Shared';
import { apiDelete, apiGet, apiPost } from '@/services/api/api';
import Helpers from '@/utilities/Helpers';
import { createAsyncThunk, createSlice, Draft, PayloadAction } from '@reduxjs/toolkit';
import { message } from 'antd';
import axios, { AxiosResponse } from 'axios';
import { LeaveRequestsActions } from './actionTypes';

interface LeaveRequestsState {
  leaveRequests: LeaveRequest[];
  myLeaveRequests: LeaveRequest[];
  selectedLeaveRequest: LeaveRequest | null;
  calculation: CalculateLeaveDaysResult['data'] | null;
  loading: boolean;
  actionLoading: boolean;
  error: string | null;
}

const initialState: LeaveRequestsState = {
  leaveRequests: [],
  myLeaveRequests: [],
  selectedLeaveRequest: null,
  calculation: null,
  loading: false,
  actionLoading: false,
  error: null,
};

const buildQueryString = (filters?: LeaveRequestFilters): string => {
  if (!filters) {
    return '';
  }

  const params = new URLSearchParams();

  if (filters.status) params.set('status', filters.status);
  if (filters.employee_id) params.set('employee_id', filters.employee_id);
  if (filters.leave_type_id) params.set('leave_type_id', filters.leave_type_id);
  if (filters.year) params.set('year', String(filters.year));
  if (filters.branch_id) params.set('branch_id', filters.branch_id);
  if (filters.date_from) params.set('date_from', filters.date_from);
  if (filters.date_to) params.set('date_to', filters.date_to);
  if (filters.mine) params.set('mine', 'true');
  if (filters.search) params.set('search', filters.search);
  if (filters.per_page) params.set('per_page', String(filters.per_page));

  const query = params.toString();
  return query ? `?${query}` : '';
};

const extractCollectionData = (payload: LeaveRequest[] | LeaveRequestListResponse): LeaveRequest[] => {
  return Array.isArray(payload) ? payload : payload.data;
};

const toFormData = (payload: Partial<CreateLeaveRequestPayload>): FormData => {
  const formData = new FormData();

  Object.entries(payload).forEach(([key, value]) => {
    if (value === undefined || value === null) {
      return;
    }

    if (key === 'attachment' && value instanceof File) {
      formData.append(key, value);
      return;
    }

    formData.append(key, String(value));
  });

  return formData;
};

const replaceInCollection = (collection: LeaveRequest[], updated: LeaveRequest): LeaveRequest[] => {
  const index = collection.findIndex((item) => item.id === updated.id);

  if (index === -1) {
    return collection;
  }

  const next = [...collection];
  next[index] = updated;
  return next;
};

export const fetchLeaveRequests = createAsyncThunk(
  LeaveRequestsActions.FETCH_LEAVE_REQUESTS_REQUEST,
  async (filters?: LeaveRequestFilters) => {
    const response = (await apiGet(
      `/leave/requests${buildQueryString(filters)}`,
    )) as AxiosResponse<LeaveRequest[] | LeaveRequestListResponse>;

    return extractCollectionData(response.data);
  },
);

export const fetchMyLeaveRequests = createAsyncThunk(
  LeaveRequestsActions.FETCH_MY_LEAVE_REQUESTS_REQUEST,
  async (filters?: Omit<LeaveRequestFilters, 'mine'>) => {
    const response = (await apiGet(
      `/leave/requests${buildQueryString({ ...(filters || {}), mine: true })}`,
    )) as AxiosResponse<LeaveRequest[] | LeaveRequestListResponse>;

    return extractCollectionData(response.data);
  },
);

export const fetchLeaveRequest = createAsyncThunk(
  LeaveRequestsActions.FETCH_LEAVE_REQUEST_REQUEST,
  async (id: string, { rejectWithValue }) => {
    try {
      const response = (await apiGet(`/leave/requests/${id}`)) as AxiosResponse<LeaveRequestSingleResponse>;
      return response.data.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const createLeaveRequest = createAsyncThunk(
  LeaveRequestsActions.CREATE_LEAVE_REQUEST_REQUEST,
  async (payload: FormData | CreateLeaveRequestPayload, { rejectWithValue }) => {
    try {
      const body = payload instanceof FormData ? payload : toFormData(payload);

      const response = (await apiPost('/leave/requests', body, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      })) as AxiosResponse<LeaveRequestActionResponse>;

      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const updateLeaveRequest = createAsyncThunk(
  LeaveRequestsActions.UPDATE_LEAVE_REQUEST_REQUEST,
  async (
    payload: { id: string; payload: FormData | Partial<CreateLeaveRequestPayload> },
    { rejectWithValue },
  ) => {
    try {
      const body = payload.payload instanceof FormData ? payload.payload : toFormData(payload.payload);
      body.append('_method', 'PUT');

      const response = (await apiPost(`/leave/requests/${payload.id}`, body, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      })) as AxiosResponse<LeaveRequestActionResponse>;

      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const deleteLeaveRequest = createAsyncThunk(
  LeaveRequestsActions.DELETE_LEAVE_REQUEST_REQUEST,
  async (id: string) => {
    const response = (await apiDelete(`/leave/requests/${id}`)) as AxiosResponse<DeleteLeaveRequestPayload>;
    const responseData = response.data;
    responseData.id = id;
    return responseData;
  },
);

export const submitLeaveRequest = createAsyncThunk(
  LeaveRequestsActions.SUBMIT_LEAVE_REQUEST_REQUEST,
  async (id: string, { rejectWithValue }) => {
    try {
      const response = (await apiPost(`/leave/requests/${id}/submit`, {})) as AxiosResponse<LeaveRequestActionResponse>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const cancelLeaveRequest = createAsyncThunk(
  LeaveRequestsActions.CANCEL_LEAVE_REQUEST_REQUEST,
  async (id: string, { rejectWithValue }) => {
    try {
      const response = (await apiPost(`/leave/requests/${id}/cancel`, {})) as AxiosResponse<LeaveRequestActionResponse>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const recallLeaveRequest = createAsyncThunk(
  LeaveRequestsActions.RECALL_LEAVE_REQUEST_REQUEST,
  async (id: string, { rejectWithValue }) => {
    try {
      const response = (await apiPost(`/leave/requests/${id}/recall`, {})) as AxiosResponse<LeaveRequestActionResponse>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const calculateLeaveDays = createAsyncThunk(
  LeaveRequestsActions.CALCULATE_LEAVE_DAYS_REQUEST,
  async (payload: CalculateLeaveDaysPayload, { rejectWithValue }) => {
    try {
      const response = (await apiPost('/leave/calculate-days', payload)) as AxiosResponse<CalculateLeaveDaysResult>;
      return response.data.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const LeaveRequestsSlice = createSlice({
  name: 'leaveRequests',
  initialState,
  reducers: {},
  extraReducers(builder) {
    builder
      .addCase(fetchLeaveRequests.pending, (state: Draft<LeaveRequestsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchLeaveRequests.fulfilled, (state: Draft<LeaveRequestsState>, action: PayloadAction<LeaveRequest[]>) => {
        state.leaveRequests = action.payload;
        state.loading = false;
        state.error = null;
      })
      .addCase(fetchLeaveRequests.rejected, (state: Draft<LeaveRequestsState>, action: RejectedActionPayload) => {
        state.loading = false;
        message.error(Helpers.handleServerError(action.payload), 8);
      });

    builder
      .addCase(fetchMyLeaveRequests.pending, (state: Draft<LeaveRequestsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchMyLeaveRequests.fulfilled, (state: Draft<LeaveRequestsState>, action: PayloadAction<LeaveRequest[]>) => {
        state.myLeaveRequests = action.payload;
        state.loading = false;
        state.error = null;
      })
      .addCase(fetchMyLeaveRequests.rejected, (state: Draft<LeaveRequestsState>, action: RejectedActionPayload) => {
        state.loading = false;
        message.error(Helpers.handleServerError(action.payload), 8);
      });

    builder
      .addCase(fetchLeaveRequest.pending, (state: Draft<LeaveRequestsState>) => {
        state.actionLoading = true;
        state.error = null;
      })
      .addCase(fetchLeaveRequest.fulfilled, (state: Draft<LeaveRequestsState>, action: PayloadAction<LeaveRequest>) => {
        state.selectedLeaveRequest = action.payload;
        state.actionLoading = false;
        state.error = null;
      })
      .addCase(fetchLeaveRequest.rejected, (state: Draft<LeaveRequestsState>, action: RejectedActionPayload) => {
        state.actionLoading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(state.error, 10);
      });

    builder
      .addCase(createLeaveRequest.pending, (state: Draft<LeaveRequestsState>) => {
        state.actionLoading = true;
        state.error = null;
      })
      .addCase(
        createLeaveRequest.fulfilled,
        (state: Draft<LeaveRequestsState>, action: PayloadAction<LeaveRequestActionResponse>) => {
          state.actionLoading = false;
          state.error = null;
          state.leaveRequests.unshift(action.payload.data);
          state.myLeaveRequests.unshift(action.payload.data);
          state.selectedLeaveRequest = action.payload.data;
          message.success(action.payload.message || 'Leave request saved successfully.');
        },
      )
      .addCase(createLeaveRequest.rejected, (state: Draft<LeaveRequestsState>, action: RejectedActionPayload) => {
        state.actionLoading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(state.error, 10);
      });

    builder
      .addCase(updateLeaveRequest.pending, (state: Draft<LeaveRequestsState>) => {
        state.actionLoading = true;
        state.error = null;
      })
      .addCase(
        updateLeaveRequest.fulfilled,
        (state: Draft<LeaveRequestsState>, action: PayloadAction<LeaveRequestActionResponse>) => {
          state.actionLoading = false;
          state.error = null;
          state.leaveRequests = replaceInCollection(state.leaveRequests, action.payload.data);
          state.myLeaveRequests = replaceInCollection(state.myLeaveRequests, action.payload.data);
          state.selectedLeaveRequest = action.payload.data;
          message.success(action.payload.message || 'Leave request updated successfully.');
        },
      )
      .addCase(updateLeaveRequest.rejected, (state: Draft<LeaveRequestsState>, action: RejectedActionPayload) => {
        state.actionLoading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(state.error, 10);
      });

    builder
      .addCase(deleteLeaveRequest.pending, (state: Draft<LeaveRequestsState>) => {
        state.actionLoading = true;
      })
      .addCase(
        deleteLeaveRequest.fulfilled,
        (state: Draft<LeaveRequestsState>, action: PayloadAction<DeleteLeaveRequestPayload>) => {
          state.actionLoading = false;
          state.leaveRequests = state.leaveRequests.filter((item) => item.id !== action.payload.id);
          state.myLeaveRequests = state.myLeaveRequests.filter((item) => item.id !== action.payload.id);

          if (state.selectedLeaveRequest?.id === action.payload.id) {
            state.selectedLeaveRequest = null;
          }

          message.success(action.payload.message || 'Leave request deleted successfully.');
        },
      )
      .addCase(deleteLeaveRequest.rejected, (state: Draft<LeaveRequestsState>, action: RejectedActionPayload) => {
        state.actionLoading = false;
        message.error(Helpers.handleServerError(action.payload), 8);
      });

    builder
      .addCase(submitLeaveRequest.pending, (state: Draft<LeaveRequestsState>) => {
        state.actionLoading = true;
        state.error = null;
      })
      .addCase(
        submitLeaveRequest.fulfilled,
        (state: Draft<LeaveRequestsState>, action: PayloadAction<LeaveRequestActionResponse>) => {
          state.actionLoading = false;
          state.error = null;
          state.leaveRequests = replaceInCollection(state.leaveRequests, action.payload.data);
          state.myLeaveRequests = replaceInCollection(state.myLeaveRequests, action.payload.data);
          state.selectedLeaveRequest = action.payload.data;
          message.success(action.payload.message || 'Leave request submitted successfully.');
        },
      )
      .addCase(submitLeaveRequest.rejected, (state: Draft<LeaveRequestsState>, action: RejectedActionPayload) => {
        state.actionLoading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(state.error, 10);
      });

    builder
      .addCase(cancelLeaveRequest.pending, (state: Draft<LeaveRequestsState>) => {
        state.actionLoading = true;
        state.error = null;
      })
      .addCase(
        cancelLeaveRequest.fulfilled,
        (state: Draft<LeaveRequestsState>, action: PayloadAction<LeaveRequestActionResponse>) => {
          state.actionLoading = false;
          state.error = null;
          state.leaveRequests = replaceInCollection(state.leaveRequests, action.payload.data);
          state.myLeaveRequests = replaceInCollection(state.myLeaveRequests, action.payload.data);
          state.selectedLeaveRequest = action.payload.data;
          message.success(action.payload.message || 'Leave request cancelled successfully.');
        },
      )
      .addCase(cancelLeaveRequest.rejected, (state: Draft<LeaveRequestsState>, action: RejectedActionPayload) => {
        state.actionLoading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(state.error, 10);
      });

    builder
      .addCase(recallLeaveRequest.pending, (state: Draft<LeaveRequestsState>) => {
        state.actionLoading = true;
        state.error = null;
      })
      .addCase(
        recallLeaveRequest.fulfilled,
        (state: Draft<LeaveRequestsState>, action: PayloadAction<LeaveRequestActionResponse>) => {
          state.actionLoading = false;
          state.error = null;
          state.leaveRequests = replaceInCollection(state.leaveRequests, action.payload.data);
          state.myLeaveRequests = replaceInCollection(state.myLeaveRequests, action.payload.data);
          state.selectedLeaveRequest = action.payload.data;
          message.success(action.payload.message || 'Leave request recalled successfully.');
        },
      )
      .addCase(recallLeaveRequest.rejected, (state: Draft<LeaveRequestsState>, action: RejectedActionPayload) => {
        state.actionLoading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(state.error, 10);
      });

    builder
      .addCase(calculateLeaveDays.pending, (state: Draft<LeaveRequestsState>) => {
        state.actionLoading = true;
        state.error = null;
      })
      .addCase(
        calculateLeaveDays.fulfilled,
        (state: Draft<LeaveRequestsState>, action: PayloadAction<CalculateLeaveDaysResult['data']>) => {
          state.actionLoading = false;
          state.error = null;
          state.calculation = action.payload;
        },
      )
      .addCase(calculateLeaveDays.rejected, (state: Draft<LeaveRequestsState>, action: RejectedActionPayload) => {
        state.actionLoading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(state.error, 10);
      });
  },
});

export default LeaveRequestsSlice;
export type { LeaveRequestsState };
