/**
 * Auth 状态机：Actors（异步服务）
 */
import { fromPromise } from "xstate";

import { AuthPlatform, type AuthProvider } from "@/lib/auth/auth_platform";
import type { LoginStatus } from "@/data/dto/auth";
import { getAuthRepository } from "@/data/repositories/auth_repository";
import { fetchFacebookUserData } from "@/data/services";
import { AuthStorage } from "@/data/storage/auth/auth_storage";
import { UserStorage } from "@/data/storage/user/user_storage";
import { deviceIdentifier, Logger, Result } from "@/utils";

import { readGuestId } from "./auth-helpers";

const log = new Logger("AuthActors");

export const emailLoginActor = fromPromise<LoginStatus, { email: string; password: string }>(
  async ({ input }) => {
    const authRepo = getAuthRepository();
    log.debug("[auth-machine] emailLoginActor ENTRY", {
      emailLength: input.email.length,
      emailPreview: input.email.slice(0, 8),
      passwordLength: input.password.length,
    });
    const guestId = await readGuestId();
    log.debug("[auth-machine] emailLoginActor calling authRepo.emailLogin", {
      hasGuestId: !!guestId,
    });
    const result = await authRepo.emailLogin({ ...input, guestId });
    log.debug("[auth-machine] emailLoginActor authRepo.emailLogin DONE", {
      success: result.success,
      hasData: result.success ? !!result.data : null,
      errorName: result.success ? null : result.error?.name,
      errorMessage: result.success ? null : result.error?.message,
    });
    if (Result.isErr(result)) throw result.error;
    return "email" as LoginStatus;
  },
);

export const emailRegisterThenLoginActor = fromPromise<
  LoginStatus,
  { email: string; password: string; username: string; confirmPassword: string }
>(async ({ input }) => {
  const authRepo = getAuthRepository();
  log.debug("[auth-machine] emailRegisterThenLoginActor ENTRY", {
    emailLength: input.email.length,
    usernameLength: input.username.length,
    passwordLength: input.password.length,
    confirmPasswordLength: input.confirmPassword.length,
  });
  const guestId = await readGuestId();

  log.debug("[auth-machine] emailRegisterThenLoginActor calling authRepo.register");
  const registerResult = await authRepo.register({ ...input, guestId });
  log.debug("[auth-machine] emailRegisterThenLoginActor authRepo.register DONE", {
    success: registerResult.success,
    errorName: registerResult.success ? null : registerResult.error?.name,
    errorMessage: registerResult.success ? null : registerResult.error?.message,
  });
  if (Result.isErr(registerResult)) throw registerResult.error;

  // 注册后自动登录（对齐 Dart 行为）
  log.debug(
    "[auth-machine] emailRegisterThenLoginActor calling authRepo.emailLogin (post-register)",
  );
  const loginResult = await authRepo.emailLogin({
    email: input.email,
    password: input.password,
    guestId,
  });
  log.debug(
    "[auth-machine] emailRegisterThenLoginActor authRepo.emailLogin DONE",
    {
      success: loginResult.success,
      errorName: loginResult.success ? null : loginResult.error?.name,
      errorMessage: loginResult.success ? null : loginResult.error?.message,
    },
  );
  if (Result.isErr(loginResult)) throw loginResult.error;
  return "email" as LoginStatus;
});

// OAuth 登录 actor：调 AuthPlatform 触发 OAuth 跳转。
// 成功路径下 NextAuth 会重定向离开本应用，actor 通常不会 resolve；
// onDone 仍保留以处理 SDK 同步返回（极少见）。
export const oauthSignInActor = fromPromise<void, AuthProvider>(async ({ input }) => {
  if (input === "google") {
    await AuthPlatform.googleSignIn();
  } else {
    await AuthPlatform.facebookSignIn();
  }
});

export const logoutActor = fromPromise<void>(async () => {
  const authRepo = getAuthRepository();
  const result = await authRepo.logout();
  if (Result.isErr(result)) throw result.error;
});

