import {
  PayrollImportBatchResponse,
  PayrollImportBatchStatus,
  PayrollImportResponse,
  PayrollImportType,
} from '@/interface/PayrollDataManagement';
import { RejectedActionPayload, ServerErrorResponse } from '@/interface/Shared';
import { apiDownloadBlob, apiGet, apiPost, axiosInstance } 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 { PayrollImportsActions } from './actionTypes';

interface PayrollImportsState {
  validationResult: PayrollImportResponse | null;
  importResult: PayrollImportResponse | null;
  importBatch: PayrollImportBatchStatus | null;
  downloadingTemplate: boolean;
  validating: boolean;
  importing: boolean;
  downloadingFailedRows: boolean;
  error: string | null;
}

const initialState: PayrollImportsState = {
  validationResult: null,
  importResult: null,
  importBatch: null,
  downloadingTemplate: false,
  validating: false,
  importing: false,
  downloadingFailedRows: false,
  error: null,
};

const resolveFilename = (contentDisposition: string | undefined, fallback: string) => {
  if (!contentDisposition) return fallback;

  const utf8Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
  if (utf8Match?.[1]) {
    return decodeURIComponent(utf8Match[1]);
  }

  const standardMatch = contentDisposition.match(/filename="?([^";]+)"?/i);
  return standardMatch?.[1] || fallback;
};

const downloadBlob = (blob: Blob, fileName: string) => {
  const url = window.URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.setAttribute('download', fileName);
  document.body.appendChild(link);
  link.click();
  link.remove();
  window.URL.revokeObjectURL(url);
};

export const downloadImportTemplate = createAsyncThunk(
  PayrollImportsActions.DOWNLOAD_TEMPLATE,
  async (type: PayrollImportType, { rejectWithValue }) => {
    try {
      const response = await apiDownloadBlob(`/payroll/imports/${type}/template`);
      const filename = resolveFilename(
        response.headers['content-disposition'],
        `payroll_${type}_import_template.xlsx`,
      );
      downloadBlob(new Blob([response.data]), filename);
      return true;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }

      return rejectWithValue({ message: 'An unexpected error occurred. Please try again.' } as ServerErrorResponse);
    }
  },
);

export const validatePayrollImport = createAsyncThunk(
  PayrollImportsActions.VALIDATE_IMPORT,
  async (
    payload: {
      type: PayrollImportType;
      file: File;
    },
    { rejectWithValue },
  ) => {
    try {
      const formData = new FormData();
      formData.append('file', payload.file);
      formData.append('validate_only', '1');

      const response = (await apiPost(`/payroll/imports/${payload.type}`, formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      })) as AxiosResponse<PayrollImportResponse>;

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

      return rejectWithValue({ message: 'An unexpected error occurred. Please try again.' } as ServerErrorResponse);
    }
  },
);

export const executePayrollImport = createAsyncThunk(
  PayrollImportsActions.EXECUTE_IMPORT,
  async (
    payload: {
      type: PayrollImportType;
      file: File;
    },
    { rejectWithValue },
  ) => {
    try {
      const formData = new FormData();
      formData.append('file', payload.file);

      const response = (await apiPost(`/payroll/imports/${payload.type}`, formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      })) as AxiosResponse<PayrollImportBatchResponse>;

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

      return rejectWithValue({ message: 'An unexpected error occurred. Please try again.' } as ServerErrorResponse);
    }
  },
);

export const fetchPayrollImportStatus = createAsyncThunk(
  PayrollImportsActions.FETCH_IMPORT_STATUS,
  async (batchId: string, { rejectWithValue }) => {
    try {
      const response = (await apiGet(
        `/payroll/imports/${batchId}/status`,
      )) as AxiosResponse<PayrollImportBatchStatus>;

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

      return rejectWithValue({ message: 'An unexpected error occurred. Please try again.' } as ServerErrorResponse);
    }
  },
);

