import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import Cookies from "js-cookie";
import { authService } from "@/services/authService";
import { getStoredPlayerId } from "@/services/oneSignalService";
import {
  firebaseEmailSignIn,
  firebaseCreateAccount,
  firebasePasswordReset,
  signOutFromFirebase,
} from "@/services/firebaseAuthService";
import { AuthState, AuthUser, LoginPayload, LoginType, RegisterPayload } from "@/types/auth";

const COOKIE_OPTS: Cookies.CookieAttributes = { expires: 7, secure: process.env.NODE_ENV === "production", sameSite: "strict" };

function deviceFields(): { device_type: string; device_token: string } {
  return {
    device_type: "5", // 5 = Chrome Web Push (OneSignal device type enum)
    device_token: getStoredPlayerId() ?? "",
  };
}

const persistAuth = (user: AuthUser) => {
  Cookies.set("user_id", String(user.id), COOKIE_OPTS);
  Cookies.set("channel_id", user.channel_id ?? "", COOKIE_OPTS);
  if (user.stream_token) {
    Cookies.set("auth_token", user.stream_token, COOKIE_OPTS);
  }
};

const clearAuth = () => {
  Cookies.remove("user_id");
  Cookies.remove("channel_id");
  Cookies.remove("auth_token");
};

const initialState: AuthState = {
  user: null,
  isAuthenticated: false,
  isLoading: false,
  error: null,
};

function extractValidUser(
  res: { status: number; message?: string; result: AuthUser | AuthUser[] },
  fallbackMsg: string,
): AuthUser | string {
  if (res.status !== 200 && res.status !== 1) return res.message ?? fallbackMsg;
  const user: AuthUser = Array.isArray(res.result) ? res.result[0] : res.result;
  if (!user || !user.id || Number(user.id) === 0)
    return res.message ?? "Invalid credentials. Please try again.";
  return user;
}

/* ── Email / Password ──────────────────────────────────────────────────────── */
export const loginWithEmail = createAsyncThunk(
  "auth/loginWithEmail",
  async (payload: LoginPayload, { rejectWithValue }) => {
    let firebaseUid: string;

    try {
      // 1. Firebase Auth first → get UID
      firebaseUid = await firebaseEmailSignIn(payload.email!, payload.password!);
    } catch (firebaseErr: unknown) {
      const code = (firebaseErr as { code?: string })?.code;
      const isInvalidCred =
        code === "auth/invalid-credential" ||
        code === "auth/wrong-password" ||
        code === "auth/user-not-found";

      if (!isInvalidCred) {
        return rejectWithValue(
          firebaseErr instanceof Error ? firebaseErr.message : "Login failed",
        );
      }

      // 2. Firebase doesn't know this user → try backend login
      try {
        const backendRes = await authService.login({
          ...payload,
          type: LoginType.Normal,
          ...deviceFields(),
        });

        if (backendRes.status !== 200 && backendRes.status !== 1) {
          return rejectWithValue(backendRes.message ?? "Invalid email or password.");
        }

        // 3. Backend accepted → create/sign-in Firebase account then complete login
        firebaseUid = await firebaseCreateAccount(payload.email!, payload.password!);
        const result = extractValidUser(backendRes, "Login failed");
        if (typeof result === "string") return rejectWithValue(result);
        persistAuth(result);
        return { ...result, firebase_id: result.firebase_id ?? firebaseUid };
      } catch (backendErr: unknown) {
        return rejectWithValue(
          backendErr instanceof Error ? backendErr.message : "Invalid email or password.",
        );
      }
    }

    // 4. Firebase succeeded → backend login with firebase_id
    try {
      const res = await authService.login({
        ...payload,
        type: LoginType.Normal,
        firebase_id: firebaseUid,
        ...deviceFields(),
      });
      const result = extractValidUser(res, "Login failed");
      if (typeof result === "string") return rejectWithValue(result);
      persistAuth(result);
      return { ...result, firebase_id: result.firebase_id ?? firebaseUid };
    } catch (err: unknown) {
      return rejectWithValue(err instanceof Error ? err.message : "Login failed");
    }
  },
);

/* ── Google Sign-In ────────────────────────────────────────────────────────── */
// Firebase popup happens in GoogleLoginButton before this is dispatched.
// The component passes firebase_id (uid) and email/full_name from Google.
export const loginWithGoogle = createAsyncThunk(
  "auth/loginWithGoogle",
  async (payload: LoginPayload, { rejectWithValue }) => {
    try {
      const res = await authService.login({
        ...payload,
        type: LoginType.Google,
        ...deviceFields(),
      });
      const result = extractValidUser(res, "Google login failed");
      if (typeof result === "string") return rejectWithValue(result);
      persistAuth(result);
      return { ...result, firebase_id: result.firebase_id ?? payload.firebase_id };
    } catch (err: unknown) {
      return rejectWithValue(err instanceof Error ? err.message : "Google login failed");
    }
  },
);

/* ── Phone OTP ─────────────────────────────────────────────────────────────── */
// Firebase phone sign-in + OTP confirm happens in OtpPage before this is dispatched.
// The component passes firebase_id (uid) and mobile_number/country_code.
export const loginWithOTP = createAsyncThunk(
  "auth/loginWithOTP",
  async (payload: LoginPayload, { rejectWithValue }) => {
    try {
      const res = await authService.login({
        ...payload,
        type: LoginType.OTP,
        ...deviceFields(),
      });
      const result = extractValidUser(res, "OTP login failed");
      if (typeof result === "string") return rejectWithValue(result);
      persistAuth(result);
      return { ...result, firebase_id: result.firebase_id ?? payload.firebase_id };
    } catch (err: unknown) {
      return rejectWithValue(err instanceof Error ? err.message : "OTP login failed");
    }
  },
);

