import {
  EntityId,
  PayloadAction,
  createAsyncThunk,
  createEntityAdapter,
  createSlice
} from '@reduxjs/toolkit';
import { RootState } from './rootReducer';
import {clearTable, setIsOnDetailSearch, setTable} from './tableSlice';
import { setNodeByNodeID } from './nodeSlice';
import axios from 'B/axios';

function cleanSearchType(searchType) {
  const searchStr = JSON.stringify(searchType);
  const search = JSON.parse(searchStr);
  const keylist = ['taskname', 'maincomment', 'comment', 'name', 'value'];
  for (let key of keylist) {
    const isEmpty = search[key].search.trim().length === 0;
    if (isEmpty) {
      search[key].search = '';
    }
  }

  return search;
}

export const getKeysList = createAsyncThunk(
    'monitor/get_keys_list',
    async ({nodeId, nodeType}: {nodeId: number, nodeType: string}) => {
      const res = await axios.get('/c/keys-list', {params: {id: nodeId, type: nodeType}});

      return { doneTaskState: res.data.doneTaskState, openTaskState: res.data.openTaskState };
    }
);

type SearchSetting = {
  id: number;
  name: string;
  settings: any;
  isPrivate: boolean;
};

const searchSettingsAdaptor = createEntityAdapter<SearchSetting>();
export const searchSettingSelector = searchSettingsAdaptor.getSelectors();

export const getCustomSearch = createAsyncThunk(
  'monitor/get_search',
  async () => {
    const res = await axios.get('/monitorfilters');

    return res.data;
  }
);

export const saveSearch = createAsyncThunk(
  'monitor/create_search',
  async (_args, thunkAPI) => {
    const rState = thunkAPI.getState() as RootState;
    if (rState.node === null) {
      return thunkAPI.rejectWithValue('noNode');
    }

    const searchDrawer = rState.searchDrawer;
    const name = searchDrawer.searchSettingName;
    const search = cleanSearchType(searchDrawer.search);
    const settings = {
      nodeID: rState.node.nodeID,
      timespan: searchDrawer.timespan,
      status: searchDrawer.ticketStatus,
      search: search,
      searchArea: searchDrawer.searchType,
      searchKeyword: searchDrawer.searchString,
      areAllDepartmentsSelected: searchDrawer.areAllDepartmentsSelected
    };
    const isPrivate = searchDrawer.isSavedSearchPrivate;

    await axios.post('/monitorfilters', {
      isPrivate,
      name,
      settings
    });
    await thunkAPI.dispatch(getCustomSearch());
  }
);

export const updateSearch = createAsyncThunk(
  'monitor/update_search',
  async (_args, thunkAPI) => {
    const rootState = thunkAPI.getState() as RootState;

    const searchDrawer = rootState.searchDrawer;
    const name = searchDrawer.searchSettingName;
    const id = searchDrawer.selectedSavedSearch;
    const search = cleanSearchType(searchDrawer.search);
    const settings = {
      nodeID: rootState.node.nodeID,
      timespan: searchDrawer.timespan,
      status: searchDrawer.ticketStatus,
      searchArea: searchDrawer.searchType,
      searchKeyword: searchDrawer.searchString,
      search: search,
      areAllDepartmentsSelected: searchDrawer.areAllDepartmentsSelected
    };
    const isPrivate = searchDrawer.isSavedSearchPrivate;

    await axios.put(`/monitorfilters/${id}`, {
      isPrivate,
      name,
      settings
    });

    await thunkAPI.dispatch(getCustomSearch());
  }
);

export const deleteSearch = createAsyncThunk(
  'monitor/delete_search',
  async (id: number | string, thunkAPI) => {
    await axios.delete(`/monitorfilters/${id}`);
    await thunkAPI.dispatch(getCustomSearch());
  }
);

