import {
  createAsyncThunk,
  createSlice,
  SerializedError,
} from '@reduxjs/toolkit';
import { store, RootState } from '../../app/store';

interface FileState {
  requestId?: string;
  url?: string;
  matrix?: number[][];
  error?: SerializedError;
}

const parseTextFormatMatrix = (text: string) => {
  const lines = text.trim().split('\n');
  const matrix = lines.map((line) =>
    line
      .trim()
      .split(/\s+/)
      .map((value) => parseFloat(value))
  );
  const isValid = matrix.every(
    (row) => row.length === 9 && row.every((value) => !isNaN(value))
  );
  if (!isValid) {
    throw new Error('invalid file format');
  }
  return matrix;
};

export const loadUrl = createAsyncThunk('loadUrl', async (url: string) => {
  const response = await fetch(url);
  const data = await response.text();
  return parseTextFormatMatrix(data);
});

type LoadUrlPending = ReturnType<typeof loadUrl.pending>;
type LoadUrlFulfilled = ReturnType<typeof loadUrl.fulfilled>;
type LoadUrlRejected = ReturnType<typeof loadUrl.rejected>;

export const fileSlice = createSlice({
  name: 'file',
  initialState: {} as FileState,
  reducers: {
    reset: () => ({}),
  },
  extraReducers: {
    [loadUrl.pending.type]: (state, action: LoadUrlPending) => {
      state.requestId = action.meta.requestId;
      state.url = action.meta.arg;
      state.matrix = undefined;
      state.error = undefined;
    },
    [loadUrl.fulfilled.type]: (state, action: LoadUrlFulfilled) => {
      if (state.requestId === action.meta.requestId) {
        state.matrix = action.payload;
        state.requestId = undefined;
      }
    },
    [loadUrl.rejected.type]: (state, action: LoadUrlRejected) => {
      if (state.requestId === action.meta.requestId) {
        state.error = action.error;
        state.requestId = undefined;
      }
    },
  },
});

export const { reset } = fileSlice.actions;

export const selectLoading = (state: RootState) =>
  state.file.requestId !== undefined;
export const selectLoaded = (state: RootState) =>
  state.file.matrix !== undefined;
export const selectLoadedMatrix = (state: RootState) => state.file.matrix;

export const selectPersistentFileSlice = (state: RootState) => state.file.url;
export const serializeFileSlice = (
  url: ReturnType<typeof selectPersistentFileSlice>,
  params: URLSearchParams
) => {
  if (url !== undefined) {
    params.set('url', url);
  }
};
export const depersistFileSlice = (
  url: ReturnType<typeof selectPersistentFileSlice>,
  dispatch: typeof store.dispatch
) => {
  if (url !== undefined) {
    dispatch(loadUrl(url));
  } else {
    dispatch(reset());
  }
};
export const deserializeFileSlice = (
  params: URLSearchParams,
  dispatch: typeof store.dispatch
) => {
  const url = params.get('url');
  if (url !== null) {
    dispatch(loadUrl(url));
  } else {
    dispatch(reset());
  }
};

export default fileSlice.reducer;
