import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios';

interface AuthTokens {
  accessToken: string;
  refreshToken: string;
}

interface AuthResponse {
  accessToken: string;
  refreshToken: string;
  user: { id: number; email: string; role: string; name?: string };
}

export class ApiClient {
  private client: AxiosInstance;
  private tokens: AuthTokens | null = null;
  private refreshPromise: Promise<AuthTokens> | null = null;
  private baseUrl: string;
  private email: string;
  private password: string;

  constructor(config: { baseUrl: string; email: string; password: string }) {
    this.baseUrl = config.baseUrl;
    this.email = config.email;
    this.password = config.password;

    this.client = axios.create({
      baseURL: config.baseUrl,
      timeout: 30_000,
      headers: { 'X-MCP-Session': 'true' },
    });

    // Attach access token to every request
    this.client.interceptors.request.use((req: InternalAxiosRequestConfig) => {
      if (this.tokens?.accessToken) {
        req.headers.Authorization = `Bearer ${this.tokens.accessToken}`;
      }
      return req;
    });

    // Auto-refresh on 401 (mirrors admin/src/lib/api.ts:39-83)
    this.client.interceptors.response.use(
      (res) => res,
      async (error: AxiosError) => {
        const original = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
        const errorCode = (error.response?.data as any)?.error?.code;

        if (
          error.response?.status === 401 &&
          (errorCode === 'INVALID_TOKEN' || errorCode === 'AUTH_REQUIRED') &&
          !original._retry
        ) {
          original._retry = true;

          if (!this.tokens?.refreshToken) {
            throw new Error('No refresh token available — call login() first');
          }

          try {
            if (!this.refreshPromise) {
              this.refreshPromise = this.doRefresh(this.tokens.refreshToken);
            }
            this.tokens = await this.refreshPromise;
            this.refreshPromise = null;

            original.headers.Authorization = `Bearer ${this.tokens.accessToken}`;
            return this.client(original);
          } catch {
            this.refreshPromise = null;
            // Refresh failed — try full re-login
            await this.login();
            original.headers.Authorization = `Bearer ${this.tokens!.accessToken}`;
            return this.client(original);
          }
        }

        throw error;
      }
    );
  }

  /** Authenticate with the API and store tokens */
  async login(): Promise<void> {
    const res = await axios.post<AuthResponse>(
      `${this.baseUrl}/api/auth/login`,
      { email: this.email, password: this.password },
      { headers: { 'X-MCP-Session': 'true' } }
    );
    this.tokens = {
      accessToken: res.data.accessToken,
      refreshToken: res.data.refreshToken,
    };
  }

  private async doRefresh(refreshToken: string): Promise<AuthTokens> {
    const res = await axios.post<AuthResponse>(
      `${this.baseUrl}/api/auth/refresh`,
      { refreshToken },
      { headers: { 'X-MCP-Session': 'true' } }
    );
    return {
      accessToken: res.data.accessToken,
      refreshToken: res.data.refreshToken,
    };
  }

  /** Make an authenticated API request */
  async request<T = any>(
    method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
    path: string,
    data?: Record<string, unknown>
  ): Promise<T> {
    const isBodyMethod = method === 'POST' || method === 'PUT' || method === 'PATCH';
    const res = await this.client.request<T>({
      method,
      url: path,
      ...(isBodyMethod ? { data } : { params: data }),
    });
    return res.data;
  }

  /** Check if we have valid tokens (not necessarily unexpired) */
  get isAuthenticated(): boolean {
    return this.tokens !== null;
  }
}
