/**
 * Zoom Server-to-Server OAuth API Client
 * Requires: ZOOM_ACCOUNT_ID, ZOOM_CLIENT_ID, ZOOM_CLIENT_SECRET
 */

export interface ZoomUser {
  id: string;
  email: string;
  display_name: string;
  status: string;
}

export interface ZoomCallLog {
  id: string;
  call_id: string;
  caller_number: string;
  caller_name: string;
  callee_number: string;
  callee_name: string;
  direction: 'inbound' | 'outbound' | 'internal';
  result: string; // 'Call connected', 'Missed', 'Voicemail', etc.
  date_time: string; // ISO 8601 — Zoom Phone API uses date_time, not start_time
  duration: number; // seconds
}

export interface ZoomMeeting {
  id: string | number;
  uuid: string;
  host_id: string;
  host_email?: string;
  topic: string;
  start_time: string;
  end_time?: string;
  duration: number; // minutes
  participants_count?: number;
}

export interface ZoomMeetingParticipant {
  id?: string;
  user_id?: string;
  name: string;
  user_email: string;
  duration: number; // seconds
  join_time: string;
  leave_time: string;
}

export interface ZoomClientConfig {
  accountId: string;
  clientId: string;
  clientSecret: string;
}

export class ZoomClient {
  private config: ZoomClientConfig;
  private accessToken: string | null = null;
  private tokenExpiry: number = 0;
  private readonly baseUrl = 'https://api.zoom.us/v2';

  constructor(config: ZoomClientConfig) {
    this.config = config;
  }

  private async getToken(): Promise<string> {
    // Refresh 5 minutes before expiry
    if (this.accessToken && Date.now() < this.tokenExpiry - 300000) {
      return this.accessToken;
    }

    const credentials = Buffer.from(
      `${this.config.clientId}:${this.config.clientSecret}`
    ).toString('base64');

    const url = `https://zoom.us/oauth/token?grant_type=account_credentials&account_id=${encodeURIComponent(this.config.accountId)}`;

    const res = await fetch(url, {
      method: 'POST',
      headers: {
        Authorization: `Basic ${credentials}`,
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    });

    if (!res.ok) {
      const text = await res.text();
      throw new Error(`Zoom token request failed: ${res.status} ${text}`);
    }

    const data = await res.json();
    this.accessToken = data.access_token;
    this.tokenExpiry = Date.now() + data.expires_in * 1000;
    return this.accessToken!;
  }

  private async fetchJson<T>(path: string, retries = 0): Promise<T> {
    const token = await this.getToken();
    const res = await fetch(`${this.baseUrl}${path}`, {
      headers: {
        Authorization: `Bearer ${token}`,
        Accept: 'application/json',
      },
    });

    if (res.status === 429) {
      if (retries >= 5) {
        throw new Error(`Zoom rate limit hit after ${retries} retries: ${path}`);
      }
      const delay = Math.pow(2, retries) * 1000;
      console.warn(`[ZOOM] Rate limited on ${path}, retrying in ${delay}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
      return this.fetchJson<T>(path, retries + 1);
    }

    if (!res.ok) {
      const text = await res.text();
      throw new Error(`Zoom API error ${res.status} for ${path}: ${text}`);
    }

    return res.json();
  }

  /**
   * Get all active Zoom users (paginated via next_page_token)
   */
  async getUsers(): Promise<ZoomUser[]> {
    const users: ZoomUser[] = [];
    let nextPageToken = '';

    do {
      const params = new URLSearchParams({
        status: 'active',
        page_size: '300',
      });
      if (nextPageToken) params.set('next_page_token', nextPageToken);

      const data = await this.fetchJson<{
        users: ZoomUser[];
        next_page_token?: string;
      }>(`/users?${params}`);

      users.push(...(data.users ?? []));
      nextPageToken = data.next_page_token ?? '';
    } while (nextPageToken);

    return users;
  }

  /**
   * Get phone call logs for a user in a date range (paginated)
   * from/to: ISO date strings 'YYYY-MM-DD'
   */
  async getUserCallLogs(
    zoomUserId: string,
    from: string,
    to: string
  ): Promise<ZoomCallLog[]> {
    const calls: ZoomCallLog[] = [];
    let nextPageToken = '';

    do {
      const params = new URLSearchParams({
        from,
        to,
        page_size: '300',
        type: 'all',
      });
      if (nextPageToken) params.set('next_page_token', nextPageToken);

      let data: { call_logs?: ZoomCallLog[]; next_page_token?: string };
      try {
        data = await this.fetchJson<{
          call_logs?: ZoomCallLog[];
          next_page_token?: string;
        }>(`/phone/users/${encodeURIComponent(zoomUserId)}/call_logs?${params}`);
      } catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        // 400 = user has no Zoom Phone license — skip silently
        if (msg.includes('400') || msg.includes('404')) {
          break;
        }
        throw err;
      }

      calls.push(...(data.call_logs ?? []));
      nextPageToken = data.next_page_token ?? '';
    } while (nextPageToken);

    return calls;
  }

  /**
   * Get past meetings for a user in a date range (paginated)
   * Uses the Reports API — requires role or admin scope
   */
  async getUserPastMeetings(
    zoomUserId: string,
    from: string,
    to: string
  ): Promise<ZoomMeeting[]> {
    const meetings: ZoomMeeting[] = [];
    let nextPageToken = '';

    do {
      const params = new URLSearchParams({
        type: 'past',
        from,
        to,
        page_size: '300',
      });
      if (nextPageToken) params.set('next_page_token', nextPageToken);

      let data: { meetings?: ZoomMeeting[]; next_page_token?: string };
      try {
        data = await this.fetchJson<{
          meetings?: ZoomMeeting[];
          next_page_token?: string;
        }>(`/report/users/${encodeURIComponent(zoomUserId)}/meetings?${params}`);
      } catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        if (msg.includes('400') || msg.includes('404')) {
          break;
        }
        throw err;
      }

      meetings.push(...(data.meetings ?? []));
      nextPageToken = data.next_page_token ?? '';
    } while (nextPageToken);

    return meetings;
  }

  /**
   * Get participants for a past meeting (Zoom limitation: last 30 days only)
   */
  async getMeetingParticipants(meetingId: string): Promise<ZoomMeetingParticipant[]> {
    const participants: ZoomMeetingParticipant[] = [];
    let nextPageToken = '';

    do {
      const params = new URLSearchParams({ page_size: '300' });
      if (nextPageToken) params.set('next_page_token', nextPageToken);

      let data: { participants?: ZoomMeetingParticipant[]; next_page_token?: string };
      try {
        data = await this.fetchJson<{
          participants?: ZoomMeetingParticipant[];
          next_page_token?: string;
        }>(`/past_meetings/${encodeURIComponent(meetingId)}/participants?${params}`);
      } catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        if (msg.includes('400') || msg.includes('404') || msg.includes('3001')) {
          break;
        }
        throw err;
      }

      participants.push(...(data.participants ?? []));
      nextPageToken = data.next_page_token ?? '';
    } while (nextPageToken);

    return participants;
  }
}
