"use client";

import { AuthApi, authApi } from "@/data/services/api";
import {
  AppleLoginRequest,
  FacebookLoginRequest,
  FbIdLoginRequest,
  GoogleLoginRequest,
  GuestLoginRequest,
  GuestLoginResponse,
  LoginRequest,
  LoginResponse,
  LoginStatus,
  type LoginStatus as LoginStatusT,
  RefreshTokenRequest,
  RefreshTokenResponse,
  RegisterRequest,
  SendCodeRequest,
} from "@/data/dto/auth";
import { User } from "@/data/dto/user";
import { AppEnvUtil, PlatformDetector, Result, Logger } from "@/utils";
import type { IAuthRepository } from "@/data/repositories/interfaces";
import { AuthStorage, type IAuthStorage } from "@/data/storage/auth";
import { UserStorage, type IUserStorage } from "@/data/storage/user";
import { createLazySingleton } from "./lazy_singleton";

const log = new Logger("DataRepositoriesAuthRepository");

const TEST_ACCOUNT_FLAG = "isTestAccount";

export class AuthRepository implements IAuthRepository {
  constructor(
    private readonly api: AuthApi,
    private readonly storage: IAuthStorage,
    private readonly userStorage: IUserStorage,
  ) {}

  // ============ 公共方法 ============

  /**
   * 用户注册。
   * 注册成功不会自动登录（与 Dart 行为一致），调用方需自行调 `emailLogin`。
   */
  async register(input: {
    username: string;
    email: string;
    password: string;
    guestId?: string;
  }): Promise<Result<void>> {
    return Result.wrap(async () => {
      await this.api.register(
        RegisterRequest.from(
          withTestAccountFlag({
            username: input.username,
            email: input.email,
            password: input.password,
            platform: getAuthPlatform(),
            guestId: input.guestId ?? "",
          }),
        ),
      );
    });
  }

  /** 邮箱/用户名 + 密码登录。 */
  async emailLogin(input: {
    email?: string;
    username?: string;
    password: string;
    guestId?: string;
  }): Promise<Result<LoginResponse>> {
    return Result.wrap(async () => {
      const email = input.email?.trim() ?? "";
      const response = await this.api.emailLogin(
        LoginRequest.from(
          withTestAccountFlag({
            email,
            username: resolveLoginUsername(input.username, email),
            password: input.password,
            platform: getAuthPlatform(),
            guestId: input.guestId ?? "",
          }),
        ),
      );
      await this._saveLoginData(response, LoginStatus.Email);
      return response;
    });
  }

  /** 发送邮箱验证码。 */
  async sendCode(email: string): Promise<Result<void>> {
    return Result.wrap(async () => {
      await this.api.sendCode(SendCodeRequest.from({ email }));
    });
  }

  /**
   * 退出登录：先调 API 注销服务端会话，无论成败都清本地登录态。
   * 强制清本地的逻辑：用户即使在 API 失败时也想从设备上登出。
   */
  async logout(): Promise<Result<void>> {
    try {
      await this.api.logout();
    } catch (e) {
      // API 失败也要继续清本地态
      log.warn("[AuthRepository] logout API failed, clearing local anyway", e);
    }
    try {
      await this.storage.clearAuthData();
      await this.userStorage.clearUserData();
      return Result.ok(undefined);
    } catch (e) {
      // 清本地是 best-effort；理论上 LS.remove 不应失败
      return Result.err(e);
    }
  }

  /**
   * 游客登录：使用 deviceId 换取 guest token。
   * 成功后写 guest token + deviceId + userId。
   */
  async guestLogin(deviceId: string): Promise<Result<GuestLoginResponse>> {
    log.debug("[AuthRepository.guestLogin] API call START", {
      deviceIdLength: deviceId.length,
      deviceIdPrefix: deviceId.slice(0, 8),
    });
    return Result.wrap(async () => {
      const response = await this.api.guestLogin(
        GuestLoginRequest.from(withTestAccountFlag({ deviceId })),
      );
      log.debug("[AuthRepository.guestLogin] API call SUCCESS (Zod parsed)", {
        hasToken: !!response.token,
        hasUser: !!response.user,
        userLastMessageAt: response.user?.lastMessageAt,
      });
      await this.storage.setGuestToken(response.token);
      await this.storage.setLoginProvider(LoginStatus.Guest);
      await this.storage.setDeviceId(deviceId);
      if (response.userId) {
        await this.userStorage.setUserId(response.userId);
      }
      return response;
    }).catch((e) => {
      log.error("[AuthRepository.guestLogin] API call FAILED", {
        errorName: e?.name,
        errorMessage: e?.message,
      });
      return Result.err(e);
    });
  }

  /** Google 登录。 */
  async googleLogin(input: {
    idToken: string;
    guestId?: string;
  }): Promise<Result<LoginResponse>> {
    return this._socialLogin(LoginStatus.Google, () =>
      this.api.googleLogin(
        GoogleLoginRequest.from(
          withTestAccountFlag({
            idToken: input.idToken,
            platform: getAuthPlatform(),
            guestId: input.guestId ?? "",
          }),
        ),
      ),
    );
  }

