import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import { ThunkSuccess, ThunkError, IUserResponse } from "../types";
import { IEditUserForm } from "../../features/UserRoles/User.interfaces";
import { RootState } from "../store";
import toast from "react-hot-toast";

interface TokenInfo {
  expiresAt: string;
  timeUntilExpiration: number;
  expiresInHours: number;
}

interface AuthState {
  isLoggedIn: boolean;
  authMessage: string;
  status: "idle" | "loading" | "succeeded" | "failed";
  error: string | null;
  user: IUserResponse | null;
  tokenInfo: TokenInfo | null;
  mustChangePassword: boolean;
  addedUser: IUserResponse | null;
}

const initialState: AuthState = {
  isLoggedIn: false,
  authMessage: "",
  status: "idle",
  error: null,
  user: null,
  tokenInfo: null,
  mustChangePassword: false,
  addedUser: null,
};

// ---------------- Login ----------------
export const login = createAsyncThunk<
  ThunkSuccess<{
    user: IUserResponse | null;
    tokenInfo: TokenInfo | null;
    mustChangePassword: boolean;
  }>,
  { email: string; password: string },
  { rejectValue: ThunkError }
>(
  "auth/login",
  async (
    { email, password }: { email: string; password: string },
    { rejectWithValue }
  ) => {
    try {
      const res = await fetch("/api/auth/login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, password }),
      });

      const data = await res.json();
      if (!res.ok) {
        toast.error(data.message || "Login failed");
        return rejectWithValue((data.message as string) || "Login failed");
      }

      // After login, validate session right away
      const validateRes = await fetch("/api/auth/validate", { method: "POST" });
      const validateData = await validateRes.json();

      toast.success(data.message || "Login successful");

      return {
        message: data.message || "Login successful",
        user: data.user || null,
        tokenInfo: validateData.tokenInfo || null,
        mustChangePassword: data.must_change_password || false,
      } as ThunkSuccess<{
        user: IUserResponse | null;
        tokenInfo: TokenInfo | null;
        mustChangePassword: boolean;
      }>;
    } catch (err: unknown) {
      return rejectWithValue(
        (err as Error).message || "Network error during login"
      );
    }
  }
);

// ---------------- Logout ----------------
export const logout = createAsyncThunk<
  ThunkSuccess,
  void,
  { rejectValue: ThunkError }
>("auth/logout", async (_, { rejectWithValue }) => {
  try {
    const res = await fetch("/api/auth/logout", { method: "DELETE" });
    const data = await res.json();
    if (!res.ok)
      return rejectWithValue((data.message as string) || "Logout failed");

    // Persisted state will be cleared by reducers below; avoid direct persistor usage here.

    return { message: data.message || "Logged out successfully" };
  } catch (err: unknown) {
    return rejectWithValue(
      (err as Error).message || "Network error during logout"
    );
  }
});

// ---------------- Auto Logout ----------------
export const autoLogout = createAsyncThunk<
  ThunkSuccess,
  string,
  { rejectValue: ThunkError }
>("auth/autoLogout", async (reason: string, { rejectWithValue }) => {
  try {
    await fetch("/api/auth/logout", { method: "DELETE" });
    return { message: `Auto-logout: ${reason}` };
  } catch (err: unknown) {
    return rejectWithValue((err as Error).message || "Auto-logout failed");
  }
});

// ---------------- Change Password ----------------
export const changePassword = createAsyncThunk<
  ThunkSuccess<{ success: boolean }>,
  { email: string; newPassword: string; currentPassword?: string },
  { rejectValue: ThunkError }
>(
  "auth/changePassword",
  async ({ email, newPassword, currentPassword }, { rejectWithValue }) => {
    try {
      const res = await fetch("/api/auth/change-password", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, newPassword, currentPassword }),
      });

      const data = await res.json();
      if (!res.ok)
        return rejectWithValue(
          (data.message as string) || "Password change failed"
        );

      return {
        success: data.success,
        message: data.message || "Password changed successfully",
      };
    } catch (err: unknown) {
      return rejectWithValue(
        (err as Error).message || "Network error during password change"
      );
    }
  }
);

// ---------------- Unified Validate Auth ----------------
export const validateAuth = createAsyncThunk<
  { tokenInfo: TokenInfo | null },
  void,
  { rejectValue: ThunkError }
>("auth/validate", async (_, { rejectWithValue, dispatch }) => {
  try {
    const res = await fetch("/api/auth/validate", { method: "POST" });
    const data = await res.json();

    if (!res.ok || !data.success) {
      toast.error(data.message || "Session invalid or expired");
      dispatch(autoLogout("Session invalid or expired"));
      return rejectWithValue("Session invalid or expired");
    }

    return {
      tokenInfo: data.tokenInfo || null,
    };
  } catch (err: unknown) {
    return rejectWithValue(
      (err as Error).message || "Network error during validation"
    );
  }
});

// TODO - Creaye a new thunk to update the user stuff

// ---------------- Add User ----------------
export const addUser = createAsyncThunk<
  ThunkSuccess<{ user: IUserResponse; success: boolean }>,
  IEditUserForm,
  { rejectValue: ThunkError; state: RootState }
