import { RejectedActionPayload, ServerErrorResponse } from '@/interface/Shared';
import {
  PayrollRun,
  PayrollEntry,
  PayrollRunSummary,
  PaymentMethodBreakdown,
  PensionSummaryItem,
  CreatePayrollRunPayload,
  ProcessPayrollRunPayload,
  ReviewPayrollRunPayload,
  ApprovePayrollRunPayload,
  ReversePayrollRunPayload,
  CancelPayrollRunPayload,
} from '@/interface/PayrollRun';
import Helpers from '@/utilities/Helpers';
import { createAsyncThunk, createSlice, Draft, PayloadAction } from '@reduxjs/toolkit';
import { message } from 'antd';
import { PayrollRunsActions } from './actionTypes';
import { apiGet, apiPost, apiDelete } from '@/services/api/api';
import axios, { AxiosResponse } from 'axios';

interface PayrollRunsState {
  runs: PayrollRun[];
  selectedRun: PayrollRun | null;
  entries: PayrollEntry[];
  summary: PayrollRunSummary | null;
  paymentMethods: PaymentMethodBreakdown[];
  pensionSummary: PensionSummaryItem[];
  loading: boolean;
  processing: boolean;
  error: string | null;
}

const initialState: PayrollRunsState = {
  runs: [],
  selectedRun: null,
  entries: [],
  summary: null,
  paymentMethods: [],
  pensionSummary: [],
  loading: false,
  processing: false,
  error: null,
};

export const fetchPayrollRuns = createAsyncThunk(
  PayrollRunsActions.FETCH_RUNS_REQUEST,
  async (params?: { payroll_period_id?: string; status?: string; run_type?: string }) => {
    const queryParams = new URLSearchParams();
    if (params?.payroll_period_id)
      queryParams.append('payroll_period_id', params.payroll_period_id);
    if (params?.status) queryParams.append('status', params.status);
    if (params?.run_type) queryParams.append('run_type', params.run_type);
    const query = queryParams.toString() ? `?${queryParams.toString()}` : '';
    const response = (await apiGet(`/payroll/payroll-runs${query}`)) as AxiosResponse<
      PayrollRun[]
    >;
    return response.data;
  },
);

export const fetchPayrollRun = createAsyncThunk(
  PayrollRunsActions.FETCH_RUN_REQUEST,
  async (id: string) => {
    const response = (await apiGet(`/payroll/payroll-runs/${id}`)) as AxiosResponse<PayrollRun>;
    return response.data;
  },
);