  /** Facebook 登录（accessToken 流程）。 */
  async facebookLogin(input: {
    accessToken: string;
    guestId?: string;
  }): Promise<Result<LoginResponse>> {
    return this._socialLogin(LoginStatus.Facebook, () =>
      this.api.facebookLogin(
        FacebookLoginRequest.from(
          withTestAccountFlag({
            accessToken: input.accessToken,
            platform: getAuthPlatform(),
            guestId: input.guestId ?? "",
          }),
        ),
      ),
    );
  }

  /** Facebook ID 登录（v7.0 新增，fbId 流程）。 */
  async facebookIdLogin(input: {
    fbId: string;
    avatarUrl?: string;
  }): Promise<Result<LoginResponse>> {
    return this._socialLogin(LoginStatus.Facebook, () =>
      this.api.facebookIdLogin(
        FbIdLoginRequest.from(
          withTestAccountFlag({
            fbId: input.fbId,
            avatarUrl: input.avatarUrl ?? "",
          }),
        ),
      ),
    );
  }

  /**
   * Apple 登录。
   * 注：Dart 端另有一个 `uploadFacebookAvatar` 方法，但当前 TS 端 `AuthApi`
   * 尚未实现该端点，故仓库暂不暴露。等 `AuthApi` 补齐后再加。
   */
  async appleLogin(identityToken: string): Promise<Result<LoginResponse>> {
    return this._socialLogin(LoginStatus.Apple, () =>
      this.api.appleLogin(
        AppleLoginRequest.from(
          withTestAccountFlag({
            identityToken,
            platform: getAuthPlatform(),
          }),
        ),
      ),
    );
  }

  /**
   * 刷新 token：先读本地的 refresh token，空则直接返回错误；
   * 调用 API 成功后写回新的 login token + refresh token。
   */
  async refreshToken(): Promise<Result<RefreshTokenResponse>> {
    const existing = await this.storage.getRefreshToken();
    if (!existing.success || !existing.data) {
      return Result.err(new Error("No refresh token available"));
    }
    const refreshToken = existing.data;
    return Result.wrap(async () => {
      const response = await this.api.refreshToken(
        RefreshTokenRequest.from({ refreshToken }),
      );
      await this.storage.setLoginToken(response.token);
      if (response.refreshToken) {
        await this.storage.setRefreshToken(response.refreshToken);
      }
      return response;
    });
  }

  /**
   * 获取当前登录用户。成功后尽力写本地 User 缓存（offline hydrate），
   * 缓存失败用 `Logger.warn` 记录，不影响主流程。
   */
  async getCurrentUser(): Promise<Result<User>> {
    return Result.wrap(async () => {
      const user = await this.api.getCurrentUser();
      const writeResult = await this.userStorage.setUser(user.toJson());
      if (!writeResult.success) {
        log.warn(
          "[AuthRepository] failed to cache current user",
          writeResult.error,
        );
      }
      return user;
    });
  }

  // ============ 私有助手 ============

  /**
   * 社交登录的公共包装：调 API → 调 `_saveLoginData` 持久化。
   * `_saveLoginData` 内部全部 best-effort，存储写失败不抛错。
   */
  private async _socialLogin(
    provider: LoginStatusT,
    call: () => Promise<LoginResponse>,
  ): Promise<Result<LoginResponse>> {
    return Result.wrap(async () => {
      const response = await call();
      await this._saveLoginData(response, provider);
      return response;
    });
  }

  /**
   * 持久化登录态：login token → refresh token → User 对象 → userId。
   * 全部 best-effort，错误用 `Logger.warn` 记录但不让登录「失败」——
   * 调用方已经从 API 拿到了 LoginResponse，缓存只是离线加速。
   */
  private async _saveLoginData(
    data: LoginResponse,
    provider: LoginStatusT,
  ): Promise<void> {
    const r1 = await this.storage.setLoginToken(data.token);
    if (!r1.success) {
      log.warn("[AuthRepository] setLoginToken failed", r1.error);
    }
    const providerResult = await this.storage.setLoginProvider(provider);
    if (!providerResult.success) {
      log.warn("[AuthRepository] setLoginProvider failed", providerResult.error);
    }
    if (data.refreshToken) {
      const r2 = await this.storage.setRefreshToken(data.refreshToken);
      if (!r2.success) {
        log.warn("[AuthRepository] setRefreshToken failed", r2.error);
      }
    }
    const r3 = await this.userStorage.setUser(data.user.toJson());
    if (!r3.success) {
      log.warn("[AuthRepository] setUser failed", r3.error);
    }
    const r4 = await this.userStorage.setUserId(data.user.id);
    if (!r4.success) {
      log.warn("[AuthRepository] setUserId failed", r4.error);
    }
  }
}

/** 全局懒单例。 */
export const getAuthRepository = createLazySingleton<IAuthRepository>(
  () =>
    new AuthRepository(
      authApi,
      AuthStorage.getInstance(),
      UserStorage.getInstance(),
    ),
);

function withTestAccountFlag<T extends Record<string, unknown>>(
  payload: T,
): T & { isTestAccount: boolean } {
  return {
    ...payload,
    [TEST_ACCOUNT_FLAG]: !AppEnvUtil.isProduction(),
  };
}

function getAuthPlatform(): string {
  return PlatformDetector.getPlatform();
}

function resolveLoginUsername(
  username: string | undefined,
  email: string,
): string {
  const trimmedUsername = username?.trim() ?? "";
  return trimmedUsername.length > 0 ? trimmedUsername : email;
}
