import { configureStore } from '@reduxjs/toolkit';
import PayrollRunsSlice, {
  fetchPayrollRuns,
  fetchPayrollRun,
  createPayrollRun,
  processPayrollRun,
  reprocessPayrollRun,
  reviewPayrollRun,
  approvePayrollRun,
  reversePayrollRun,
  cancelPayrollRun,
  fetchPayrollEntries,
  fetchPayrollSummary,
  deletePayrollRun,
  clearSelectedRun,
  clearEntries,
} from '../PayrollRunsSlice';

jest.mock('@/services/api/api', () => ({
  apiGet: jest.fn(),
  apiPost: jest.fn(),
  apiDelete: jest.fn(),
}));

jest.mock('antd', () => ({
  message: { success: jest.fn(), error: jest.fn(), info: jest.fn(), warning: jest.fn() },
}));

jest.mock('@/utilities/Helpers', () => {
  const mockHelpers = { handleServerError: jest.fn((e: any) => e?.message || 'Error') };
  return { __esModule: true, default: mockHelpers };
});

const { apiGet, apiPost, apiDelete } = require('@/services/api/api');

const createTestStore = () =>
  configureStore({ reducer: { payrollRuns: PayrollRunsSlice.reducer } });

const mockRun = { id: '1', status: 'draft', payroll_period_id: 'p1' };