const searchDetails = createAsyncThunk(
  'monitor/search_details',
  async (areAllDepartmentsSelected: boolean, thunkAPI) => {
    const s = (await thunkAPI.getState()) as RootState;
    const {
      timespan,
      searchType,
      searchString,
      ticketStatus,
      search
    } = s.searchDrawer;

    let ticketStatusParams = [];
    if (ticketStatus.open) {
      ticketStatusParams.push('open');
    }
    if (ticketStatus.closed) {
      ticketStatusParams.push('close');
    }

    let params = {
      timespan,
      searchString,
      searchType,
      ticketStatus: ticketStatusParams,
      search
    };

    if (!areAllDepartmentsSelected) {
      const nodeType = s.node.type;
      const nodeID = s.node.id;

      switch (nodeType) {
        case 'department':
          params['ber'] = nodeID;
          break;
        case 'GRP':
          params['grp'] = nodeID;
          break;
        default:
          params['agb'] = nodeID;
          break;
      }  
    }

    const r = await axios.post('/c/detail-search', { ...params });
    const d = thunkAPI.dispatch;
    d(clearTable());
    d(setIsOnDetailSearch(true));
    d(setTable(r.data.table));
  }
);

const timeSpanSelector = {
  w1: '1w',
  m1: '1m',
  m2: '2m',
  m3: '3m',
  y1: '1y',
  y2: '2y',
  y3: '3y',
  y4: '4y',
  y5: '5y',
  yAll: '50y'
};

const searchTypeState = {
  taskname: {
    search: '',
    andOr: 'or'
  },
  creator: {
    search: '',
    andOr: 'or'
  },
  maincomment: {
    search: '',
    andOr: 'or'
  },
  comment: {
    search: '',
    andOr: 'or'
  },
  name: {
    search: '',
    andOr: 'or'
  },
  value: {
    search: '',
    andOr: 'and'
  }
};

