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

import {
  create{{pascalCase model}},
  delete{{pascalCase model}},
  getAll{{pascalCase model}}s,
  getOne{{pascalCase model}},
  update{{pascalCase model}},
} from '../../controllers';

import { {{pascalCase model}} } from '../../models';

import {
  Create{{pascalCase model}}Request,
  DeleteResponse,
  ErrorResponse,
  Status,
  Update{{pascalCase model}}Request,
  UpsertRequest,
} from '../../requests';

import {
  createRefresh,
  deleteRefresh,
  updateRefresh,
} from '../../utils';

export const {{camelCase model}}State: {
  all{{pascalCase model}}s: {{pascalCase model}}[];
  all{{pascalCase model}}sStatus: Status['name'];
  all{{pascalCase model}}sError?: ErrorResponse['errors'] | null;

  one{{pascalCase model}}: {{pascalCase model}};
  one{{pascalCase model}}Status: Status['name'];
  one{{pascalCase model}}Error?: ErrorResponse['errors'] | null;

  create{{pascalCase model}}: UpsertRequest<Create{{pascalCase model}}Request>;
  created{{pascalCase model}}: {{pascalCase model}};
  create{{pascalCase model}}Status: Status['name'];
  create{{pascalCase model}}Error?: ErrorResponse['errors'] | null;

  update{{pascalCase model}}: UpsertRequest<Update{{pascalCase model}}Request>;
  updated{{pascalCase model}}: {{pascalCase model}};
  update{{pascalCase model}}Status: Status['name'];
  update{{pascalCase model}}Error?: ErrorResponse['errors'] | null;

  deleted{{pascalCase model}}: DeleteResponse['data']['attributes'];
  delete{{pascalCase model}}Status: Status['name'];
  delete{{pascalCase model}}Error?: ErrorResponse['errors'] | null;
} = {
  all{{pascalCase model}}s: [],
  all{{pascalCase model}}sStatus: 'idle',
  one{{pascalCase model}}: {},
  one{{pascalCase model}}Status: 'idle',
  create{{pascalCase model}}: {
    data: {
      attributes: {},
    },
  },
  created{{pascalCase model}}: {},
  create{{pascalCase model}}Status: 'idle',
  update{{pascalCase model}}: {
    data: {
      attributes: {},
    },
  },
  updated{{pascalCase model}}: {},
  update{{pascalCase model}}Status: 'idle',
  deleted{{pascalCase model}}: {
    success: false,
    purged: [],
  },
  delete{{pascalCase model}}Status: 'idle',
};

export const {
  actions: {
    setCreate{{pascalCase model}},
    resetCreate{{pascalCase model}},
    setUpdate{{pascalCase model}},
    resetUpdate{{pascalCase model}},
  },
  reducer: {{pascalCase model}}Reducer,
} = createSlice({
  name: '{{camelCase model}}',
  initialState: {{camelCase model}}State,
  reducers: {
    setCreate{{pascalCase model}}(state, {
      payload
    }: PayloadAction<UpsertRequest<Create{{pascalCase model}}Request>>) {
      state.create{{pascalCase model}} = merge(state.create{{pascalCase model}}, payload);
    },
    resetCreate{{pascalCase model}} (state) {
      state.create{{pascalCase model}} = {{camelCase model}}State.create{{pascalCase model}};
    },
    setUpdate{{pascalCase model}}(state, {
      payload
    }: PayloadAction<UpsertRequest<Update{{pascalCase model}}Request>>) {
      state.update{{pascalCase model}} = merge(state.update{{pascalCase model}}, payload);
    },
    resetUpdate{{pascalCase model}} (state) {
      state.update{{pascalCase model}} = {{camelCase model}}State.update{{pascalCase model}};
    },
  },
  extraReducers: builder => {
    builder
      .addCase(
        getAll{{pascalCase model}}s.pending,
        state => {
          state.all{{pascalCase model}}sStatus = 'loading';
        }
      )
      .addCase(
        getAll{{pascalCase model}}s.fulfilled,
        (state, { payload }) => {
          state.all{{pascalCase model}}sStatus = 'success';

          payload.data.forEach(({
            id,
            attributes,
            included,
          }) => {
            state.all{{pascalCase model}}s.push({ id, ...attributes, included });
          });
        }
      )
      .addCase(
        getAll{{pascalCase model}}s.rejected,
        (state, { payload }) => {
          state.all{{pascalCase model}}sStatus = 'failed';
          state.all{{pascalCase model}}sError = payload?.errors;
        }
      )
      .addCase(
        getOne{{pascalCase model}}.pending,
        state => {
          state.one{{pascalCase model}}Status = 'loading';
        }
      )
      .addCase(
        getOne{{pascalCase model}}.fulfilled,
        (state, {
          payload: {
            data: {
              id,
              attributes,
              included,
            },
          },
        }) => {
          state.one{{pascalCase model}}Status = 'success';
          state.one{{pascalCase model}} = { id, ...attributes, included };
        }
      )
      .addCase(
        getOne{{pascalCase model}}.rejected,
        (state, { payload }) => {
          state.one{{pascalCase model}}Status = 'failed';
          state.one{{pascalCase model}}Error = payload?.errors;
        }
      )
      .addCase(
        create{{pascalCase model}}.pending,
        state => {
          state.create{{pascalCase model}}Status = 'loading';
        }
      )
      .addCase(
        create{{pascalCase model}}.fulfilled,
        (state, {
          payload: {
            data: {
              id,
              attributes,
              included,
            },
          },
        }) => {
          state.create{{pascalCase model}}Status = 'success';
          state.created{{pascalCase model}} = { id, ...attributes, included };

          createRefresh(
            state.all{{pascalCase model}}s,
            state.created{{pascalCase model}}
          );
        }
      )
      .addCase(
        create{{pascalCase model}}.rejected,
        (state, { payload }) => {
          state.create{{pascalCase model}}Status = 'failed';
          state.create{{pascalCase model}}Error = payload?.errors;
        }
      )
      .addCase(
        update{{pascalCase model}}.pending,
        state => {
          state.update{{pascalCase model}}Status = 'loading';
        }
      )
      .addCase(
        update{{pascalCase model}}.fulfilled,
        (state, {
          payload: {
            data: {
              id,
              attributes,
              included,
            },
          },
        }) => {
          state.update{{pascalCase model}}Status = 'success';
          state.updated{{pascalCase model}} = { id, ...attributes, included };

          updateRefresh(
            state.all{{pascalCase model}}s,
            state.updated{{pascalCase model}},
            id
          );
        }
      )
      .addCase(
        update{{pascalCase model}}.rejected,
        (state, { payload }) => {
          state.update{{pascalCase model}}Status = 'failed';
          state.update{{pascalCase model}}Error = payload?.errors;
        }
      )
      .addCase(
        delete{{pascalCase model}}.pending,
        state => {
          state.delete{{pascalCase model}}Status = 'loading';
        }
      )
      .addCase(
        delete{{pascalCase model}}.fulfilled,
        (state, {
          payload: {
            data: {
              attributes,
              attributes: {
                purged,
              },
            },
          },
        }) => {
          state.delete{{pascalCase model}}Status = 'success';
          state.deleted{{pascalCase model}} = attributes;

          deleteRefresh(state.all{{pascalCase model}}s, purged);
        }
      )
      .addCase(
        delete{{pascalCase model}}.rejected,
        (state, { payload }) => {
          state.delete{{pascalCase model}}Status = 'failed';
          state.delete{{pascalCase model}}Error = payload?.errors;
        }
      );
  },
});
