import {createSlice, PayloadAction, createAsyncThunk, createSelector} from '@reduxjs/toolkit';
import AuthenticationService from '../../services/Authentication.service';
import {PhenixWebSocketStatusType} from 'services/net/websockets/PhenixWebSocketStatus';
import {IPhenixWebSocketResponse} from 'services/net/websockets/PhenixWebSocket';
import PCastApiService from 'services/PCastApi.service';
import ChannelService from 'services/Channel.service';

export interface IAuthenticationState {
  applicationId: string | null;
  secret: string | null;
  isAuthenticated: boolean;
  isLoading: boolean;
  status: PhenixWebSocketStatusType;
  error: string | null;
  sessionId: string | null;
  roles: string[];
}

const initialAuthenticationState: IAuthenticationState = {
  applicationId: null,
  sessionId: null,
  isAuthenticated: false,
  isLoading: false,
  status: 'Offline',
  roles: [],
  error: null,
  secret: null
};

// Memoized selectors
export const selectAuthentication = (state: {authentication: IAuthenticationState}) => state.authentication;

export const selectIsLoading = createSelector([selectAuthentication], authentication => authentication.isLoading);

export const selectIsAuthenticated = createSelector([selectAuthentication], authentication => authentication.isAuthenticated);

export const selectError = createSelector([selectAuthentication], authentication => authentication.error);

export const selectStatus = createSelector([selectAuthentication], authentication => authentication.status);

export const selectCredentials = createSelector([selectAuthentication], authentication => ({
  id: authentication.applicationId,
  secret: authentication.secret
}));

export const selectSessionInfo = createSelector([selectAuthentication], authentication => ({
  sessionId: authentication.sessionId,
  roles: authentication.roles
}));

export const selectApplicationId = createSelector([selectAuthentication], authentication => authentication.applicationId);

export const selectSecret = createSelector([selectAuthentication], authentication => authentication.secret);

export const selectSessionId = createSelector([selectAuthentication], authentication => authentication.sessionId);

export const selectRoles = createSelector([selectAuthentication], authentication => authentication.roles);

export const selectHasRole = createSelector([selectAuthentication, (_, role: string) => role], (authentication, role) => authentication.roles.includes(role));

export const selectIsOnline = createSelector([selectAuthentication], authentication => authentication.status === 'Online');

const authenticateCredentialsThunk = createAsyncThunk<IPhenixWebSocketResponse, {applicationId: string; secret: string}>(
  'authentication/authenticate',
  async (credentials, {rejectWithValue}) => {
    try {
      const response = await AuthenticationService.authenticate(credentials.applicationId, credentials.secret);

      return response as IPhenixWebSocketResponse;
    } catch (error) {
      // Convert error to serializable format
      const errorMessage = error instanceof Error ? error.message : 'Authentication failed';

      return rejectWithValue(errorMessage);
    }
  }
);

const signoutThunk = createAsyncThunk('authentication/signout', async (_, {rejectWithValue}) => {
  try {
    return await AuthenticationService.signout();
  } catch (error) {
    // Convert error to serializable format
    const errorMessage = error instanceof Error ? error.message : 'Signout failed';

    return rejectWithValue(errorMessage);
  }
});

const authenticationSlice = createSlice({
  name: 'authentication',
  initialState: {...initialAuthenticationState},
  reducers: {
    setIsLoading: (state, action: PayloadAction<boolean>) => {
      state.isLoading = action.payload;
    },
    setCredentials: (state, action: PayloadAction<{applicationId: string; secret: string}>) => {
      state.applicationId = action.payload.applicationId;
      state.secret = action.payload.secret;
    },
    clearState: state => {
      state.applicationId = null;
      state.sessionId = null;
      state.isAuthenticated = false;
      state.isLoading = false;
      state.error = null;
      state.secret = null;
      state.status = 'Offline';
      state.roles = [];
    },
    setSessionId: (state, action: PayloadAction<string>) => {
      state.sessionId = action.payload;
    },
    setError: (state, action: PayloadAction<string | null>) => {
      state.error = action.payload;
    },
    setUnauthorized: state => {
      state.isAuthenticated = false;
      state.isLoading = false;
      state.error = 'Unauthorized';
      state.secret = null;
      state.status = 'Offline';
      state.roles = [];
    }
  },
  extraReducers: builder => {
    builder
      .addCase(authenticateCredentialsThunk.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(authenticateCredentialsThunk.fulfilled, (state, action) => {
        const authenticationResponse = action.payload;

        if (authenticationResponse.status === 'ok') {
          state.applicationId = authenticationResponse.applicationId ?? null;
          state.sessionId = authenticationResponse.sessionId ?? null;
          state.isAuthenticated = true;
          state.roles = authenticationResponse.roles ?? [];
          state.status = 'Online';
          state.isLoading = false;

          PCastApiService.initialize('https://pcast-stg.phenixrts.com', {
            id: state.applicationId ?? 'phenixrts.com-alex.zinn',
            secret: state.secret ?? ''
          });
          ChannelService.initializeWithPCastApi(PCastApiService.channels);
        } else {
          state.applicationId = null;
          state.sessionId = null;
          state.isAuthenticated = false;
          state.secret = null;
          state.roles = [];
          state.status = 'Offline';
          state.error = 'Invalid credentials. Please check your Application ID and Secret.';
          state.isLoading = false;
        }
      })
      .addCase(authenticateCredentialsThunk.rejected, (state, action) => {
        state.applicationId = null;
        state.sessionId = null;
        state.isAuthenticated = false;
        state.isLoading = false;
        state.error = (action.payload as string) || 'Authentication failed';
        state.secret = null;
        state.status = 'Offline';
        state.roles = [];
      })
      .addCase(signoutThunk.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(signoutThunk.fulfilled, state => {
        state.isAuthenticated = false;
        state.isLoading = false;
        state.error = null;
        state.secret = null;
        state.status = 'Offline';
        state.roles = [];
      })
      .addCase(signoutThunk.rejected, (state, action) => {
        state.isAuthenticated = false;
        state.isLoading = false;
        state.error = (action.payload as string) || 'Signout failed';
        state.secret = null;
        state.status = 'Offline';
        state.roles = [];
      });
  }
});

export const {setUnauthorized, setIsLoading, setCredentials, clearState, setSessionId, setError} = authenticationSlice.actions;
export {authenticateCredentialsThunk};
export default authenticationSlice.reducer;
