import { RejectedActionPayload, ServerErrorResponse } from '@/interface/Shared';
import {
  ArchiveListResponse,
  BulkPayslipPayload,
  BulkReportCreateResponse,
  BulkTaxCertificatePayload,
  PayrollReportArchive,
  PensionReportDownloadPayload,
  ReportBatchStatus,
  ReportDownloadPayload,
  ReportPreviewResponse,
} from '@/interface/PayrollReport';
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 { PayrollReportsActions } from './actionTypes';

interface PayrollReportsState {
  payslipPreviewUrl: string | null;
  taxCertificatePreviewUrl: string | null;
  bulkPayslipBatch: ReportBatchStatus | null;
  bulkTaxCertificateBatch: ReportBatchStatus | null;
  payslipArchives: PayrollReportArchive[];
  taxCertArchives: PayrollReportArchive[];
  archivesLoading: boolean;
  pensionPreview: ReportPreviewResponse | null;
  journalPreview: ReportPreviewResponse | null;
  taxSchedulePreview: ReportPreviewResponse | null;
  loading: boolean;
  downloading: boolean;
  error: string | null;
}

const initialState: PayrollReportsState = {
  payslipPreviewUrl: null,
  taxCertificatePreviewUrl: null,
  bulkPayslipBatch: null,
  bulkTaxCertificateBatch: null,
  payslipArchives: [],
  taxCertArchives: [],
  archivesLoading: false,
  pensionPreview: null,
  journalPreview: null,
  taxSchedulePreview: null,
  loading: false,
  downloading: 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 fetchPayslipPreview = createAsyncThunk(
  PayrollReportsActions.FETCH_PAYSLIP_PREVIEW,
  async (payload: { employee_id: string; payroll_run_id: string }, { rejectWithValue }) => {
    try {
      const response = await apiDownloadBlob(
        `/payroll/reports/payslip/${payload.employee_id}/${payload.payroll_run_id}`,
      );
      const blob = new Blob([response.data], { type: 'application/pdf' });
      const url = window.URL.createObjectURL(blob);

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

export const createBulkPayslips = createAsyncThunk(
  PayrollReportsActions.CREATE_BULK_PAYSLIPS,
  async (payload: BulkPayslipPayload, { rejectWithValue }) => {
    try {
      const response = (await apiPost('/payroll/reports/payslips/bulk', payload)) as AxiosResponse<
        BulkReportCreateResponse
      >;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const fetchBulkPayslipStatus = createAsyncThunk(
  PayrollReportsActions.FETCH_BULK_PAYSLIP_STATUS,
  async (batchId: string, { rejectWithValue }) => {
    try {
      const response = (await apiGet(
        `/payroll/reports/payslips/bulk/${batchId}/status`,
      )) as AxiosResponse<ReportBatchStatus>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const downloadBulkPayslips = createAsyncThunk(
  PayrollReportsActions.DOWNLOAD_BULK_PAYSLIPS,
  async (downloadUrl: string, { rejectWithValue }) => {
    try {
      const response = await axiosInstance.get(downloadUrl, { responseType: 'blob' });
      const filename = resolveFilename(response.headers['content-disposition'], 'payslips.zip');
      downloadBlob(new Blob([response.data]), filename);
      return true;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        try {
          const text = await (error.response.data as Blob).text();
          return rejectWithValue(JSON.parse(text) as ServerErrorResponse);
        } catch {
          return rejectWithValue({ message: 'Download failed. Please try again.' } as ServerErrorResponse);
        }
      }
      return rejectWithValue({ message: 'Download failed. Please try again.' } as ServerErrorResponse);
    }
  },
);

export const fetchTaxCertificatePreview = createAsyncThunk(
  PayrollReportsActions.FETCH_TAX_CERTIFICATE_PREVIEW,
  async (payload: { employee_id: string; year: number }, { rejectWithValue }) => {
    try {
      const response = await apiDownloadBlob(
        `/payroll/reports/tax-certificate/${payload.employee_id}/${payload.year}`,
      );
      const blob = new Blob([response.data], { type: 'application/pdf' });
      const url = window.URL.createObjectURL(blob);

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

export const createBulkTaxCertificates = createAsyncThunk(
  PayrollReportsActions.CREATE_BULK_TAX_CERTIFICATES,
  async (payload: BulkTaxCertificatePayload, { rejectWithValue }) => {
    try {
      const response = (await apiPost(
        '/payroll/reports/tax-certificates/bulk',
        payload,
      )) as AxiosResponse<BulkReportCreateResponse>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const fetchBulkTaxCertificateStatus = createAsyncThunk(
  PayrollReportsActions.FETCH_BULK_TAX_CERTIFICATE_STATUS,
  async (batchId: string, { rejectWithValue }) => {
    try {
      const response = (await apiGet(
        `/payroll/reports/tax-certificates/bulk/${batchId}/status`,
      )) as AxiosResponse<ReportBatchStatus>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const downloadBulkTaxCertificates = createAsyncThunk(
  PayrollReportsActions.DOWNLOAD_BULK_TAX_CERTIFICATES,
  async (downloadUrl: string, { rejectWithValue }) => {
    try {
      const response = await axiosInstance.get(downloadUrl, { responseType: 'blob' });
      const filename = resolveFilename(
        response.headers['content-disposition'],
        'tax_certificates.zip',
      );
      downloadBlob(new Blob([response.data]), filename);
      return true;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        try {
          const text = await (error.response.data as Blob).text();
          return rejectWithValue(JSON.parse(text) as ServerErrorResponse);
        } catch {
          return rejectWithValue({ message: 'Download failed. Please try again.' } as ServerErrorResponse);
        }
      }
      return rejectWithValue({ message: 'Download failed. Please try again.' } as ServerErrorResponse);
    }
  },
);

export const fetchPayslipArchives = createAsyncThunk(
  PayrollReportsActions.FETCH_PAYSLIP_ARCHIVES,
  async (payrollRunId: string, { rejectWithValue }) => {
    try {
      const response = (await apiGet(
        `/payroll/reports/payslips/archives?payroll_run_id=${encodeURIComponent(payrollRunId)}`,
      )) as AxiosResponse<ArchiveListResponse>;
      return response.data.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const fetchTaxCertArchives = createAsyncThunk(
  PayrollReportsActions.FETCH_TAX_CERT_ARCHIVES,
  async (year: number, { rejectWithValue }) => {
    try {
      const response = (await apiGet(
        `/payroll/reports/tax-certificates/archives?year=${year}`,
      )) as AxiosResponse<ArchiveListResponse>;
      return response.data.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const downloadBankSchedule = createAsyncThunk(
  PayrollReportsActions.DOWNLOAD_BANK_SCHEDULE,
  async (payload: ReportDownloadPayload, { rejectWithValue }) => {
    try {
      const format = payload.format || 'xlsx';
      const response = await apiDownloadBlob(
        `/payroll/reports/bank-schedule/${payload.payroll_run_id}?format=${format}`,
      );
      downloadBlob(new Blob([response.data]), `bank_schedule_${payload.payroll_run_id}.${format}`);
      return true;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const fetchPensionPreview = createAsyncThunk(
  PayrollReportsActions.FETCH_PENSION_PREVIEW,
  async (payload: { payrollRunId: string; reportGroup: string }, { rejectWithValue }) => {
    try {
      const response = (await apiGet(
        `/payroll/reports/pension/${payload.payrollRunId}/${payload.reportGroup}`,
      )) as AxiosResponse<ReportPreviewResponse>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const downloadPensionReport = createAsyncThunk(
  PayrollReportsActions.DOWNLOAD_PENSION_REPORT,
  async (payload: PensionReportDownloadPayload, { rejectWithValue }) => {
    try {
      const format = payload.format || 'xlsx';
      const response = await apiDownloadBlob(
        `/payroll/reports/pension/${payload.payroll_run_id}/${payload.report_group}?download=1&format=${format}`,
      );
      downloadBlob(
        new Blob([response.data]),
        `pension_${payload.report_group}_report_${payload.payroll_run_id}.${format}`,
      );
      return true;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const fetchJournalPreview = createAsyncThunk(
  PayrollReportsActions.FETCH_JOURNAL_PREVIEW,
  async (payrollRunId: string, { rejectWithValue }) => {
    try {
      const response = (await apiGet(`/payroll/reports/journal/${payrollRunId}`)) as AxiosResponse<
        ReportPreviewResponse
      >;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const downloadJournalReport = createAsyncThunk(
  PayrollReportsActions.DOWNLOAD_JOURNAL,
  async (payload: ReportDownloadPayload, { rejectWithValue }) => {
    try {
      const format = payload.format || 'xlsx';
      const response = await apiDownloadBlob(
        `/payroll/reports/journal/${payload.payroll_run_id}?download=1&format=${format}`,
      );
      downloadBlob(new Blob([response.data]), `journal_entries_${payload.payroll_run_id}.${format}`);
      return true;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const fetchTaxSchedulePreview = createAsyncThunk(
  PayrollReportsActions.FETCH_TAX_SCHEDULE_PREVIEW,
  async (payrollRunId: string, { rejectWithValue }) => {
    try {
      const response = (await apiGet(
        `/payroll/reports/tax-schedule/${payrollRunId}`,
      )) as AxiosResponse<ReportPreviewResponse>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const downloadTaxSchedule = createAsyncThunk(
  PayrollReportsActions.DOWNLOAD_TAX_SCHEDULE,
  async (payload: ReportDownloadPayload, { rejectWithValue }) => {
    try {
      const format = payload.format || 'xlsx';
      const response = await apiDownloadBlob(
        `/payroll/reports/tax-schedule/${payload.payroll_run_id}?download=1&format=${format}`,
      );
      downloadBlob(new Blob([response.data]), `tax_schedule_${payload.payroll_run_id}.${format}`);
      return true;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const PayrollReportsSlice = createSlice({
  name: 'payrollReports',
  initialState,
  reducers: {
    clearPayslipPreview(state) {
      if (state.payslipPreviewUrl) {
        window.URL.revokeObjectURL(state.payslipPreviewUrl);
      }
      state.payslipPreviewUrl = null;
    },
    clearTaxCertificatePreview(state) {
      if (state.taxCertificatePreviewUrl) {
        window.URL.revokeObjectURL(state.taxCertificatePreviewUrl);
      }
      state.taxCertificatePreviewUrl = null;
    },
  },
  extraReducers(builder) {
    builder
      .addCase(fetchPayslipPreview.pending, (state: Draft<PayrollReportsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchPayslipPreview.fulfilled, (state: Draft<PayrollReportsState>, action) => {
        if (state.payslipPreviewUrl) {
          window.URL.revokeObjectURL(state.payslipPreviewUrl);
        }

        state.payslipPreviewUrl = action.payload;
        state.loading = false;
      })
      .addCase(fetchPayslipPreview.rejected, (state: Draft<PayrollReportsState>, action) => {
        state.loading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(Helpers.handleServerError(action.payload), 10);
      })

      .addCase(fetchTaxCertificatePreview.pending, (state: Draft<PayrollReportsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(
        fetchTaxCertificatePreview.fulfilled,
        (state: Draft<PayrollReportsState>, action: PayloadAction<string>) => {
          if (state.taxCertificatePreviewUrl) {
            window.URL.revokeObjectURL(state.taxCertificatePreviewUrl);
          }

          state.taxCertificatePreviewUrl = action.payload;
          state.loading = false;
        },
      )
      .addCase(fetchTaxCertificatePreview.rejected, (state: Draft<PayrollReportsState>, action) => {
        state.loading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(Helpers.handleServerError(action.payload), 10);
      })

      .addCase(createBulkPayslips.pending, (state: Draft<PayrollReportsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(createBulkPayslips.fulfilled, (state: Draft<PayrollReportsState>) => {
        state.loading = false;
        message.success('Bulk payslip generation started');
      })
      .addCase(createBulkPayslips.rejected, (state: Draft<PayrollReportsState>, action) => {
        state.loading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(Helpers.handleServerError(action.payload), 10);
      })

      .addCase(fetchBulkPayslipStatus.fulfilled, (state: Draft<PayrollReportsState>, action) => {
        state.bulkPayslipBatch = action.payload;
      })
      .addCase(fetchBulkPayslipStatus.rejected, (state: Draft<PayrollReportsState>, action) => {
        state.error = Helpers.handleServerError(action.payload);
        message.error(Helpers.handleServerError(action.payload), 10);
      })

      .addCase(createBulkTaxCertificates.pending, (state: Draft<PayrollReportsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(createBulkTaxCertificates.fulfilled, (state: Draft<PayrollReportsState>) => {
        state.loading = false;
        message.success('Bulk tax certificate generation started');
      })
      .addCase(createBulkTaxCertificates.rejected, (state: Draft<PayrollReportsState>, action) => {
        state.loading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(Helpers.handleServerError(action.payload), 10);
      })

      .addCase(fetchBulkTaxCertificateStatus.fulfilled, (state: Draft<PayrollReportsState>, action) => {
        state.bulkTaxCertificateBatch = action.payload;
      })
      .addCase(fetchBulkTaxCertificateStatus.rejected, (state: Draft<PayrollReportsState>, action) => {
        state.error = Helpers.handleServerError(action.payload);
        message.error(Helpers.handleServerError(action.payload), 10);
      })

      .addCase(fetchPayslipArchives.pending, (state: Draft<PayrollReportsState>) => {
        state.archivesLoading = true;
      })
      .addCase(fetchPayslipArchives.fulfilled, (state: Draft<PayrollReportsState>, action) => {
        state.archivesLoading = false;
        state.payslipArchives = action.payload;
      })
      .addCase(fetchPayslipArchives.rejected, (state: Draft<PayrollReportsState>) => {
        state.archivesLoading = false;
      })

      .addCase(fetchTaxCertArchives.pending, (state: Draft<PayrollReportsState>) => {
        state.archivesLoading = true;
      })
      .addCase(fetchTaxCertArchives.fulfilled, (state: Draft<PayrollReportsState>, action) => {
        state.archivesLoading = false;
        state.taxCertArchives = action.payload;
      })
      .addCase(fetchTaxCertArchives.rejected, (state: Draft<PayrollReportsState>) => {
        state.archivesLoading = false;
      })

      .addCase(fetchPensionPreview.pending, (state: Draft<PayrollReportsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchPensionPreview.fulfilled, (state: Draft<PayrollReportsState>, action) => {
        state.pensionPreview = action.payload;
        state.loading = false;
      })
      .addCase(fetchPensionPreview.rejected, (state: Draft<PayrollReportsState>, action) => {
        state.loading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(Helpers.handleServerError(action.payload), 10);
      })

      .addCase(fetchJournalPreview.pending, (state: Draft<PayrollReportsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchJournalPreview.fulfilled, (state: Draft<PayrollReportsState>, action) => {
        state.journalPreview = action.payload;
        state.loading = false;
      })
      .addCase(fetchJournalPreview.rejected, (state: Draft<PayrollReportsState>, action) => {
        state.loading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(Helpers.handleServerError(action.payload), 10);
      })

      .addCase(fetchTaxSchedulePreview.pending, (state: Draft<PayrollReportsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchTaxSchedulePreview.fulfilled, (state: Draft<PayrollReportsState>, action) => {
        state.taxSchedulePreview = action.payload;
        state.loading = false;
      })
      .addCase(fetchTaxSchedulePreview.rejected, (state: Draft<PayrollReportsState>, action) => {
        state.loading = false;
        state.error = Helpers.handleServerError(action.payload);
        message.error(Helpers.handleServerError(action.payload), 10);
      })

      .addMatcher(
        (action) => action.type.endsWith('/pending') && action.type.startsWith('payrollReports/download'),
        (state: Draft<PayrollReportsState>) => {
          state.downloading = true;
          state.error = null;
        },
      )
      .addMatcher(
        (action) => action.type.endsWith('/fulfilled') && action.type.startsWith('payrollReports/download'),
        (state: Draft<PayrollReportsState>) => {
          state.downloading = false;
          message.success('Download completed');
        },
      )
      .addMatcher(
        (action) => action.type.endsWith('/rejected') && action.type.startsWith('payrollReports/download'),
        (state: Draft<PayrollReportsState>, action: RejectedActionPayload) => {
          state.downloading = false;
          const payload = action.payload ?? { message: 'Download failed. Please try again.' };
          state.error = Helpers.handleServerError(payload);
          message.error(Helpers.handleServerError(payload), 10);
        },
      );
  },
});

export const { clearPayslipPreview, clearTaxCertificatePreview } = PayrollReportsSlice.actions;
export type { PayrollReportArchive };
export default PayrollReportsSlice;
