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

import { emptyFilterState } from '../../../common/InventoryFilter';
import type { InventoryFilterState } from '../../../common/InventoryFilter';
import type { Group, GroupStats, InventoryResult, SortState } from '../../types';
import { fetchGroup, fetchStats, runInventoryQuery, saveGroupTitle } from './thunks';

const emptyResult = (): InventoryResult => ({ header: [], rows: [], rowCount: 0 });
const emptyStats = (): GroupStats => ({ os: [], versions: [], osDistribution: {} });

export interface GroupDetailState {
  error: string | null;
  // Live, editable filter state and the last-saved baseline for the dirty check.
  filterState: InventoryFilterState;
  group: Group | null;
  groupExists: boolean;
  loading: boolean;
  loadingResult: boolean;
  originalFilterState: InventoryFilterState;
  result: InventoryResult;
  // False until the first query resolves; `result` is empty, not empty-because-zero.
  resultLoaded: boolean;
  resultPage: number;
  resultRowsPerPage: number;
  resultSort: SortState;
  stats: GroupStats;
}

const initialState: GroupDetailState = {
  group: null,
  groupExists: true,
  loading: false,
  error: null,
  filterState: emptyFilterState(),
  originalFilterState: emptyFilterState(),
  result: emptyResult(),
  resultLoaded: false,
  loadingResult: false,
  resultSort: { column: '', descending: false },
  resultPage: 1,
  resultRowsPerPage: 20,
  stats: emptyStats()
};

const groupDetailSlice = createSlice({
  name: 'groupDetail',
  initialState,
  reducers: {
    resetDetail: () => initialState,
    setFilterState(state, { payload }: PayloadAction<InventoryFilterState>) {
      state.filterState = payload;
    },
    // Set both live + baseline (after loading a group and converting its filter).
    initFilterState(state, { payload }: PayloadAction<InventoryFilterState>) {
      state.filterState = payload;
      state.originalFilterState = payload;
    },
    // Commit the current state as the new baseline (after a successful save).
    commitFilterState(state) {
      state.originalFilterState = state.filterState;
    },
    clearAllFilters(state) {
      state.filterState = emptyFilterState();
    },
    cancelFilterChanges(state) {
      state.filterState = state.originalFilterState;
    },
    setResultSort(state, { payload }: PayloadAction<SortState>) {
      state.resultSort = payload;
      state.resultPage = 1;
    },
    setResultPage(state, { payload }: PayloadAction<number>) {
      state.resultPage = payload;
    },
    setResultRowsPerPage(state, { payload }: PayloadAction<number>) {
      state.resultRowsPerPage = payload;
      state.resultPage = 1;
    }
  },
  extraReducers: builder => {
    builder
      .addCase(fetchGroup.pending, state => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchGroup.fulfilled, (state, { payload }) => {
        state.loading = false;
        state.group = payload;
        state.groupExists = true;
      })
      .addCase(fetchGroup.rejected, (state, { payload }) => {
        state.loading = false;
        // 404 -> render the "group doesn't exist" state instead of an error.
        if (payload?.status === 404) {
          state.groupExists = false;
        } else {
          state.error = 'Failed to load group.';
        }
      })
      .addCase(runInventoryQuery.pending, state => {
        state.loadingResult = true;
      })
      .addCase(runInventoryQuery.fulfilled, (state, { payload }) => {
        state.loadingResult = false;
        state.resultLoaded = true;
        state.result = payload;
      })
      .addCase(runInventoryQuery.rejected, state => {
        state.loadingResult = false;
      })
      .addCase(fetchStats.fulfilled, (state, { payload }) => {
        state.stats = payload;
      })
      .addCase(saveGroupTitle.fulfilled, (state, { payload }) => {
        if (state.group) state.group.name = payload;
      });
  }
});

export const {
  resetDetail,
  setFilterState,
  initFilterState,
  commitFilterState,
  clearAllFilters,
  cancelFilterChanges,
  setResultSort,
  setResultPage,
  setResultRowsPerPage
} = groupDetailSlice.actions;

export default groupDetailSlice.reducer;
