import {
  AppNotification,
  PaginatedNotifications,
  NotificationPreference,
  UpdateNotificationPreferencesPayload,
} from '@/interface/Notification';
import { RejectedActionPayload, ServerErrorResponse } from '@/interface/Shared';
import { apiGet, apiPost, apiPut } from '@/services/api/api';
import Helpers from '@/utilities/Helpers';
import { createAsyncThunk, createSlice, Draft, PayloadAction } from '@reduxjs/toolkit';
import { message } from 'antd';
import axios, { AxiosResponse } from 'axios';
import { NotificationActions } from './actionTypes';

interface NotificationsState {
  notifications: PaginatedNotifications | null;
  unreadCount: number;
  preferences: NotificationPreference[];
  loading: boolean;
  preferencesLoading: boolean;
  error: string | null;
}

const initialState: NotificationsState = {
  notifications: null,
  unreadCount: 0,
  preferences: [],
  loading: false,
  preferencesLoading: false,
  error: null,
};

export const fetchNotifications = createAsyncThunk(
  NotificationActions.FETCH_NOTIFICATIONS,
  async (params: { page?: number; per_page?: number } = {}, { rejectWithValue }) => {
    try {
      const queryParams = new URLSearchParams();
      if (params.page) queryParams.append('page', params.page.toString());
      if (params.per_page) queryParams.append('per_page', params.per_page.toString());
      const query = queryParams.toString();
      const response = (await apiGet(
        `/notifications${query ? `?${query}` : ''}`,
      )) as AxiosResponse<PaginatedNotifications>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const fetchUnreadCount = createAsyncThunk(
  NotificationActions.FETCH_UNREAD_COUNT,
  async (_, { rejectWithValue }) => {
    try {
      const response = (await apiGet(
        '/notifications/unread-count',
      )) as AxiosResponse<{ unread_count: number }>;
      return response.data.unread_count;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const markAsRead = createAsyncThunk(
  NotificationActions.MARK_AS_READ,
  async (notificationId: string, { rejectWithValue }) => {
    try {
      await apiPost(`/notifications/${notificationId}/mark-as-read`, {});
      return notificationId;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const markAllAsRead = createAsyncThunk(
  NotificationActions.MARK_ALL_AS_READ,
  async (_, { rejectWithValue }) => {
    try {
      await apiPost('/notifications/mark-all-as-read', {});
      return true;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const fetchNotificationPreferences = createAsyncThunk(
  NotificationActions.FETCH_PREFERENCES,
  async (_, { rejectWithValue }) => {
    try {
      const response = (await apiGet(
        '/notification-preferences',
      )) as AxiosResponse<NotificationPreference[]>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const updateNotificationPreferences = createAsyncThunk(
  NotificationActions.UPDATE_PREFERENCES,
  async (payload: UpdateNotificationPreferencesPayload, { rejectWithValue }) => {
    try {
      const response = (await apiPut(
        '/notification-preferences',
        payload,
      )) as AxiosResponse<NotificationPreference[]>;
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        return rejectWithValue(error.response.data as ServerErrorResponse);
      }
      throw error;
    }
  },
);

export const NotificationSlice = createSlice({
  name: 'notifications',
  initialState,
  reducers: {
    clearNotifications: (state: Draft<NotificationsState>) => {
      state.notifications = null;
      state.error = null;
    },
  },
  extraReducers(builder) {
    builder
      .addCase(fetchNotifications.pending, (state: Draft<NotificationsState>) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(
        fetchNotifications.fulfilled,
        (state: Draft<NotificationsState>, action: PayloadAction<PaginatedNotifications>) => {
          state.loading = false;
          state.notifications = action.payload;
        },
      )
      .addCase(
        fetchNotifications.rejected,
        (state: Draft<NotificationsState>, action: RejectedActionPayload) => {
          state.loading = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(state.error, 10);
        },
      )

      .addCase(fetchUnreadCount.fulfilled, (state: Draft<NotificationsState>, action: PayloadAction<number>) => {
        state.unreadCount = action.payload;
      })

      .addCase(markAsRead.fulfilled, (state: Draft<NotificationsState>, action: PayloadAction<string>) => {
        if (state.notifications) {
          const notification = state.notifications.data.find(
            (n: AppNotification) => n.id === action.payload,
          );
          if (notification) {
            notification.read_at = new Date().toISOString();
          }
        }
        if (state.unreadCount > 0) {
          state.unreadCount -= 1;
        }
      })
      .addCase(
        markAsRead.rejected,
        (state: Draft<NotificationsState>, action: RejectedActionPayload) => {
          state.error = Helpers.handleServerError(action.payload);
          message.error(state.error, 10);
        },
      )

      .addCase(markAllAsRead.fulfilled, (state: Draft<NotificationsState>) => {
        if (state.notifications) {
          state.notifications.data.forEach((n: Draft<AppNotification>) => {
            n.read_at = new Date().toISOString();
          });
        }
        state.unreadCount = 0;
      })
      .addCase(
        markAllAsRead.rejected,
        (state: Draft<NotificationsState>, action: RejectedActionPayload) => {
          state.error = Helpers.handleServerError(action.payload);
          message.error(state.error, 10);
        },
      )

      .addCase(fetchNotificationPreferences.pending, (state: Draft<NotificationsState>) => {
        state.preferencesLoading = true;
      })
      .addCase(
        fetchNotificationPreferences.fulfilled,
        (state: Draft<NotificationsState>, action: PayloadAction<NotificationPreference[]>) => {
          state.preferencesLoading = false;
          state.preferences = action.payload;
        },
      )
      .addCase(
        fetchNotificationPreferences.rejected,
        (state: Draft<NotificationsState>, action: RejectedActionPayload) => {
          state.preferencesLoading = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(state.error, 10);
        },
      )

      .addCase(updateNotificationPreferences.pending, (state: Draft<NotificationsState>) => {
        state.preferencesLoading = true;
      })
      .addCase(
        updateNotificationPreferences.fulfilled,
        (state: Draft<NotificationsState>, action: PayloadAction<NotificationPreference[]>) => {
          state.preferencesLoading = false;
          state.preferences = action.payload;
          message.success('Notification preferences updated');
        },
      )
      .addCase(
        updateNotificationPreferences.rejected,
        (state: Draft<NotificationsState>, action: RejectedActionPayload) => {
          state.preferencesLoading = false;
          state.error = Helpers.handleServerError(action.payload);
          message.error(state.error, 10);
        },
      );
  },
});

export const { clearNotifications } = NotificationSlice.actions;
export default NotificationSlice;