export const createPayrollRun = createAsyncThunk(
  PayrollRunsActions.CREATE_RUN_REQUEST,
  async (payload: CreatePayrollRunPayload, { rejectWithValue }) => {
    try {
      const response = (await apiPost('/payroll/payroll-runs', payload)) as AxiosResponse<{
        data: PayrollRun;
      }>;
      return response.data.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const processPayrollRun = createAsyncThunk(
  PayrollRunsActions.PROCESS_RUN_REQUEST,
  async (payload: ProcessPayrollRunPayload, { rejectWithValue }) => {
    try {
      const { id, ...data } = payload;
      const response = (await apiPost(`/payroll/payroll-runs/${id}/process`, data)) as AxiosResponse<{
        data: PayrollRun;
      }>;
      return response.data.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const reprocessPayrollRun = createAsyncThunk(
  PayrollRunsActions.REPROCESS_RUN_REQUEST,
  async (id: string, { rejectWithValue }) => {
    try {
      const response = (await apiPost(
        `/payroll/payroll-runs/${id}/reprocess`,
        {},
      )) as AxiosResponse<{ data: PayrollRun }>;
      return response.data.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const reviewPayrollRun = createAsyncThunk(
  PayrollRunsActions.REVIEW_RUN_REQUEST,
  async (payload: ReviewPayrollRunPayload, { rejectWithValue }) => {
    try {
      const { id, ...data } = payload;
      const response = (await apiPost(
        `/payroll/payroll-runs/${id}/review`,
        data,
      )) as AxiosResponse<{ data: PayrollRun }>;
      return response.data.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const approvePayrollRun = createAsyncThunk(
  PayrollRunsActions.APPROVE_RUN_REQUEST,
  async (payload: ApprovePayrollRunPayload, { rejectWithValue }) => {
    try {
      const { id, ...data } = payload;
      const response = (await apiPost(
        `/payroll/payroll-runs/${id}/approve`,
        data,
      )) as AxiosResponse<{ data: PayrollRun }>;
      return response.data.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const reversePayrollRun = createAsyncThunk(
  PayrollRunsActions.REVERSE_RUN_REQUEST,
  async (payload: ReversePayrollRunPayload, { rejectWithValue }) => {
    try {
      const { id, ...data } = payload;
      const response = (await apiPost(
        `/payroll/payroll-runs/${id}/reverse`,
        data,
      )) as AxiosResponse<{ data: PayrollRun }>;
      return response.data.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const cancelPayrollRun = createAsyncThunk(
  PayrollRunsActions.CANCEL_RUN_REQUEST,
  async (payload: CancelPayrollRunPayload, { rejectWithValue }) => {
    try {
      const { id, ...data } = payload;
      const response = (await apiPost(
        `/payroll/payroll-runs/${id}/cancel`,
        data,
      )) as AxiosResponse<{ data: PayrollRun }>;
      return response.data.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const fetchPayrollEntries = createAsyncThunk(
  PayrollRunsActions.FETCH_ENTRIES_REQUEST,
  async (params: { id: string; search?: string }) => {
    const queryParams = new URLSearchParams();
    if (params.search) queryParams.append('search', params.search);
    const query = queryParams.toString() ? `?${queryParams.toString()}` : '';
    const response = (await apiGet(
      `/payroll/payroll-runs/${params.id}/entries${query}`,
    )) as AxiosResponse<PayrollEntry[]>;
    return response.data;
  },
);

export const fetchPayrollSummary = createAsyncThunk(
  PayrollRunsActions.FETCH_SUMMARY_REQUEST,
  async (id: string) => {
    const response = (await apiGet(`/payroll/payroll-runs/${id}/summary`)) as AxiosResponse<{
      run: PayrollRun;
      summary: PayrollRunSummary;
      payment_methods: PaymentMethodBreakdown[];
      pension_summary: PensionSummaryItem[];
    }>;
    return response.data;
  },
);

export const deletePayrollRun = createAsyncThunk(
  PayrollRunsActions.DELETE_RUN_REQUEST,
  async (id: string, { rejectWithValue }) => {
    try {
      await apiDelete(`/payroll/payroll-runs/${id}`);
      return id;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const PayrollRunsSlice = createSlice({
  name: 'payrollRuns',
  initialState,
  reducers: {
    clearSelectedRun(state) {
      state.selectedRun = null;
      state.entries = [];
      state.summary = null;
      state.paymentMethods = [];
      state.pensionSummary = [];
    },
    clearEntries(state) {
      state.entries = [];
    },
  },
  extraReducers(builder) {
    builder
      .addCase(fetchPayrollRuns.pending, (state: Draft<PayrollRunsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(
        fetchPayrollRuns.fulfilled,
        (state: Draft<PayrollRunsState>, action: PayloadAction<PayrollRun[]>) => {
          state.runs = action.payload;
          state.loading = false;
          state.error = null;
        },
      )
      .addCase(fetchPayrollRuns.rejected, (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
        state.loading = false;
        state.error = 'Failed to load payroll runs';
        message.error(Helpers.handleServerError(action.payload), 8);
      });

    builder
      .addCase(fetchPayrollRun.pending, (state: Draft<PayrollRunsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(
        fetchPayrollRun.fulfilled,
        (state: Draft<PayrollRunsState>, action: PayloadAction<PayrollRun>) => {
          state.selectedRun = action.payload;
          state.loading = false;
          state.error = null;
        },
      )
      .addCase(fetchPayrollRun.rejected, (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
        state.loading = false;
        state.error = 'Failed to load payroll run details';
        message.error(Helpers.handleServerError(action.payload), 8);
      });

    builder
      .addCase(createPayrollRun.pending, (state: Draft<PayrollRunsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(
        createPayrollRun.fulfilled,
        (state: Draft<PayrollRunsState>, action: PayloadAction<PayrollRun>) => {
          state.runs.unshift(action.payload);
          state.selectedRun = action.payload;
          state.loading = false;
          state.error = null;
          message.success('Payroll run created successfully');
        },
      )
      .addCase(
        createPayrollRun.rejected,
        (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
          state.loading = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

    builder
      .addCase(processPayrollRun.pending, (state: Draft<PayrollRunsState>) => {
        state.processing = true;
        state.error = null;
      })
      .addCase(
        processPayrollRun.fulfilled,
        (state: Draft<PayrollRunsState>, action: PayloadAction<PayrollRun>) => {
          const index = state.runs.findIndex((r) => r.id === action.payload.id);
          if (index !== -1) state.runs[index] = action.payload;
          state.selectedRun = action.payload;
          state.processing = false;
          state.error = null;
          message.info('Payroll processing has been queued. This may take a few minutes.');
        },
      )
      .addCase(
        processPayrollRun.rejected,
        (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
          state.processing = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

    builder
      .addCase(reprocessPayrollRun.pending, (state: Draft<PayrollRunsState>) => {
        state.processing = true;
        state.error = null;
      })
      .addCase(
        reprocessPayrollRun.fulfilled,
        (state: Draft<PayrollRunsState>, action: PayloadAction<PayrollRun>) => {
          const index = state.runs.findIndex((r) => r.id === action.payload.id);
          if (index !== -1) state.runs[index] = action.payload;
          state.selectedRun = action.payload;
          state.processing = false;
          state.error = null;
          message.info('Payroll reprocessing has been queued. This may take a few minutes.');
        },
      )
      .addCase(
        reprocessPayrollRun.rejected,
        (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
          state.processing = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

    builder
      .addCase(reviewPayrollRun.pending, (state: Draft<PayrollRunsState>) => {
        state.loading = true;
      })
      .addCase(
        reviewPayrollRun.fulfilled,
        (state: Draft<PayrollRunsState>, action: PayloadAction<PayrollRun>) => {
          const index = state.runs.findIndex((r) => r.id === action.payload.id);
          if (index !== -1) state.runs[index] = action.payload;
          state.selectedRun = action.payload;
          state.loading = false;
          message.success('Payroll run reviewed successfully');
        },
      )
      .addCase(
        reviewPayrollRun.rejected,
        (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
          state.loading = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

    builder
      .addCase(approvePayrollRun.pending, (state: Draft<PayrollRunsState>) => {
        state.loading = true;
      })
      .addCase(
        approvePayrollRun.fulfilled,
        (state: Draft<PayrollRunsState>, action: PayloadAction<PayrollRun>) => {
          const index = state.runs.findIndex((r) => r.id === action.payload.id);
          if (index !== -1) state.runs[index] = action.payload;
          state.selectedRun = action.payload;
          state.loading = false;
          message.success('Payroll run approved successfully');
        },
      )
      .addCase(
        approvePayrollRun.rejected,
        (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
          state.loading = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

    builder
      .addCase(reversePayrollRun.pending, (state: Draft<PayrollRunsState>) => {
        state.loading = true;
      })
      .addCase(
        reversePayrollRun.fulfilled,
        (state: Draft<PayrollRunsState>, action: PayloadAction<PayrollRun>) => {
          const index = state.runs.findIndex((r) => r.id === action.payload.id);
          if (index !== -1) state.runs[index] = action.payload;
          state.selectedRun = action.payload;
          state.loading = false;
          message.success('Payroll run reversed successfully');
        },
      )
      .addCase(
        reversePayrollRun.rejected,
        (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
          state.loading = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

    builder
      .addCase(cancelPayrollRun.pending, (state: Draft<PayrollRunsState>) => {
        state.loading = true;
      })
      .addCase(
        cancelPayrollRun.fulfilled,
        (state: Draft<PayrollRunsState>, action: PayloadAction<PayrollRun>) => {
          const index = state.runs.findIndex((r) => r.id === action.payload.id);
          if (index !== -1) state.runs[index] = action.payload;
          state.selectedRun = action.payload;
          state.loading = false;
          message.success('Payroll run cancelled successfully');
        },
      )
      .addCase(
        cancelPayrollRun.rejected,
        (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
          state.loading = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );

    builder
      .addCase(fetchPayrollEntries.pending, (state: Draft<PayrollRunsState>) => {
        state.loading = true;
      })
      .addCase(
        fetchPayrollEntries.fulfilled,
        (state: Draft<PayrollRunsState>, action: PayloadAction<PayrollEntry[]>) => {
          state.entries = action.payload;
          state.loading = false;
        },
      )
      .addCase(fetchPayrollEntries.rejected, (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
        state.loading = false;
        state.error = 'Failed to load payroll entries';
        message.error(Helpers.handleServerError(action.payload), 8);
      });

    builder
      .addCase(fetchPayrollSummary.pending, (state: Draft<PayrollRunsState>) => {
        state.loading = true;
      })
      .addCase(fetchPayrollSummary.fulfilled, (state: Draft<PayrollRunsState>, action) => {
        state.summary = action.payload.summary;
        state.paymentMethods = action.payload.payment_methods;
        state.pensionSummary = action.payload.pension_summary ?? [];
        state.loading = false;
      })
      .addCase(fetchPayrollSummary.rejected, (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
        state.loading = false;
        state.error = 'Failed to load payroll summary';
        message.error(Helpers.handleServerError(action.payload), 8);
      });

    builder
      .addCase(deletePayrollRun.pending, (state: Draft<PayrollRunsState>) => {
        state.loading = true;
      })
      .addCase(
        deletePayrollRun.fulfilled,
        (state: Draft<PayrollRunsState>, action: PayloadAction<string>) => {
          state.runs = state.runs.filter((r) => r.id !== action.payload);
          if (state.selectedRun?.id === action.payload) {
            state.selectedRun = null;
            state.entries = [];
            state.summary = null;
            state.paymentMethods = [];
            state.pensionSummary = [];
          }
          state.loading = false;
          message.success('Payroll run deleted successfully');
        },
      )
      .addCase(
        deletePayrollRun.rejected,
        (state: Draft<PayrollRunsState>, action: RejectedActionPayload) => {
          state.loading = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(Helpers.handleServerError(action.payload), 10);
        },
      );
  },
});

export const { clearSelectedRun, clearEntries } = PayrollRunsSlice.actions;
export default PayrollRunsSlice;
export type { PayrollRunsState };
