import { RejectedActionPayload, ServerErrorResponse } from '@/interface/Shared';
import { Applicant } from '@/interface/Recruitment';
import Helpers from '@/utilities/Helpers';
import { createAsyncThunk, createSlice, Draft, PayloadAction } from '@reduxjs/toolkit';
import { message } from 'antd';
import axios, { AxiosResponse } from 'axios';
import { apiDelete, apiDownloadBlob, apiGet, apiPost, apiPut, axiosInstance, API_ENDPOINT } from '@/services/api/api';
import { ApplicantsActions } from './actionTypes';

interface ApplicantsState {
  applicants: Applicant[];
  applicant: Applicant | null;
  meta: any;
  loading: boolean;
  actionLoading: boolean;
  error: string | null;
}

const initialState: ApplicantsState = {
  applicants: [],
  applicant: null,
  meta: null,
  loading: false,
  actionLoading: false,
  error: null,
};

export const fetchApplicants = createAsyncThunk(
  ApplicantsActions.FETCH_APPLICANTS,
  async (
    params?: {
      job_opening_id?: string;
      status?: string;
      search?: string;
      per_page?: number;
      page?: number;
    },
    { rejectWithValue },
  ) => {
    try {
      const queryParams = new URLSearchParams(
        Object.entries(params ?? {})
          .filter(([, v]) => v !== undefined && v !== null)
          .map(([k, v]) => [k, String(v)]),
      );
      const query = queryParams.toString() ? `?${queryParams.toString()}` : '';
      const response = (await apiGet(`/recruitment/applicants${query}`)) as AxiosResponse<{
        data: Applicant[];
        meta: any;
      }>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const fetchApplicant = createAsyncThunk(
  ApplicantsActions.FETCH_APPLICANT,
  async (id: string, { rejectWithValue }) => {
    try {
      const response = (await apiGet(`/recruitment/applicants/${id}`)) as AxiosResponse<{
        data: Applicant;
      }>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const createApplicant = createAsyncThunk(
  ApplicantsActions.CREATE_APPLICANT,
  async (formData: FormData, { rejectWithValue }) => {
    try {
      const response = (await apiPost('/recruitment/applicants', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
      })) as AxiosResponse<{ data: Applicant }>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const updateApplicant = createAsyncThunk(
  ApplicantsActions.UPDATE_APPLICANT,
  async ({ id, ...rest }: { id: string } & any, { rejectWithValue }) => {
    try {
      const response = (await apiPut(
        `/recruitment/applicants/${id}`,
        rest,
      )) as AxiosResponse<{ data: Applicant }>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const deleteApplicant = createAsyncThunk(
  ApplicantsActions.DELETE_APPLICANT,
  async (id: string, { rejectWithValue }) => {
    try {
      await apiDelete(`/recruitment/applicants/${id}`);
      return id;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const advanceApplicantStatus = createAsyncThunk(
  ApplicantsActions.ADVANCE_STATUS,
  async (
    {
      applicantId,
      applicationId,
      new_status,
      rejection_reason,
      rejection_notes,
    }: {
      applicantId: string;
      applicationId: string;
      new_status: string;
      rejection_reason?: string;
      rejection_notes?: string;
    },
    { rejectWithValue },
  ) => {
    try {
      const response = (await apiPost(
        `/recruitment/applicants/${applicantId}/applications/${applicationId}/advance`,
        { new_status, rejection_reason, rejection_notes },
      )) as AxiosResponse<{ data: Applicant }>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const downloadApplicantResume = createAsyncThunk(
  ApplicantsActions.DOWNLOAD_RESUME,
  async (applicantId: string, { rejectWithValue }) => {
    try {
      const response = await apiDownloadBlob(`/recruitment/applicants/${applicantId}/download-resume`);
      return response;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const fetchApplicantAttachments = createAsyncThunk(
  ApplicantsActions.FETCH_ATTACHMENTS,
  async (applicantId: string, { rejectWithValue }) => {
    try {
      const response = (await apiGet(
        `/recruitment/applicants/${applicantId}/attachments`,
      )) as AxiosResponse<{ data: any[] }>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const uploadApplicantAttachment = createAsyncThunk(
  ApplicantsActions.UPLOAD_ATTACHMENT,
  async (
    { applicantId, formData }: { applicantId: string; formData: FormData },
    { rejectWithValue },
  ) => {
    try {
      const response = (await apiPost(
        `/recruitment/applicants/${applicantId}/attachments`,
        formData,
        { headers: { 'Content-Type': 'multipart/form-data' } },
      )) as AxiosResponse<{ data: any }>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const deleteApplicantAttachment = createAsyncThunk(
  ApplicantsActions.DELETE_ATTACHMENT,
  async (
    {
      applicantId,
      type,
      path,
      delete_file,
    }: { applicantId: string; type: string; path: string; delete_file?: boolean },
    { rejectWithValue },
  ) => {
    try {
      const response = await axiosInstance.delete(
        `${API_ENDPOINT}/recruitment/applicants/${applicantId}/attachments`,
        { data: { type, path, delete_file } },
      );
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const ApplicantsSlice = createSlice({
  name: 'applicants',
  initialState,
  reducers: {
    clearApplicant(state: Draft<ApplicantsState>) {
      state.applicant = null;
    },
  },
  extraReducers(builder) {
    builder
      .addCase(fetchApplicants.pending, (state: Draft<ApplicantsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(
        fetchApplicants.fulfilled,
        (
          state: Draft<ApplicantsState>,
          action: PayloadAction<{ data: Applicant[]; meta: any }>,
        ) => {
          state.applicants = action.payload.data;
          state.meta = action.payload.meta;
          state.loading = false;
          state.error = null;
        },
      )
      .addCase(
        fetchApplicants.rejected,
        (state: Draft<ApplicantsState>, action: RejectedActionPayload) => {
          state.loading = false;
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

    builder
      .addCase(fetchApplicant.pending, (state: Draft<ApplicantsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(
        fetchApplicant.fulfilled,
        (state: Draft<ApplicantsState>, action: PayloadAction<{ data: Applicant }>) => {
          state.applicant = action.payload.data;
          state.loading = false;
          state.error = null;
        },
      )
      .addCase(fetchApplicant.rejected, (state: Draft<ApplicantsState>) => {
        state.loading = false;
      });

    builder
      .addCase(createApplicant.pending, (state: Draft<ApplicantsState>) => {
        state.actionLoading = true;
        state.error = null;
      })
      .addCase(
        createApplicant.fulfilled,
        (state: Draft<ApplicantsState>, action: PayloadAction<{ data: Applicant }>) => {
          state.applicants.unshift(action.payload.data);
          state.actionLoading = false;
          state.error = null;
        },
      )
      .addCase(
        createApplicant.rejected,
        (state: Draft<ApplicantsState>, action: RejectedActionPayload) => {
          state.actionLoading = false;
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

    builder
      .addCase(updateApplicant.pending, (state: Draft<ApplicantsState>) => {
        state.actionLoading = true;
        state.error = null;
      })
      .addCase(
        updateApplicant.fulfilled,
        (state: Draft<ApplicantsState>, action: PayloadAction<{ data: Applicant }>) => {
          const updated = action.payload.data;
          const index = state.applicants.findIndex((a) => a.id === updated.id);
          if (index !== -1) {
            state.applicants[index] = updated;
          }
          if (state.applicant?.id === updated.id) {
            state.applicant = updated;
          }
          state.actionLoading = false;
          state.error = null;
        },
      )
      .addCase(
        updateApplicant.rejected,
        (state: Draft<ApplicantsState>, action: RejectedActionPayload) => {
          state.actionLoading = false;
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

    builder
      .addCase(deleteApplicant.pending, (state: Draft<ApplicantsState>) => {
        state.actionLoading = true;
        state.error = null;
      })
      .addCase(
        deleteApplicant.fulfilled,
        (state: Draft<ApplicantsState>, action: PayloadAction<string>) => {
          state.applicants = state.applicants.filter((a) => a.id !== action.payload);
          state.actionLoading = false;
          state.error = null;
        },
      )
      .addCase(
        deleteApplicant.rejected,
        (state: Draft<ApplicantsState>, action: RejectedActionPayload) => {
          state.actionLoading = false;
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

    builder
      .addCase(advanceApplicantStatus.pending, (state: Draft<ApplicantsState>) => {
        state.actionLoading = true;
        state.error = null;
      })
      .addCase(
        advanceApplicantStatus.fulfilled,
        (state: Draft<ApplicantsState>, action: PayloadAction<{ data: Applicant }>) => {
          const updated = action.payload.data;
          const index = state.applicants.findIndex((a) => a.id === updated.id);
          if (index !== -1) {
            state.applicants[index] = updated;
          }
          if (state.applicant?.id === updated.id) {
            state.applicant = updated;
          }
          state.actionLoading = false;
          state.error = null;
        },
      )
      .addCase(
        advanceApplicantStatus.rejected,
        (state: Draft<ApplicantsState>, action: RejectedActionPayload) => {
          state.actionLoading = false;
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

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

    builder
      .addCase(fetchApplicantAttachments.pending, (state: Draft<ApplicantsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchApplicantAttachments.fulfilled, (state: Draft<ApplicantsState>) => {
        state.loading = false;
        state.error = null;
      })
      .addCase(fetchApplicantAttachments.rejected, (state: Draft<ApplicantsState>) => {
        state.loading = false;
      });

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

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

export const { clearApplicant } = ApplicantsSlice.actions;

export default ApplicantsSlice;
export type { ApplicantsState };