const initialState = {
  search: searchTypeState,
  areAllDepartmentsSelected: false,
  isOpen: false,
  timespan: timeSpanSelector.yAll,
  searchType: [],
  searchString: '',
  ticketStatus: { open: true, closed: true },
  searchSettings: searchSettingsAdaptor.getInitialState(),
  searchSettingName: '',
  isSavedSearchPrivate: false,
  isNewSearchDialogOpen: false,
  isUpdateSearchDialogOpen: false,
  selectedSavedSearch: '',
  isSelectItemInTreeError: false,
  keysList: { doneTaskState: [], openTaskState: [] }
};
const slice = createSlice({
  name: 'monitorpage/searchDrawer',
  initialState,
  reducers: {
    closeDrawer() {
      return initialState;
    },
    openDrawer(state) {
      state.isOpen = true;
    },
    toggleDrawer(state) {
      if (state.isOpen) {
        return initialState;
      } else {
        state.isOpen = true;
      }
    },
    setAreAllDepartmentsSelected(state, { payload }) {
      state.areAllDepartmentsSelected = payload;
    },
    setSearchString(
      state,
      { payload }: PayloadAction<{ name: string; value: string }>
    ) {
      const isInvalidField = state.search[payload.name] === undefined;
      if (isInvalidField) return;
      state.search[payload.name].search = payload.value;
    },
    setStatusFilter(
      state,
      { payload }: PayloadAction<{ name: string; checked: boolean }>
    ) {
      state.ticketStatus[payload.name] = payload.checked;
    },
    setSearchType(state, { payload }: PayloadAction<string>) {
      const isInvalidSearchParams = state.search[payload] === undefined;
      if (isInvalidSearchParams) return;
      state.search[payload].isChecked = !state.search[payload].isChecked;
      const isChecked = state.search[payload].isChecked;
      if (isChecked === false) {
        state.search[payload].search = '';
      }
    },
    setAndOr(
      state,
      { payload }: PayloadAction<{ name: string; value: string }>
    ) {
      switch (payload.name) {
        case 'taskname-andor':
          state.search.taskname.andOr = payload.value;
          break;
        case 'maincomment-andor':
          state.search.maincomment.andOr = payload.value;
          break;
        case 'comment-andor':
          state.search.comment.andOr = payload.value;
          break;
        case 'name-andor':
          state.search.name.andOr = payload.value;
          break;
        case 'value-andor':
          state.search.value.andOr = payload.value;
          break;
        default:
          break;
      }
    },
    setTimeSpan(state, { payload }: PayloadAction<string>) {
      state.timespan = payload;
    },
    setNewSettingName(state, { payload }: PayloadAction<string>) {
      state.searchSettingName = payload;
    },
    setSavedSearchAsPublicOrPrivate(state, { payload }: PayloadAction<boolean>) {
      state.isSavedSearchPrivate = payload;
    },
    searchSettingSelected(state, { payload }: PayloadAction<string>) {
      state.selectedSavedSearch = payload;
    },
    searchSettingAdded(state, { payload }: PayloadAction<SearchSetting>) {
      searchSettingsAdaptor.addOne(state.searchSettings, payload);
    },
    searchSettingUpdated(
      state,
      { payload }: PayloadAction<Partial<SearchSetting>>
    ) {
      searchSettingsAdaptor.updateOne(state.searchSettings, {
        id: payload.id,
        changes: payload
      });
    },
    searchSettingRemove(state, { payload }: PayloadAction<SearchSetting>) {
      searchSettingsAdaptor.removeOne(state.searchSettings, payload.id);
    },
    openNewSettingDialog(state) {
      state.isNewSearchDialogOpen = true;
    },
    closeNewSettingDialog(state) {
      state.isNewSearchDialogOpen = false;
    },
    setItemNotSelectedInTreeError(state, { payload }: PayloadAction<boolean>) {
      state.isSelectItemInTreeError = payload;
    },
    setSearchSettings(state, { payload }) {
      let tmp = JSON.parse(JSON.stringify(payload.search));
      if (tmp.taskname === undefined) {
        tmp.taskname = {search: '', andOr: 'or'};
      }
      if (tmp.creator === undefined) {
        tmp.creator = {search: '', andOr: 'or'};
      }
      state.search = tmp;
      state.searchSettingName = payload.name;
      state.timespan = payload.timespan;
      state.ticketStatus = payload.status;
    },
    toggleUpdateSearchDialog(state) {
      state.isUpdateSearchDialogOpen = !state.isUpdateSearchDialogOpen;
      state.searchSettingName = searchSettingSelector.selectById(
        state.searchSettings,
        state.selectedSavedSearch
      ).name;
    },
    updateCustomSearchName(state, { payload }) {
      state.searchSettingName = payload;
    }
  },
  extraReducers: (builder) => {
    builder.addCase(getKeysList.fulfilled, (state, action) => {
      state.keysList = action.payload;
    });

    builder.addCase(saveSearch.fulfilled, (state) => {
      state.isNewSearchDialogOpen = false;
      state.searchSettingName = '';
    });

    builder.addCase(updateSearch.fulfilled, (state) => {
      state.isUpdateSearchDialogOpen = false;
      state.searchSettingName = '';
    });

    builder.addCase(getCustomSearch.fulfilled, (state, action) => {
      const data = action.payload;
      const searchSettings: SearchSetting[] = data.map((v) => {
        return { ...v, settings: JSON.parse(v.settings), isPrivate: v.user_id != null };
      });

      searchSettingsAdaptor.removeAll(state.searchSettings);
      searchSettingsAdaptor.addMany(state.searchSettings, searchSettings);
    });

    builder.addCase(deleteSearch.fulfilled, (state, action) => {
      state.selectedSavedSearch = '';
    });
  }
});

export const loadSavedSearch = createAsyncThunk(
  'monitor/custom_search/load_saved_search',
  async (selectedSearchID: EntityId, thunkAPI) => {
    const rootState = thunkAPI.getState() as RootState;
    const d = thunkAPI.dispatch;
    const actions = slice.actions;

    const search = searchSettingSelector.selectById(
      rootState.searchDrawer.searchSettings,
      selectedSearchID
    );
    if (search === undefined) return;

    const setting = search.settings as any;

    await d(setNodeByNodeID(setting.nodeID));
    d(actions.searchSettingSelected(selectedSearchID as string));
    d(actions.setSearchSettings({ ...setting, name: search.name }));
    d(actions.setAreAllDepartmentsSelected(setting.areAllDepartmentsSelected ?? false));
    d(actions.setSavedSearchAsPublicOrPrivate(search.isPrivate));
    await d(searchDetails(setting.areAllDepartmentsSelected ?? false));
  }
);

export { timeSpanSelector, searchDetails };
export const actions = slice.actions;
export const {
  setAreAllDepartmentsSelected,
  setTimeSpan,
  setSearchString,
  setStatusFilter,
  setSearchType,
  closeDrawer,
  openDrawer,
  setNewSettingName,
  toggleDrawer,
  setAndOr,
  setSavedSearchAsPublicOrPrivate
} = slice.actions;
export default slice.reducer;