/* ── Sign Up ───────────────────────────────────────────────────────────────── */
export const registerUser = createAsyncThunk(
  "auth/register",
  async (payload: RegisterPayload & { password: string }, { rejectWithValue }) => {
    try {
      // 1. Create Firebase account first → get UID
      await firebaseCreateAccount(payload.email, payload.password);

      // 2. Backend registration
      const res = await authService.register(payload);
      if (res.status !== 200 && res.status !== 1)
        return rejectWithValue(res.message ?? "Registration failed");
      return true;
    } catch (err: unknown) {
      const code = (err as { code?: string })?.code;
      if (code === "auth/email-already-in-use") {
        // Firebase account exists — backend may still accept registration
        const res = await authService.register(payload).catch(() => null);
        if (res && (res.status === 200 || res.status === 1)) return true;
      }
      return rejectWithValue(err instanceof Error ? err.message : "Registration failed");
    }
  },
);

/* ── Logout ────────────────────────────────────────────────────────────────── */
export const logoutUser = createAsyncThunk(
  "auth/logout",
  async (_, { dispatch }) => {
    try {
      await authService.logout(getStoredPlayerId() ?? "");
    } catch {
      // continue even if logout API fails
    } finally {
      await signOutFromFirebase();
      clearAuth();
      dispatch({ type: "RESET_APP" });
    }
  },
);

/* ── Forgot Password (Firebase sends reset email) ──────────────────────────── */
export const forgotPassword = createAsyncThunk(
  "auth/forgotPassword",
  async (email: string, { rejectWithValue }) => {
    try {
      await firebasePasswordReset(email);
      return "Password reset email sent. Check your inbox.";
    } catch (err: unknown) {
      const code = (err as { code?: string })?.code;
      if (code === "auth/user-not-found") return rejectWithValue("No account found with this email.");
      return rejectWithValue(err instanceof Error ? err.message : "Request failed");
    }
  },
);

/* ── Reset Password (backend token flow) ───────────────────────────────────── */
export const resetPassword = createAsyncThunk(
  "auth/resetPassword",
  async (
    payload: { token: string; password: string; password_confirmation: string },
    { rejectWithValue },
  ) => {
    try {
      const res = await authService.resetPassword(
        payload.token,
        payload.password,
        payload.password_confirmation,
      );
      if (res.status !== 200 && res.status !== 1)
        return rejectWithValue(res.message ?? "Reset failed");
      return res.message as string;
    } catch (err: unknown) {
      return rejectWithValue(err instanceof Error ? err.message : "Reset failed");
    }
  },
);

/* ── Slice ─────────────────────────────────────────────────────────────────── */
const authSlice = createSlice({
  name: "auth",
  initialState,
  reducers: {
    clearError: (state) => { state.error = null; },
    setError: (state, action: { payload: string }) => {
      state.error = action.payload;
      state.isLoading = false;
    },
    logout: (state) => {
      state.user = null;
      state.isAuthenticated = false;
      clearAuth();
    },
    validateAuth: (state) => {
      const id = Cookies.get("user_id");
      const isValidId = !!id && id !== "0" && id !== "" && id !== "null" && id !== "undefined";
      if (state.isAuthenticated && !isValidId) {
        state.isAuthenticated = false;
        state.user = null;
        clearAuth();
      }
    },
  },
  extraReducers: (builder) => {
    const loginFulfilled = (state: AuthState, action: { payload: AuthUser }) => {
      state.isLoading = false;
      state.user = action.payload;
      state.isAuthenticated = true;
      state.error = null;
    };
    const loginRejected = (state: AuthState, action: { payload: unknown }) => {
      state.isLoading = false;
      state.error = action.payload as string;
    };
    const loginPending = (state: AuthState) => {
      state.isLoading = true;
      state.error = null;
    };

    builder
      .addCase(loginWithEmail.pending, loginPending)
      .addCase(loginWithEmail.fulfilled, loginFulfilled)
      .addCase(loginWithEmail.rejected, loginRejected)
      .addCase(loginWithGoogle.pending, loginPending)
      .addCase(loginWithGoogle.fulfilled, loginFulfilled)
      .addCase(loginWithGoogle.rejected, loginRejected)
      .addCase(loginWithOTP.pending, loginPending)
      .addCase(loginWithOTP.fulfilled, loginFulfilled)
      .addCase(loginWithOTP.rejected, loginRejected)
      .addCase(registerUser.pending, (state) => { state.isLoading = true; state.error = null; })
      .addCase(registerUser.fulfilled, (state) => { state.isLoading = false; })
      .addCase(registerUser.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      })
      .addCase(forgotPassword.pending, (state) => { state.isLoading = true; state.error = null; })
      .addCase(forgotPassword.fulfilled, (state) => { state.isLoading = false; })
      .addCase(forgotPassword.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      })
      .addCase(resetPassword.pending, (state) => { state.isLoading = true; state.error = null; })
      .addCase(resetPassword.fulfilled, (state) => { state.isLoading = false; })
      .addCase(resetPassword.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      });
  },
});

export const { clearError, setError, logout, validateAuth } = authSlice.actions;
export default authSlice.reducer;
