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

const BATCH_SIZE = 1000;

/**
 * Awin Adapter (placeholder — credentials TBD)
 * 
 * Awin feeds are typically CSV downloads from their datafeed service.
 * Once credentials are configured, this adapter will:
 * 1. List available programmes with product feeds
 * 2. Download CSV feeds and stream-parse them
 */
export class AwinAdapter implements FeedProvider {
  readonly slug = 'awin';
  private apiToken: string = '';
  private publisherId: string = '';

  async authenticate(config: ProviderConfig): Promise<void> {
    this.apiToken = config.api_token || process.env.AWIN_API_TOKEN || '';
    this.publisherId = config.publisher_id || process.env.AWIN_PUBLISHER_ID || '';

    if (!this.apiToken || !this.publisherId) {
      log('Awin: credentials not configured yet (placeholder adapter)');
      return;
    }

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

  async listFeeds(): Promise<ExternalFeed[]> {
    if (!this.apiToken || !this.publisherId) {
      log('Awin: not configured, returning empty feed list');
      return [];
    }

    try {
      const resp = await fetch(
        `https://api.awin.com/publishers/${this.publisherId}/programmedetails`,
        { headers: { Authorization: `Bearer ${this.apiToken}` } }
      );
      if (!resp.ok) throw new Error(`Awin: ${resp.status} ${resp.statusText}`);
      const programmes = await resp.json() as any[];

      return programmes
        .filter((p: any) => p.hasProductFeed)
        .map((p: any) => ({
          externalId: String(p.id),
          name: p.name || '',
          merchantName: p.name || '',
          productCount: p.productFeedCount || 0,
          lastUpdated: null,
          feedUrl: p.productFeedUrl || undefined,
        }));
    } catch (err: any) {
      logError('Awin listFeeds failed', err);
      return [];
    }
  }

  async *fetchProducts(feedId: string, _options?: FetchOptions): AsyncGenerator<RawProduct[]> {
    if (!this.apiToken) {
      log('Awin: not configured, skipping fetch');
      return;
    }

    const feedUrl = `https://productdata.awin.com/datafeed/list/apikey/${this.apiToken}/language/nl/fid/${feedId}/format/csv/`;

    try {
      const resp = await fetch(feedUrl);
      if (!resp.ok) throw new Error(`Awin feed download: ${resp.status}`);

      const text = await resp.text();
      const records = await this.parseCsv(text);

      // Yield in batches
      for (let i = 0; i < records.length; i += BATCH_SIZE) {
        const batch: RawProduct[] = records.slice(i, i + BATCH_SIZE).map((row: any) => ({
          ...row,
          _external_id: row.aw_product_id || row.merchant_product_id || '',
        }));
        yield batch;
      }
    } catch (err: any) {
      logError(`Awin fetch error for feed ${feedId}`, err);
    }
  }

  private parseCsv(text: string): Promise<any[]> {
    return new Promise((resolve, reject) => {
      const records: any[] = [];
      const parser = parse({
        columns: true,
        skip_empty_lines: true,
        relax_column_count: true,
        delimiter: ',',
      });
      parser.on('readable', () => {
        let record;
        while ((record = parser.read()) !== null) {
          records.push(record);
        }
      });
      parser.on('error', reject);
      parser.on('end', () => resolve(records));
      Readable.from(text).pipe(parser);
    });
  }

  async getFeedInfo(feedId: string): Promise<FeedInfo> {
    return { productCount: 0, lastUpdated: null };
  }
}