>("auth/addUser", async (userData, { rejectWithValue, getState }) => {
  try {
    const state = getState();
    const currentUserId = state.auth.user?.id;
    console.log("[DEBUG] [ADD-USER] [currentUserId]: ", currentUserId);
    const res = await fetch("/api/auth/register", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ ...userData, creator: currentUserId || "" }),
    });

    const data = await res.json();

    console.log("[DEBUG] [ADD-USER] [data]: ", data);

    if (!res.ok) {
      return rejectWithValue(data.message || "Failed to create user");
    }

    return {
      success: data.success,
      message: data.message || "User created successfully",
      user: data.user,
    } as ThunkSuccess<{ user: IUserResponse; success: boolean }>;
  } catch (err: unknown) {
    return rejectWithValue(
      (err as Error).message || "Network error during user creation"
    );
  }
});

// ---------------- Update User Details ----------------
export const updateUserDetails = createAsyncThunk<
  ThunkSuccess<{ user: IUserResponse }>,
  { id: string; updates: Partial<IUserResponse> },
  { rejectValue: ThunkError }
>("auth/updateUserDetails", async ({ id, updates }, { rejectWithValue }) => {
  try {
    const res = await fetch(`/api/dashboard/admin/users/${id}`, {
      method: "PATCH",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(updates),
    });

    const data = await res.json();

    if (!res.ok) {
      return rejectWithValue(data.message || "Failed to update user details");
    }

    return {
      message: data.message || "User details updated successfully",
      user: data.user,
    };
  } catch (err: unknown) {
    return rejectWithValue(
      (err as Error).message || "Network error during user update"
    );
  }
});

// ---------------- Slice ----------------
const authSlice = createSlice({
  name: "auth",
  initialState,
  reducers: {
    setAuthMessage: (state, action: PayloadAction<string>) => {
      state.authMessage = action.payload;
    },
    clearAuthMessage: state => {
      state.authMessage = "";
    },
    clearAddedUser: state => {
      state.addedUser = null;
    },
  },
  extraReducers: builder => {
    builder
      // Login
      .addCase(login.pending, state => {
        state.status = "loading";
        state.authMessage = "Attempting login...";
      })
      .addCase(login.fulfilled, (state, action) => {
        state.status = "succeeded";
        state.isLoggedIn = true;
        state.authMessage = action.payload.message;
        state.user = action.payload.user;
        state.tokenInfo = action.payload.tokenInfo;
        state.mustChangePassword = action.payload.mustChangePassword;
      })
      .addCase(login.rejected, (state, action) => {
        state.status = "failed";
        state.isLoggedIn = false;
        state.error = action.payload as string;
        state.authMessage = action.payload as string;
      })
      // Logout
      .addCase(logout.fulfilled, (state, action) => {
        return {
          ...initialState,
          status: "succeeded",
          authMessage: action.payload.message,
        };
      })
      .addCase(logout.rejected, (state, action) => {
        state.status = "failed";
        state.error = action.payload as string;
        state.authMessage = action.payload as string;
      })
      // Auto Logout
      .addCase(autoLogout.fulfilled, (state, action) => {
        return {
          ...initialState,
          status: "succeeded",
          isLoggedIn: false,
          authMessage: action.payload.message,
        };
      })
      .addCase(autoLogout.rejected, (state, action) => {
        state.status = "failed";
        state.error = action.payload as string;
      })
      // Change Password
      .addCase(changePassword.pending, state => {
        state.status = "loading";
        state.authMessage = "Changing password...";
      })
      .addCase(changePassword.fulfilled, (state, action) => {
        state.status = "succeeded";
        state.authMessage = action.payload.message;
        state.mustChangePassword = action.payload.success;
      })
      .addCase(changePassword.rejected, (state, action) => {
        state.status = "failed";
        state.error = action.payload as string;
        state.authMessage = action.payload as string;
      })
      // Validate Auth
      .addCase(validateAuth.pending, state => {
        state.status = "loading";
      })
      .addCase(validateAuth.fulfilled, (state, action) => {
        state.status = "succeeded";
        state.isLoggedIn = true;
        state.tokenInfo = action.payload.tokenInfo;
      })
      .addCase(validateAuth.rejected, (state, action) => {
        state.status = "failed";
        state.isLoggedIn = false;
        state.tokenInfo = null;
        state.error = action.payload as string;
      })
      // Add User
      .addCase(addUser.pending, state => {
        state.status = "loading";
        state.authMessage = "Creating user...";
        state.addedUser = null;
      })
      .addCase(addUser.fulfilled, (state, action) => {
        state.status = "succeeded";
        state.authMessage = action.payload.message;
        state.addedUser = action.payload.user;
      })
      .addCase(addUser.rejected, (state, action) => {
        state.status = "failed";
        state.error = action.payload as string;
        state.authMessage = action.payload as string;
        state.addedUser = null;
      })
      // Update User Details
      .addCase(updateUserDetails.pending, state => {
        state.status = "loading";
        state.authMessage = "Updating user details...";
      })
      .addCase(updateUserDetails.fulfilled, (state, action) => {
        state.status = "succeeded";
        state.user = action.payload.user;
        state.authMessage = action.payload.message;
      })
      .addCase(updateUserDetails.rejected, (state, action) => {
        state.status = "failed";
        state.error = action.payload as string;
        state.authMessage = action.payload as string;
      });
  },
});

export const { setAuthMessage, clearAuthMessage, clearAddedUser } =
  authSlice.actions;
export default authSlice.reducer;
