import {createAsyncThunk, createSelector, createSlice, PayloadAction, WritableDraft} from '@reduxjs/toolkit';
import {ApplicationCredentials, Channel} from '@techniker-me/pcast-api';
import PCastApiService from 'services/PCastApi.service';
import {listChannels, createChannelThunk, deleteChannelThunk, getChannelThunk, getPublisherCountThunk} from '../action/channels';

export interface IChannelsState {
  isLoading: boolean;
  channels: Channel[];
  selectedChannel: Channel | null;
  error: string | null;
}

export const initialChannelsState: IChannelsState = {
  isLoading: false,
  channels: [],
  selectedChannel: null,
  error: null
};

export const selectChannels = (state: {channels: IChannelsState}) => state.channels;

export const selectChannelList = createSelector([selectChannels], channels => channels.channels);

export const fetchChannelList = createAsyncThunk('channels/fetchChannelList', async (_, {rejectWithValue}) => {
  try {
    return PCastApiService.channels.list();
    
  } catch (error) {
    return rejectWithValue(error);
  }
});

export const fetchChannelsListPublisherStatus = createAsyncThunk(
  'channels/fetchChannelsListPublisherStatus',
  async (channels: Channel[], {rejectWithValue}) => {
    try {
      const channelResponses = await Promise.all(
        channels.map(async channel => {
          const publisherCount = await PCastApiService.channels.getPublisherCount(channel.channelId);
          return {
            ...channel,
            isActivePublisher: publisherCount > 0
          };
        })
      );

      return channelResponses as Channel[];
    } catch (error) {
      return rejectWithValue(error);
    }
  }
);

const channelsSlice = createSlice({
  name: 'channels',
  initialState: {...initialChannelsState},
  reducers: {
    initializeChannels: (state, action: PayloadAction<{pcastUri: string; applicationCredentials: ApplicationCredentials}>) => {
      PCastApiService.initialize(action.payload.pcastUri, action.payload.applicationCredentials);
      state.isLoading = false;
      state.error = null;
    },
    setChannels: (state, action: PayloadAction<Channel[]>) => {
      state.channels = action.payload as WritableDraft<Channel>[];
    },
    setIsLoading: (state, action: PayloadAction<boolean>) => {
      state.isLoading = action.payload;
    },
    setSelectedChannel: (state, action: PayloadAction<Channel | null>) => {
      state.selectedChannel = action.payload as WritableDraft<Channel> | null;
    },
    setError: (state, action: PayloadAction<string | null>) => {
      state.error = action.payload;
    }
  },
  extraReducers: builder => {
    builder
      // fetchChannelList cases
      .addCase(fetchChannelList.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(fetchChannelList.fulfilled, (state, action) => {
        state.channels = action.payload as WritableDraft<Channel[]>;
        state.isLoading = false;
        state.error = null;
      })
      .addCase(fetchChannelList.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      })
      // fetchChannelsListPublisherStatus cases
      .addCase(fetchChannelsListPublisherStatus.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(fetchChannelsListPublisherStatus.fulfilled, (state, action) => {
        state.channels = action.payload as WritableDraft<Channel[]>;
        state.isLoading = false;
        state.error = null;
      })
      .addCase(fetchChannelsListPublisherStatus.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      })
      // listChannels cases (used by the component)
      .addCase(listChannels.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(listChannels.fulfilled, (state, action) => {
        state.channels = action.payload as WritableDraft<Channel[]>;
        state.isLoading = false;
        state.error = null;
      })
      .addCase(listChannels.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      })
      // createChannelThunk cases
      .addCase(createChannelThunk.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(createChannelThunk.fulfilled, (state, _action) => {
        // Channel is already added to the list by the thunk calling listChannels
        state.isLoading = false;
        state.error = null;
      })
      .addCase(createChannelThunk.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      })
      // deleteChannelThunk cases
      .addCase(deleteChannelThunk.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(deleteChannelThunk.fulfilled, (state, _action) => {
        // Channel is already removed from the list by the thunk calling listChannels
        state.isLoading = false;
        state.error = null;
      })
      .addCase(deleteChannelThunk.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      })
      // getChannelThunk cases
      .addCase(getChannelThunk.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(getChannelThunk.fulfilled, (state, action) => {
        state.selectedChannel = action.payload as WritableDraft<Channel>;
        state.isLoading = false;
        state.error = null;
      })
      .addCase(getChannelThunk.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      })
      // getPublisherCountThunk cases
      .addCase(getPublisherCountThunk.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(getPublisherCountThunk.fulfilled, (state, action) => {
        // Update the specific channel with publisher count
        const {channelId, count} = action.payload;
        const channelIndex = state.channels.findIndex(channel => channel.channelId === channelId);
        if (channelIndex !== -1) {
          (state.channels[channelIndex] as any).publisherCount = count;
        }
        state.isLoading = false;
        state.error = null;
      })
      .addCase(getPublisherCountThunk.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      });
  }
});

export const {initializeChannels, setChannels, setIsLoading, setSelectedChannel, setError} = channelsSlice.actions;
export default channelsSlice.reducer;