// ========== OAuth token → 后端业务 token sync actors ==========
// 由 <OAuthSessionSync /> 在 NextAuth 回调后派发（监听 useSession()）。
// 用 OAuth provider 的 idToken / accessToken 调后端换业务 token + user。
// 后端返回的 LoginResponse 已经被 AuthRepository._saveLoginData 持久化。

export const syncGoogleBackendActor = fromPromise<LoginStatus, { idToken: string }>(
  async ({ input }) => {
    const authRepo = getAuthRepository();
    const guestId = await readGuestId();
    const result = await authRepo.googleLogin({
      idToken: input.idToken,
      guestId,
    });
    if (Result.isErr(result)) throw result.error;
    return "google" as LoginStatus;
  },
);

export const syncFacebookBackendActor = fromPromise<
  LoginStatus,
  { accessToken: string }
>(async ({ input }) => {
  const authRepo = getAuthRepository();
  const guestId = await readGuestId();
  const result = await authRepo.facebookLogin({
    accessToken: input.accessToken,
    guestId,
  });
  if (Result.isErr(result)) throw result.error;

  // 业务 token 已到手 —— 顺带用同一个 accessToken 调 Facebook Graph `/me`
  // 拉用户资料（id / name / email / picture），把 pictureUrl + Facebook ID
  // 写本地。
  //
  // 为什么要这一步？后端 LoginResponse.user.avatarUrl 不一定总有（依赖
  // 后端在 OAuth 流程里有没有抓过）；前端用 accessToken 直接调 Graph
  // 拿到的 picture 是最新的，独立于后端缓存。
  //
  // 失败策略：best-effort。Graph 调用挂掉 / 用户没头像 / token 过期都不
  // 影响 Facebook 登录主流程 —— 业务 token 已经在 result 里。
  await persistFacebookProfile(input.accessToken);

  return "facebook" as LoginStatus;
});

/**
 * 用 accessToken 调 Facebook Graph `/me` 拉用户资料，写本地：
 *   - `id`            → `AuthStorage.setFacebookId()`（独立于后端 userId）
 *   - `pictureUrl`    → `UserStorage.setAvatarUrl()`（头像专用 slot，便于快速读取）
 *
 * 全部 best-effort：单条失败只 warn，不抛错。`pictureUrl == null` 也算成功
 * （用户没设头像），只是不写入。
 */
async function persistFacebookProfile(accessToken: string): Promise<void> {
  const fbR = await fetchFacebookUserData(accessToken);
  if (Result.isErr(fbR)) {
    log.warn(
      { err: fbR.error.message },
      "[auth-machine] syncFacebookBackendActor: fetchFacebookUserData failed (continuing anyway)",
    );
    return;
  }
  const fbUser = fbR.data;
  log.info(
    {
      hasId: !!fbUser.id,
      hasPicture: !!fbUser.pictureUrl,
    },
    "[auth-machine] syncFacebookBackendActor: Facebook profile fetched",
  );

  const authStorage = AuthStorage.getInstance();
  if (fbUser.id) {
    const r = await authStorage.setFacebookId(fbUser.id);
    if (!r.success) {
      log.warn(
        { err: r.error.message },
        "[auth-machine] syncFacebookBackendActor: setFacebookId failed",
      );
    }
  }

  if (fbUser.pictureUrl) {
    const r = await UserStorage.getInstance().setAvatarUrl(fbUser.pictureUrl);
    if (!r.success) {
      log.warn(
        { err: r.error.message },
        "[auth-machine] syncFacebookBackendActor: setAvatarUrl failed",
      );
    }
  }
}

// ========== 显式游客登录 actor（opt-in） ==========
// 由 splash UI 派发 `AuthGuestLoginSubmitted` 触发，不自动跑。

