import type { FeedProvider, ExternalFeed, RawProduct, FetchOptions, FeedInfo, ProviderConfig } from './types.js';
import { log, logError } from './db.js';

const BATCH_SIZE = 1000;

export class WebgainsAdapter implements FeedProvider {
  readonly slug = 'webgains';
  private apiUrl: string = '';
  private token: string = '';
  private publisherId: string = '';

  async authenticate(config: ProviderConfig): Promise<void> {
    this.apiUrl = config.api_url || process.env.WEBGAINS_API_URL || 'https://platform-api.webgains.com';
    this.token = config.token || process.env.WEBGAINS_TOKEN || '';
    this.publisherId = config.publisher_id || process.env.WEBGAINS_PUBLISHER_ID || '';

    if (!this.token) throw new Error('Webgains: missing API token');
    if (!this.publisherId) throw new Error('Webgains: missing publisher ID');

    log(`Webgains: authenticated (publisher ${this.publisherId})`);
  }

  private headers(): Record<string, string> {
    return {
      'Authorization': `Bearer ${this.token}`,
      'Content-Type': 'application/json',
    };
  }

  async listFeeds(): Promise<ExternalFeed[]> {
    // List feeds across all campaigns
    const url = `${this.apiUrl}/auth/publishers/${this.publisherId}/feeds`;
    const resp = await fetch(url, { headers: this.headers() });
    if (!resp.ok) throw new Error(`Webgains listFeeds: ${resp.status} ${resp.statusText}`);
    const data = await resp.json() as any;

    return (data.data || []).map((f: any) => ({
      externalId: String(f.id),
      name: f.name || '',
      merchantName: f.program?.name || f.name || '',
      productCount: f.products_successfully_stored || 0,
      lastUpdated: null,
      feedUrl: undefined,
    }));
  }

  async *fetchProducts(feedId: string, options?: FetchOptions): AsyncGenerator<RawProduct[]> {
    const campaignId = options?.limit ? undefined : undefined; // Use feed config
    // Webgains: paginate through product search endpoint
    let offset = options?.offset || 0;
    const pageSize = BATCH_SIZE;
    let hasMore = true;

    while (hasMore) {
      const url = new URL(`${this.apiUrl}/auth/publishers/${this.publisherId}/feeds/products`);
      url.searchParams.set('feeds[]', feedId);
      url.searchParams.set('size', String(pageSize));
      url.searchParams.set('offset', String(offset));
      url.searchParams.set('fields[]', 'title');
      url.searchParams.append('fields[]', 'brand');
      url.searchParams.append('fields[]', 'price');
      url.searchParams.append('fields[]', 'link');
      url.searchParams.append('fields[]', 'image_link');
      url.searchParams.append('fields[]', 'description');
      url.searchParams.append('fields[]', 'id');
      url.searchParams.append('fields[]', 'ean');
      url.searchParams.append('fields[]', 'colour');
      url.searchParams.append('fields[]', 'rrp');
      url.searchParams.append('fields[]', 'currency');
      url.searchParams.append('fields[]', 'category_name');
      url.searchParams.append('fields[]', 'in_stock');
      url.searchParams.append('fields[]', 'model_number');

      const resp = await fetch(url.toString(), { headers: this.headers() });
      if (!resp.ok) {
        if (resp.status === 429) {
          const retryAfter = parseInt(resp.headers.get('Retry-After') || '60');
          log(`Webgains: rate limited, waiting ${retryAfter}s`);
          await new Promise(r => setTimeout(r, retryAfter * 1000));
          continue;
        }
        throw new Error(`Webgains fetchProducts: ${resp.status} ${resp.statusText}`);
      }

      const data = await resp.json() as any;
      const products: RawProduct[] = (data.data || []).map((p: any) => ({
        ...p,
        _external_id: String(p.id || ''),
      }));

      if (products.length === 0) {
        hasMore = false;
      } else {
        yield products;
        offset += products.length;
        if (products.length < pageSize) hasMore = false;
      }
    }
  }

  async getFeedInfo(feedId: string): Promise<FeedInfo> {
    const feeds = await this.listFeeds();
    const feed = feeds.find(f => f.externalId === feedId);
    return {
      productCount: feed?.productCount || 0,
      lastUpdated: feed?.lastUpdated || null,
    };
  }
}
