import { createSlice, type PayloadAction } from '@reduxjs/toolkit';

import { apiErrorMessage } from '../../../../utils/api';
import {
  applyFailure,
  applyPage,
  applyPending,
  applyPerPage,
  applySort,
  emptyList,
  isStaleResponse,
  type ListState,
  type PageChangePayload,
  type PerPageChangePayload,
  type SortChangePayload
} from '../listState';
import { fetchChanges, fetchChangesWithMessages, fetchFileIntegrityMonitoring } from './thunks';
import type { Change, FileIntegrityMonitoring } from './types';

const LOAD_FAILED = 'Failed to load';

export interface ChangesListState extends ListState<Change> {
  totalUnfiltered: number;
  totalWithMessages: number | null;
}

export interface HostActivityEntry {
  changes: ChangesListState;
  fileIntegrityMonitoring: ListState<FileIntegrityMonitoring>;
}

type ListKey = 'changes' | 'fileIntegrityMonitoring';

export const emptyChangesList = (): ChangesListState => ({
  ...emptyList<Change>(),
  totalUnfiltered: 0,
  totalWithMessages: null
});

const emptyEntry = (): HostActivityEntry => ({
  changes: emptyChangesList(),
  fileIntegrityMonitoring: emptyList<FileIntegrityMonitoring>()
});

export interface ActivityState {
  byHost: Record<string, HostActivityEntry>;
}

const initialState: ActivityState = {
  byHost: {}
};

const ensureEntry = (state: ActivityState, key: string): HostActivityEntry => {
  if (!state.byHost[key]) state.byHost[key] = emptyEntry();
  return state.byHost[key];
};

const activitySlice = createSlice({
  name: 'activity',
  initialState,
  reducers: {
    setListPage: (state, action: PayloadAction<PageChangePayload<ListKey>>) => {
      applyPage(ensureEntry(state, action.payload.hostkey)[action.payload.list], action.payload.page);
    },
    setListPerPage: (state, action: PayloadAction<PerPageChangePayload<ListKey>>) => {
      applyPerPage(ensureEntry(state, action.payload.hostkey)[action.payload.list], action.payload.perPage);
    },
    setListSort: (state, action: PayloadAction<SortChangePayload<ListKey>>) => {
      applySort(ensureEntry(state, action.payload.hostkey)[action.payload.list], action.payload.sort);
    }
  },
  extraReducers: builder => {
    builder
      .addCase(fetchChanges.pending, (state, action) => {
        applyPending(ensureEntry(state, action.meta.arg.hostkey).changes, action.meta.requestId);
      })
      .addCase(fetchChanges.fulfilled, (state, action) => {
        const list = ensureEntry(state, action.payload.hostkey).changes;
        if (isStaleResponse(list, action.meta.requestId)) return;
        list.loading = false;
        list.rows = action.payload.data.data ?? [];
        list.total = action.payload.data.total ?? 0;
        if (!action.meta.arg.onlyWithMessages) {
          list.totalUnfiltered = list.total;
        }
      })
      .addCase(fetchChanges.rejected, (state, action) => {
        const list = ensureEntry(state, action.meta.arg.hostkey).changes;
        if (isStaleResponse(list, action.meta.requestId)) return;
        applyFailure(list, apiErrorMessage(action.payload ?? action.error, LOAD_FAILED));
      })
      .addCase(fetchChangesWithMessages.fulfilled, (state, action) => {
        ensureEntry(state, action.payload.hostkey).changes.totalWithMessages = action.payload.data.total ?? 0;
      })
      .addCase(fetchChangesWithMessages.rejected, (state, action) => {
        ensureEntry(state, action.meta.arg.hostkey).changes.totalWithMessages = null;
      })
      .addCase(fetchFileIntegrityMonitoring.pending, (state, action) => {
        applyPending(ensureEntry(state, action.meta.arg.hostkey).fileIntegrityMonitoring, action.meta.requestId);
      })
      .addCase(fetchFileIntegrityMonitoring.fulfilled, (state, action) => {
        const list = ensureEntry(state, action.payload.hostkey).fileIntegrityMonitoring;
        if (isStaleResponse(list, action.meta.requestId)) return;
        list.loading = false;
        list.rows = action.payload.data.data ?? [];
        list.total = action.payload.data.total ?? 0;
      })
      .addCase(fetchFileIntegrityMonitoring.rejected, (state, action) => {
        const list = ensureEntry(state, action.meta.arg.hostkey).fileIntegrityMonitoring;
        if (isStaleResponse(list, action.meta.requestId)) return;
        applyFailure(list, apiErrorMessage(action.payload ?? action.error, LOAD_FAILED));
      });
  }
});

export const { setListPage, setListPerPage, setListSort } = activitySlice.actions;

export default activitySlice.reducer;
