import { env } from '../config/env';
import { logger } from '../utils/logger';

// --- Types ---

export interface VaultwardenUser {
  Id: string;
  Email: string;
  Name: string;
  _Status: number; // 0 = enabled, 1 = invited, 2 = disabled
  CreatedAt: string;
}

// --- Client ---

class VaultwardenClient {
  private sessionCookie: string | null = null;
  private sessionExpiresAt = 0;

  private get baseUrl(): string {
    return env.VAULTWARDEN_URL;
  }

  private get adminToken(): string {
    return env.VAULTWARDEN_ADMIN_TOKEN;
  }

  get hasCredentials(): boolean {
    return !!this.adminToken;
  }

  /**
   * Authenticate to the Vaultwarden admin panel and get a session cookie.
   * Session is cached and reused.
   */
  private async ensureSession(): Promise<void> {
    if (this.sessionCookie && Date.now() < this.sessionExpiresAt) return;

    const res = await fetch(`${this.baseUrl}/admin`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: `token=${encodeURIComponent(this.adminToken)}`,
      redirect: 'manual', // Don't follow redirect — we need the Set-Cookie header
    });

    // Vaultwarden returns 303 redirect on successful admin auth
    const setCookie = res.headers.get('set-cookie');
    if (!setCookie) {
      throw new Error(`Vaultwarden admin auth failed (status ${res.status})`);
    }

    // Extract the session cookie value
    const match = setCookie.match(/VW_ADMIN=([^;]+)/);
    if (!match) {
      throw new Error('Vaultwarden admin auth: no session cookie found');
    }

    this.sessionCookie = `VW_ADMIN=${match[1]}`;
    // Cache session for 20 minutes (Vaultwarden default is 20min)
    this.sessionExpiresAt = Date.now() + 19 * 60 * 1000;
    logger.debug('Vaultwarden admin session refreshed');
  }

  /**
   * Make an authenticated request to the Vaultwarden admin API
   */
  private async request<T>(
    method: string,
    path: string,
    body?: unknown,
  ): Promise<T> {
    await this.ensureSession();

    const url = `${this.baseUrl}/admin${path}`;
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 10000);

    const headers: Record<string, string> = {
      Cookie: this.sessionCookie!,
    };

    let fetchBody: string | undefined;
    if (body) {
      headers['Content-Type'] = 'application/json';
      fetchBody = JSON.stringify(body);
    }

    try {
      const res = await fetch(url, {
        method,
        headers,
        body: fetchBody,
        signal: controller.signal,
      });

      if (!res.ok) {
        const text = await res.text().catch(() => '');
        throw new Error(`Vaultwarden API ${method} ${path} returned ${res.status}: ${text}`);
      }

      const contentType = res.headers.get('content-type') || '';
      if (contentType.includes('application/json')) {
        return (await res.json()) as T;
      }
      return {} as T;
    } finally {
      clearTimeout(timeout);
    }
  }

  // --- Health ---

  async healthCheck(): Promise<boolean> {
    if (!this.hasCredentials) return false;
    try {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), 5000);
      try {
        const res = await fetch(`${this.baseUrl}/alive`, {
          signal: controller.signal,
        });
        return res.ok;
      } finally {
        clearTimeout(timeout);
      }
    } catch {
      return false;
    }
  }

  // --- User Management ---

  /** List all users */
  async listUsers(): Promise<VaultwardenUser[]> {
    return this.request<VaultwardenUser[]>('GET', '/users');
  }

  /** Find a user by email */
  async findUserByEmail(email: string): Promise<VaultwardenUser | null> {
    try {
      const users = await this.listUsers();
      return users.find(u => u.Email.toLowerCase() === email.toLowerCase()) || null;
    } catch (err) {
      logger.warn('Vaultwarden findUserByEmail failed:', err instanceof Error ? err.message : err);
      return null;
    }
  }

  /** Invite a user by email (user sets their own master password) */
  async inviteUser(email: string): Promise<void> {
    await this.request('POST', '/invite', { email });
  }

  /** Deactivate a user (disable login without deleting data) */
  async deactivateUser(userId: string): Promise<void> {
    await this.request('POST', `/users/${userId}/disable`);
  }

  /** Re-enable a previously deactivated user */
  async enableUser(userId: string): Promise<void> {
    await this.request('POST', `/users/${userId}/enable`);
  }

  /** Delete a user and all their data (destructive) */
  async deleteUser(userId: string): Promise<void> {
    await this.request('DELETE', `/users/${userId}`);
  }
}

export const vaultwardenClient = new VaultwardenClient();