describe('PayrollRunsSlice', () => {
  beforeEach(() => jest.clearAllMocks());

  it('has correct initial state', () => {
    const store = createTestStore();
    const state = store.getState().payrollRuns;
    expect(state.runs).toEqual([]);
    expect(state.selectedRun).toBeNull();
    expect(state.entries).toEqual([]);
    expect(state.summary).toBeNull();
    expect(state.paymentMethods).toEqual([]);
    expect(state.pensionSummary).toEqual([]);
    expect(state.loading).toBe(false);
    expect(state.processing).toBe(false);
    expect(state.error).toBeNull();
  });

  describe('fetchPayrollRuns', () => {
    it('populates runs on fulfilled', async () => {
      apiGet.mockResolvedValue({ data: [mockRun] });
      const store = createTestStore();
      await store.dispatch(fetchPayrollRuns());
      expect(store.getState().payrollRuns.runs).toEqual([mockRun]);
    });
  });

  describe('fetchPayrollRun', () => {
    it('sets selectedRun on fulfilled', async () => {
      apiGet.mockResolvedValue({ data: mockRun });
      const store = createTestStore();
      await store.dispatch(fetchPayrollRun('1'));
      expect(store.getState().payrollRuns.selectedRun).toEqual(mockRun);
    });
  });

  describe('createPayrollRun', () => {
    it('prepends new run and sets selectedRun on fulfilled', async () => {
      apiPost.mockResolvedValue({ data: { data: mockRun } });
      const store = createTestStore();
      await store.dispatch(createPayrollRun({ payroll_period_id: 'p1' } as any));
      expect(store.getState().payrollRuns.runs[0]).toEqual(mockRun);
      expect(store.getState().payrollRuns.selectedRun).toEqual(mockRun);
    });
  });

  describe('processPayrollRun', () => {
    it('sets processing on pending and updates run on fulfilled', async () => {
      const processed = { ...mockRun, status: 'processing' };
      apiPost.mockResolvedValue({ data: { data: processed } });
      const store = createTestStore();
      store.dispatch({ type: fetchPayrollRuns.fulfilled.type, payload: [mockRun] });
      const promise = store.dispatch(processPayrollRun({ id: '1' } as any));
      expect(store.getState().payrollRuns.processing).toBe(true);
      await promise;
      expect(store.getState().payrollRuns.processing).toBe(false);
      expect(store.getState().payrollRuns.selectedRun?.status).toBe('processing');
    });
  });

  describe('reprocessPayrollRun', () => {
    it('sets processing on pending', async () => {
      apiPost.mockResolvedValue({ data: { data: { ...mockRun, status: 'processing' } } });
      const store = createTestStore();
      store.dispatch({ type: fetchPayrollRuns.fulfilled.type, payload: [mockRun] });
      const promise = store.dispatch(reprocessPayrollRun('1'));
      expect(store.getState().payrollRuns.processing).toBe(true);
      await promise;
      expect(store.getState().payrollRuns.processing).toBe(false);
    });
  });

  describe('reviewPayrollRun', () => {
    it('updates run status on fulfilled', async () => {
      const reviewed = { ...mockRun, status: 'reviewed' };
      apiPost.mockResolvedValue({ data: { data: reviewed } });
      const store = createTestStore();
      store.dispatch({ type: fetchPayrollRuns.fulfilled.type, payload: [mockRun] });
      await store.dispatch(reviewPayrollRun({ id: '1' } as any));
      expect(store.getState().payrollRuns.runs[0].status).toBe('reviewed');
    });
  });

  describe('approvePayrollRun', () => {
    it('updates run status on fulfilled', async () => {
      const approved = { ...mockRun, status: 'approved' };
      apiPost.mockResolvedValue({ data: { data: approved } });
      const store = createTestStore();
      store.dispatch({ type: fetchPayrollRuns.fulfilled.type, payload: [mockRun] });
      await store.dispatch(approvePayrollRun({ id: '1' } as any));
      expect(store.getState().payrollRuns.runs[0].status).toBe('approved');
    });
  });

  describe('reversePayrollRun', () => {
    it('updates run status on fulfilled', async () => {
      const reversed = { ...mockRun, status: 'reversed' };
      apiPost.mockResolvedValue({ data: { data: reversed } });
      const store = createTestStore();
      store.dispatch({ type: fetchPayrollRuns.fulfilled.type, payload: [mockRun] });
      await store.dispatch(reversePayrollRun({ id: '1' } as any));
      expect(store.getState().payrollRuns.runs[0].status).toBe('reversed');
    });
  });

  describe('fetchPayrollEntries', () => {
    it('populates entries on fulfilled', async () => {
      const entries = [{ id: 'e1', employee_id: 'emp1' }];
      apiGet.mockResolvedValue({ data: entries });
      const store = createTestStore();
      await store.dispatch(fetchPayrollEntries({ id: '1' }));
      expect(store.getState().payrollRuns.entries).toEqual(entries);
    });
  });

  describe('fetchPayrollSummary', () => {
    it('populates summary fields on fulfilled', async () => {
      const summaryData = {
        run: mockRun,
        summary: { total_gross: 100000 },
        payment_methods: [{ method: 'bank', count: 5 }],
        pension_summary: [{ pool: 'tier1', amount: 5000 }],
      };
      apiGet.mockResolvedValue({ data: summaryData });
      const store = createTestStore();
      await store.dispatch(fetchPayrollSummary('1'));
      const state = store.getState().payrollRuns;
      expect(state.summary).toEqual(summaryData.summary);
      expect(state.paymentMethods).toEqual(summaryData.payment_methods);
      expect(state.pensionSummary).toEqual(summaryData.pension_summary);
    });
  });

  describe('deletePayrollRun', () => {
    it('removes run and clears selectedRun if matching on fulfilled', async () => {
      apiDelete.mockResolvedValue({});
      const store = createTestStore();
      store.dispatch({ type: fetchPayrollRuns.fulfilled.type, payload: [mockRun] });
      store.dispatch({ type: fetchPayrollRun.fulfilled.type, payload: mockRun });
      await store.dispatch(deletePayrollRun('1'));
      expect(store.getState().payrollRuns.runs).toEqual([]);
      expect(store.getState().payrollRuns.selectedRun).toBeNull();
    });
  });

  describe('clearSelectedRun', () => {
    it('resets selectedRun and related fields', () => {
      const store = createTestStore();
      store.dispatch({ type: fetchPayrollRun.fulfilled.type, payload: mockRun });
      store.dispatch(clearSelectedRun());
      const state = store.getState().payrollRuns;
      expect(state.selectedRun).toBeNull();
      expect(state.entries).toEqual([]);
      expect(state.summary).toBeNull();
    });
  });

  describe('cancelPayrollRun', () => {
    it('updates run status on fulfilled', async () => {
      const cancelled = { ...mockRun, status: 'cancelled' };
      apiPost.mockResolvedValue({ data: { data: cancelled } });
      const store = createTestStore();
      store.dispatch({ type: fetchPayrollRuns.fulfilled.type, payload: [mockRun] });
      await store.dispatch(cancelPayrollRun({ id: '1' } as any));
      expect(store.getState().payrollRuns.runs[0].status).toBe('cancelled');
      expect(store.getState().payrollRuns.selectedRun?.status).toBe('cancelled');
      expect(store.getState().payrollRuns.loading).toBe(false);
    });
  });

  describe('clearEntries', () => {
    it('clears entries array', () => {
      const store = createTestStore();
      store.dispatch({ type: fetchPayrollEntries.fulfilled.type, payload: [{ id: 'e1' }] });
      store.dispatch(clearEntries());
      expect(store.getState().payrollRuns.entries).toEqual([]);
    });
  });

  describe('fetchPayrollRuns rejected', () => {
    it('sets loading false on rejection', () => {
      const store = createTestStore();
      store.dispatch({ type: fetchPayrollRuns.pending.type });
      expect(store.getState().payrollRuns.loading).toBe(true);
      store.dispatch({
        type: fetchPayrollRuns.rejected.type,
        payload: { message: 'Test error' },
      });
      expect(store.getState().payrollRuns.loading).toBe(false);
    });
  });
});
