import { Endpoint } from './endpoint.js';
import type { AuthTokenProvider } from './types.js';

export interface AuthClientConfig {
  baseURL: string;
  fetchImpl: typeof fetch;
  /** Path of the dev-defined login endpoint (default `/api/auth/login`). */
  loginPath?: string;
  /** Path of the dev-defined logout endpoint (default `/api/auth/logout`). */
  logoutPath?: string;
  /** Called whenever the stored token changes (e.g. to persist it). */
  onToken?: (token: string | null) => void;
}

interface LoginResponse {
  token?: string;
}

/**
 * Auth-flow helpers. These call **dev-defined** endpoints under the hood
 * (the script layer owns the actual auth); the lib only standardizes the
 * dance + in-memory token storage. There is no built-in identity model —
 * `login` POSTs credentials and stores whatever `token` comes back.
 */
export class AuthClient {
  private current: string | null = null;

  constructor(private readonly cfg: AuthClientConfig) {}

  /** The current bearer token, or null. */
  get token(): string | null {
    return this.current;
  }

  /** Suitable as `PicloudClientOptions.getAuthToken`. */
  readonly provider: AuthTokenProvider = () => this.current;

  /** POST credentials to the login endpoint; store the returned token. */
  async login(email: string, password: string): Promise<string | null> {
    const ep = new Endpoint<{ email: string; password: string }, LoginResponse>({
      baseURL: this.cfg.baseURL,
      path: this.cfg.loginPath ?? '/api/auth/login',
      fetchImpl: this.cfg.fetchImpl
    });
    const res = await ep.post({ email, password });
    this.setToken(typeof res?.token === 'string' ? res.token : null);
    return this.current;
  }

  /** POST to the logout endpoint (best-effort) and clear the token. */
  async logout(): Promise<void> {
    const ep = new Endpoint<undefined, unknown>({
      baseURL: this.cfg.baseURL,
      path: this.cfg.logoutPath ?? '/api/auth/logout',
      // Send the current token so the script can invalidate the session.
      getAuthToken: () => this.current,
      fetchImpl: this.cfg.fetchImpl
    });
    try {
      await ep.post();
    } finally {
      this.setToken(null);
    }
  }

  /** Manually set (or clear) the token — e.g. restoring from storage. */
  setToken(token: string | null): void {
    this.current = token;
    this.cfg.onToken?.(token);
  }
}