export const downloadImportFailedRows = createAsyncThunk(
  PayrollImportsActions.DOWNLOAD_FAILED_ROWS,
  async (downloadUrl: string, { rejectWithValue }) => {
    try {
      const response = await axiosInstance.get(downloadUrl, { responseType: 'blob' });
      const filename = resolveFilename(
        response.headers['content-disposition'],
        'payroll_import_failed_rows.xlsx',
      );
      downloadBlob(new Blob([response.data]), filename);
      return true;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }

      return rejectWithValue({ message: 'An unexpected error occurred. Please try again.' } as ServerErrorResponse);
    }
  },
);

export const PayrollImportsSlice = createSlice({
  name: 'payrollImports',
  initialState,
  reducers: {
    clearImportState(state) {
      state.validationResult = null;
      state.importResult = null;
      state.importBatch = null;
      state.error = null;
    },
  },
  extraReducers(builder) {
    builder
      .addCase(downloadImportTemplate.pending, (state: Draft<PayrollImportsState>) => {
        state.downloadingTemplate = true;
        state.error = null;
      })
      .addCase(downloadImportTemplate.fulfilled, (state: Draft<PayrollImportsState>) => {
        state.downloadingTemplate = false;
      })
      .addCase(
        downloadImportTemplate.rejected,
        (state: Draft<PayrollImportsState>, action: RejectedActionPayload) => {
          state.downloadingTemplate = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(state.error, 10);
        },
      );

    builder
      .addCase(validatePayrollImport.pending, (state: Draft<PayrollImportsState>) => {
        state.validating = true;
        state.error = null;
      })
      .addCase(
        validatePayrollImport.fulfilled,
        (state: Draft<PayrollImportsState>, action: PayloadAction<PayrollImportResponse>) => {
          state.validating = false;
          state.validationResult = action.payload;
          message.success(action.payload.message || 'Validation completed successfully');
        },
      )
      .addCase(
        validatePayrollImport.rejected,
        (state: Draft<PayrollImportsState>, action: RejectedActionPayload) => {
          state.validating = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(state.error, 10);
        },
      );

    builder
      .addCase(executePayrollImport.pending, (state: Draft<PayrollImportsState>) => {
        state.importing = true;
        state.error = null;
      })
      .addCase(
        executePayrollImport.fulfilled,
        (state: Draft<PayrollImportsState>, action: PayloadAction<PayrollImportBatchResponse>) => {
          state.importing = false;
          state.importBatch = {
            batch_id: action.payload.batch_id,
            progress: 0,
            finished: false,
            cancelled: false,
            total_jobs: 1,
            pending_jobs: 1,
            processed_jobs: 0,
            failed_jobs: 0,
          };
          message.success(action.payload.message || 'Import job started. Processing in background...');
        },
      )
      .addCase(
        executePayrollImport.rejected,
        (state: Draft<PayrollImportsState>, action: RejectedActionPayload) => {
          state.importing = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(state.error, 10);
        },
      );

    builder
      .addCase(
        fetchPayrollImportStatus.fulfilled,
        (state: Draft<PayrollImportsState>, action: PayloadAction<PayrollImportBatchStatus>) => {
          state.importBatch = action.payload;

          if (action.payload.finished && !action.payload.error) {
            state.importResult = {
              message: 'Import completed successfully.',
              type: state.validationResult?.type ?? 'grades',
              validate_only: false,
              imported_count: action.payload.imported_count ?? 0,
              updated_count: action.payload.updated_count ?? 0,
              failed_count: action.payload.failed_count ?? 0,
              processed_count: action.payload.processed_count ?? 0,
              failed_rows_file: null,
              failed_rows_download_url: action.payload.failed_rows_download_url ?? null,
            };
          }

          if (action.payload.finished && action.payload.error) {
            state.error = action.payload.error;
            message.error(action.payload.error, 10);
          }
        },
      )
      .addCase(
        fetchPayrollImportStatus.rejected,
        (state: Draft<PayrollImportsState>, action: RejectedActionPayload) => {
          state.error = Helpers.handleServerError(action.payload);
        },
      );

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

export const { clearImportState } = PayrollImportsSlice.actions;

export default PayrollImportsSlice;
