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

// --- Types matching Represent API response shapes ---

export interface RepresentOffice {
  type?: string;
  tel?: string;
  fax?: string;
  postal?: string;
}

export interface RepresentRepresentative {
  name: string;
  email: string | null;
  elected_office: string;
  district_name: string;
  party_name: string | null;
  representative_set_name: string;
  url: string;
  photo_url: string | null;
  offices: RepresentOffice[];
}

export interface RepresentPostalCodeResponse {
  city: string | null;
  province: string | null;
  centroid: { type: string; coordinates: [number, number] } | null;
  representatives_centroid: RepresentRepresentative[];
  representatives_concordance: RepresentRepresentative[];
}

// --- In-memory sliding window rate limiter (55/min, under 60/min API limit) ---

const RATE_LIMIT = 55;
const RATE_WINDOW_MS = 60_000;
const requestTimestamps: number[] = [];

function checkRateLimit(): boolean {
  const now = Date.now();
  // Remove timestamps outside the window
  while (requestTimestamps.length > 0 && requestTimestamps[0] < now - RATE_WINDOW_MS) {
    requestTimestamps.shift();
  }
  return requestTimestamps.length < RATE_LIMIT;
}

function recordRequest(): void {
  requestTimestamps.push(Date.now());
}

// --- API Client ---

class RepresentApiClient {
  private baseUrl: string;

  constructor() {
    this.baseUrl = env.REPRESENT_API_URL;
  }

  async getByPostalCode(code: string): Promise<RepresentPostalCodeResponse> {
    if (!checkRateLimit()) {
      throw new Error('Represent API rate limit reached. Please try again in a minute.');
    }

    const url = `${this.baseUrl}/postcodes/${code}/`;
    logger.debug(`Represent API request: ${url}`);

    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 10_000);

    try {
      recordRequest();
      const response = await fetch(url, {
        signal: controller.signal,
        headers: { Accept: 'application/json' },
      });

      if (!response.ok) {
        const text = await response.text().catch(() => '');
        throw new Error(`Represent API error ${response.status}: ${text}`);
      }

      return (await response.json()) as RepresentPostalCodeResponse;
    } finally {
      clearTimeout(timeout);
    }
  }

  async testConnection(): Promise<{ ok: boolean; message: string }> {
    try {
      const url = `${this.baseUrl}/boundary-sets/?limit=1`;
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), 10_000);

      try {
        const response = await fetch(url, {
          signal: controller.signal,
          headers: { Accept: 'application/json' },
        });

        if (!response.ok) {
          return { ok: false, message: `HTTP ${response.status}` };
        }

        return { ok: true, message: 'Represent API is reachable' };
      } finally {
        clearTimeout(timeout);
      }
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Unknown error';
      return { ok: false, message };
    }
  }
}

export const representApiClient = new RepresentApiClient();
