"use client";

import { LoginStatus, type LoginStatus as LoginStatusT } from "@/data/schemas/auth";
import { Result, type Result as ResultT } from "@/utils/result";
import { SpAsyncUtil } from "@/utils/storage";
import { StorageKeys } from "../storage_keys";
import type { IAuthStorage } from "./iauth_storage";

export class AuthStorage implements IAuthStorage {
  private static _instance: AuthStorage | null = null;

  static getInstance(): AuthStorage {
    if (!AuthStorage._instance) AuthStorage._instance = new AuthStorage();
    return AuthStorage._instance;
  }

  /** @internal */
  static _resetForTests(): void {
    AuthStorage._instance = null;
  }

  // ---- login token ----

  getLoginToken(): Promise<ResultT<string | null>> {
    return SpAsyncUtil.getString(StorageKeys.loginToken);
  }
  setLoginToken(token: string): Promise<ResultT<void>> {
    return SpAsyncUtil.setString(StorageKeys.loginToken, token);
  }
  async hasLoginToken(): Promise<ResultT<boolean>> {
    const r = await this.getLoginToken();
    if (!r.success) return Result.err(r.error);
    return Result.ok(r.data !== null && r.data.length > 0);
  }
  clearLoginToken(): Promise<ResultT<void>> {
    return SpAsyncUtil.remove(StorageKeys.loginToken);
  }

  // ---- login provider ----

  async getLoginProvider(): Promise<ResultT<LoginStatusT | null>> {
    const r = await SpAsyncUtil.getString(StorageKeys.loginProvider);
    if (!r.success) return Result.err(r.error);
    if (r.data === null || r.data.length === 0) return Result.ok(null);
    if (isLoginStatus(r.data)) return Result.ok(r.data);
    return Result.ok(null);
  }
  setLoginProvider(provider: LoginStatusT): Promise<ResultT<void>> {
    return SpAsyncUtil.setString(StorageKeys.loginProvider, provider);
  }
  clearLoginProvider(): Promise<ResultT<void>> {
    return SpAsyncUtil.remove(StorageKeys.loginProvider);
  }

  // ---- guest token ----

  getGuestToken(): Promise<ResultT<string | null>> {
    return SpAsyncUtil.getString(StorageKeys.guestToken);
  }
  setGuestToken(token: string): Promise<ResultT<void>> {
    return SpAsyncUtil.setString(StorageKeys.guestToken, token);
  }
  async hasGuestToken(): Promise<ResultT<boolean>> {
    const r = await this.getGuestToken();
    if (!r.success) return Result.err(r.error);
    return Result.ok(r.data !== null && r.data.length > 0);
  }
  clearGuestToken(): Promise<ResultT<void>> {
    return SpAsyncUtil.remove(StorageKeys.guestToken);
  }

  // ---- device id ----

  getDeviceId(): Promise<ResultT<string | null>> {
    return SpAsyncUtil.getString(StorageKeys.deviceId);
  }
  setDeviceId(deviceId: string): Promise<ResultT<void>> {
    return SpAsyncUtil.setString(StorageKeys.deviceId, deviceId);
  }

  // ---- refresh token ----
  
  getRefreshToken(): Promise<ResultT<string | null>> {
    return SpAsyncUtil.getString(StorageKeys.refreshToken);
  }
  setRefreshToken(token: string): Promise<ResultT<void>> {
    return SpAsyncUtil.setString(StorageKeys.refreshToken, token);
  }
  clearRefreshToken(): Promise<ResultT<void>> {
    return SpAsyncUtil.remove(StorageKeys.refreshToken);
  }

  // ---- ASID / PSID ----

  async getAsid(): Promise<ResultT<string | null>> {
    return SpAsyncUtil.getString(StorageKeys.asid);
  }
  setAsid(id: string): Promise<ResultT<void>> {
    return SpAsyncUtil.setString(StorageKeys.asid, id);
  }
  clearAsid(): Promise<ResultT<void>> {
    return SpAsyncUtil.remove(StorageKeys.asid);
  }

  async getPsid(): Promise<ResultT<string | null>> {
    return SpAsyncUtil.getString(StorageKeys.psid);
  }
  setPsid(id: string): Promise<ResultT<void>> {
    return SpAsyncUtil.setString(StorageKeys.psid, id);
  }
  clearPsid(): Promise<ResultT<void>> {
    return SpAsyncUtil.remove(StorageKeys.psid);
  }

  // ---- bulk clear ----

  async clearBusinessAuthData(): Promise<ResultT<void>> {
    const r1 = await this.clearLoginToken();
    if (!r1.success) return r1;
    const r2 = await this.clearRefreshToken();
    if (!r2.success) return r2;
    const r3 = await this.clearAsid();
    if (!r3.success) return r3;
    const r4 = await this.clearPsid();
    if (!r4.success) return r4;
    return this.clearLoginProvider();
  }

  async clearAuthData(): Promise<ResultT<void>> {
    const r1 = await this.clearLoginToken();
    if (!r1.success) return r1;
    const r2 = await this.clearLoginProvider();
    if (!r2.success) return r2;
    const r3 = await this.clearGuestToken();
    if (!r3.success) return r3;
    const r4 = await this.clearRefreshToken();
    if (!r4.success) return r4;
    const r5 = await this.clearAsid();
    if (!r5.success) return r5;
    return this.clearPsid();
  }
}

function isLoginStatus(value: string): value is LoginStatusT {
  return Object.values(LoginStatus).includes(value as LoginStatusT);
}