export const guestLoginActor = fromPromise<LoginStatus, void>(async () => {
  log.debug("[auth-machine] guestLoginActor ENTRY");

  // 1. 先查本地 guestToken —— 已有就直接进 idle（带 pendingRedirect）
  const hasTokenR = await AuthStorage.getInstance().hasGuestToken();
  log.debug("[auth-machine] guestLoginActor hasGuestToken", {
    success: hasTokenR.success,
    hasData: hasTokenR.success ? !!hasTokenR.data : null,
  });
  if (hasTokenR.success && hasTokenR.data) {
    log.debug("[auth-machine] guestLoginActor SHORT-CIRCUIT (has local guestToken) → return guest");
    return "guest" as LoginStatus;
  }
  // Result.err(_) 时不抛 —— 落到 API 路径让真正的错误信号出现

  // 2. 没有（或读不出）才走完整流程：拿 deviceId → 调后端
  const deviceId = await deviceIdentifier.getDeviceId();
  log.debug("[auth-machine] guestLoginActor deviceId", {
    length: deviceId.length,
    prefix: deviceId.slice(0, 8),
  });

  log.debug("[auth-machine] guestLoginActor calling authRepo.guestLogin");
  const result = await getAuthRepository().guestLogin(deviceId);
  log.debug("[auth-machine] guestLoginActor authRepo.guestLogin DONE", {
    success: result.success,
    hasData: result.success ? !!result.data : null,
    errorName: result.success ? null : result.error?.name,
    errorMessage: result.success ? null : result.error?.message,
  });
  if (Result.isErr(result)) throw result.error;
  return "guest" as LoginStatus;
});

// ========== 启动 / 恢复时检查登录态 actor（只查不创） ==========
// 由 <AuthStatusChecker /> 在 mount 时派发（一次性）。
// 流程（纯只读）：
//   1. deviceIdentifier.getDeviceId()     —— 无则生成 + 落盘
//      （生产环境使用 fingerprintjs；非生产环境使用本地 UUID）
//      （给后续 OAuth/email 登录准备 deviceId，不在 splash 首次访问就 auto-create 游客）
//   2. AuthStorage.getLoginToken()         —— 有 → 读取 loginProvider，缺省回退 "email"
//   3. AuthStorage.getFacebookId()         —— 有 → facebookIdLogin → "facebook"
//   4. AuthStorage.getGuestToken()         —— 有 → "guest"
//   5. 都没有 → "notLoggedIn"（让用户显式选 OAuth / Email / Guest 入口）

export const checkAuthStatusActor = fromPromise<LoginStatus, void>(async () => {
  // 1. 拿 deviceId（不用于 auto-guest-login，仅供后续登录用）
  await deviceIdentifier.getDeviceId();
  const storage = AuthStorage.getInstance();

  // 2. 查 loginToken
  const loginTokenR = await storage.getLoginToken();
  if (loginTokenR.success && loginTokenR.data) {
    const providerR = await storage.getLoginProvider();
    if (providerR.success && isBusinessLoginProvider(providerR.data)) {
      return providerR.data;
    }
    return "email" as LoginStatus;
  }

  // 3. 外部浏览器深链同步：仅 fbid 落地时，用它换业务 token。
  const facebookIdR = await storage.getFacebookId();
  if (facebookIdR.success && facebookIdR.data) {
    const authRepo = getAuthRepository();
    const avatarUrlR = await UserStorage.getInstance().getAvatarUrl();
    const result = await authRepo.facebookIdLogin({
      fbId: facebookIdR.data,
      avatarUrl: avatarUrlR.success ? avatarUrlR.data ?? undefined : undefined,
    });

    if (Result.isErr(result)) {
      log.warn(
        { err: result.error.message },
        "[auth-machine] checkAuthStatusActor: facebookIdLogin failed",
      );
      return "notLoggedIn" as LoginStatus;
    }

    return "facebook" as LoginStatus;
  }

  // 4. 查 guestToken
  const guestTokenR = await storage.getGuestToken();
  if (guestTokenR.success && guestTokenR.data) {
    return "guest" as LoginStatus;
  }

  // 5. 都没有 → "notLoggedIn"（保持 splash 页面 + 登录入口）
  return "notLoggedIn" as LoginStatus;
});

function isBusinessLoginProvider(
  provider: LoginStatus | null,
): provider is LoginStatus {
  return (
    provider === "email" ||
    provider === "facebook" ||
    provider === "google" ||
    provider === "apple"
  );
}
