import { SourceAdapter } from './base.mjs';
import { log } from '../logger.mjs';
import { join } from 'node:path';
import { existsSync, statSync, readdirSync } from 'node:fs';
import { ensureDir, LOG_DIR, targetDirPath, safeFilename } from '../paths.mjs';

const ENTRY_URL = 'https://www.amazon.de/your-orders/orders';

const MONTHS = {
  Januar: '01', Februar: '02', März: '03', April: '04',
  Mai: '05', Juni: '06', Juli: '07', August: '08',
  September: '09', Oktober: '10', November: '11', Dezember: '12',
};

function parseGermanDate(text) {
  const parts = (text || '').trim().split(/\s+/);
  if (parts.length < 3) return null;
  const [dayRaw, monthName, year] = parts;
  const day = dayRaw.replace('.', '').padStart(2, '0');
  const month = MONTHS[monthName];
  if (!month) return null;
  return { iso: `${year}-${month}-${day}`, year: Number(year) };
}

export class AmazonAdapter extends SourceAdapter {
  static key = 'amazon';
  static label = 'Amazon';
  static group = 'Geschäfte';

  async dismissAmazonCookies() {
    const { page } = this;
    // Amazon-Cookie-Banner: Standard-Selektoren für "Accept All"
    const candidates = [
      page.locator('#sp-cc-accept').first(),
      page.locator('input[name="accept"][data-cel-widget*="cookie"]').first(),
      page.locator('button:has-text("Akzeptieren")').first(),
    ];
    for (const c of candidates) {
      if (await c.isVisible({ timeout: 800 }).catch(() => false)) {
        await c.click().catch(() => {});
        log.info('[amazon] Cookie-Banner akzeptiert');
        await page.waitForTimeout(300);
        return;
      }
    }
  }

  async login() {
    const { page } = this;
    await page.goto(ENTRY_URL, { waitUntil: 'domcontentloaded' });
    await page.waitForTimeout(1200);
    log.debug(`[amazon] URL nach goto: ${page.url()}`);

    await this.dismissAmazonCookies();

    if (!/ap\/signin/.test(page.url())) {
      log.info('[amazon] Session noch aktiv');
      return;
    }

    // E-Mail
    const emailField = page.locator('input[name="email"], input[type="email"]').first();
    try {
      await emailField.waitFor({ state: 'visible', timeout: 30_000 });
    } catch {
      const shot = join(ensureDir(LOG_DIR), `amazon-no-email-${Date.now()}.png`);
      await page.screenshot({ path: shot, fullPage: true }).catch(() => {});
      throw new Error(`[amazon] E-Mail-Feld nicht sichtbar. URL: ${page.url()} — Screenshot: ${shot}`);
    }
    await emailField.fill(this.account.credentials.username);
    await page.locator('input#continue, input[type="submit"]').first().click();
    await page.waitForTimeout(1000);

    // Passwort
    const pwField = page.locator('input[name="password"]').first();
    await pwField.waitFor({ state: 'visible', timeout: 30_000 });
    await pwField.fill(this.account.credentials.password);
    await page.locator('input#signInSubmit, input[type="submit"]').first().click();

    log.info('[amazon] Warte auf Login-Abschluss (bis zu 3 Min — evtl. 2FA / Captcha) ...');
    try {
      await page.waitForFunction(
        () => !/ap\/(signin|cvf|mfa)/.test(location.href),
        null,
        { timeout: 180_000 },
      );
    } catch (e) {
      const shot = join(ensureDir(LOG_DIR), `amazon-no-redirect-${Date.now()}.png`);
      await page.screenshot({ path: shot, fullPage: true }).catch(() => {});
      log.warn(`[amazon] Kein Redirect nach 3 Min. URL: ${page.url()} — Screenshot: ${shot}`);
      throw e;
    }
    log.info(`[amazon] Login erfolgreich (URL: ${page.url()})`);
  }

  async *iterateYearOrders(year) {
    const { page } = this;
    const url = `${ENTRY_URL}?timeFilter=year-${year}`;
    log.info(`[amazon] Lade Bestellungen ${year} — ${url}`);
    await page.goto(url, { waitUntil: 'domcontentloaded' });
    await page.waitForTimeout(1500);
    await this.dismissAmazonCookies();

    let pageNum = 1;
    while (true) {
      // Auf Order-Karten warten — wenn keine, Liste ist leer für dieses Jahr
      try {
        await page.locator('.order-card.js-order-card').first().waitFor({ state: 'visible', timeout: 8000 });
      } catch {
        log.info(`[amazon] Keine Bestellungen für ${year} (Seite ${pageNum})`);
        return;
      }

      const orders = page.locator('.order-card.js-order-card');
      const count = await orders.count();
      log.info(`[amazon] Jahr ${year}, Seite ${pageNum}: ${count} Bestellungen`);

      for (let i = 0; i < count; i++) {
        const order = orders.nth(i);
        const dateText = ((await order.locator('.order-header__header-list-item span.a-size-base.a-color-secondary.aok-break-word').first().textContent().catch(() => '')) || '').trim();
        const orderId = ((await order.locator('.yohtmlc-order-id span[dir="ltr"]').first().textContent().catch(() => '')) || '').trim();
        const invoiceHref = await order.locator('a[href*="/your-orders/invoice"]').first().getAttribute('href').catch(() => null);

        if (!dateText || !orderId || !invoiceHref) {
          log.debug(`[amazon] Bestellung unvollständig — date="${dateText}", id="${orderId}", href=${invoiceHref}`);
          continue;
        }

        const parsed = parseGermanDate(dateText);
        if (!parsed) {
          log.warn(`[amazon] Datum nicht parsebar: "${dateText}"`);
          continue;
        }

        // Best-effort: Total-Betrag und ersten Artikel-Titel extrahieren
        let total = null;
        let itemTitle = null;
        try {
          const headerText = ((await order.locator('.order-header').first().textContent().catch(() => '')) || '').replace(/\s+/g, ' ');
          // Deutsche Beträge: 19,99 € oder 1.234,56 €
          const m = headerText.match(/(\d{1,4}(?:\.\d{3})*,\d{2})\s*€/);
          if (m) total = m[1].replace(/\./g, '') + '€';
        } catch { /* ignore */ }
        try {
          const itemLink = order.locator('a.a-link-normal[href*="/dp/"], a.a-link-normal[href*="/gp/product/"]').first();
          const t = ((await itemLink.textContent({ timeout: 800 }).catch(() => '')) || '').replace(/\s+/g, ' ').trim();
          if (t) itemTitle = t.slice(0, 60);
        } catch { /* ignore */ }

        const parts = [parsed.iso, orderId];
        if (total) parts.push(`(${total})`);
        if (itemTitle) parts.push(itemTitle);
        const filename = `${parts.join(' ')}.pdf`;

        // Pre-skip: matcht jeden vorhandenen Filename, der mit "<datum>" beginnt und die orderId enthaelt
        // (deckt altes Format "2024-12-23-302-...pdf" und neues "2024-12-23 302-... 19,99€ ...pdf" ab).
        // Prueft beide Layout-Varianten (mit/ohne Year-Subfolder).
        if (this.cfg) {
          const prefix = `${parsed.iso}`;
          const current = this.cfg.layout?.yearSubfolder ?? true;
          let foundPath = null;
          for (const yearSubfolder of [current, !current]) {
            const targetDir = targetDirPath(this.cfg.rootDir, this.account.folder, parsed.year, undefined, { yearSubfolder });
            if (!existsSync(targetDir)) continue;
            const f = readdirSync(targetDir).find((name) => {
              if (!name.toLowerCase().endsWith('.pdf')) return false;
              if (!name.startsWith(prefix)) return false;
              return name.includes(orderId);
            });
            if (f) {
              const full = join(targetDir, f);
              if (statSync(full).size > 0) { foundPath = full; break; }
            }
          }
          if (foundPath) {
            log.info(`[${this.account.id}] skip (bereits vorhanden): ${foundPath}`);
            continue;
          }
        }

        const absUrl = new URL(invoiceHref, page.url()).href;
        try {
          const resp = await this.context.request.get(absUrl);
          if (!resp.ok()) {
            log.warn(`[amazon] HTTP ${resp.status()} bei "${filename}"`);
            continue;
          }
          const buffer = Buffer.from(await resp.body());
          const isPdf = buffer.length >= 5 && buffer.slice(0, 4).toString('latin1') === '%PDF';

          if (isPdf) {
            yield { filename, year: parsed.year, buffer };
          } else {
            // HTML-Auswahl-Seite: extrahiere alle direkten PDF-Links (typisch /documents/download/<uuid>/invoice.pdf)
            const html = buffer.toString('utf8');
            const pdfPaths = [...new Set(
              [...html.matchAll(/href=["']([^"']*\/documents\/download\/[^"']+\.pdf[^"']*)["']/gi)].map((m) => m[1]),
            )];
            if (pdfPaths.length === 0) {
              const preview = html.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').slice(0, 200);
              log.warn(`[amazon] Auswahl-Seite ohne PDF-Link bei "${filename}" — Preview: ${preview}`);
              continue;
            }
            log.info(`[amazon] Auswahl-Seite mit ${pdfPaths.length} PDF(s) für ${orderId}`);
            for (let p = 0; p < pdfPaths.length; p++) {
              const numbered = pdfPaths.length > 1
                ? filename.replace(/\.pdf$/, ` (${p + 1}-${pdfPaths.length}).pdf`)
                : filename;
              const pdfUrl = new URL(pdfPaths[p], page.url()).href;
              try {
                const pdfResp = await this.context.request.get(pdfUrl);
                if (!pdfResp.ok()) {
                  log.warn(`[amazon] HTTP ${pdfResp.status()} bei "${numbered}"`);
                  continue;
                }
                const pdfBuf = Buffer.from(await pdfResp.body());
                if (pdfBuf.slice(0, 4).toString('latin1') !== '%PDF') {
                  log.warn(`[amazon] Direkter Link liefert kein PDF bei "${numbered}" (size=${pdfBuf.length})`);
                  continue;
                }
                yield { filename: numbered, year: parsed.year, buffer: pdfBuf };
              } catch (e) {
                log.warn(`[amazon] PDF-Download fehlgeschlagen "${numbered}": ${e.message}`);
              }
              await page.waitForTimeout(300);
            }
          }
        } catch (e) {
          log.warn(`[amazon] Download fehlgeschlagen "${filename}": ${e.message}`);
        }

        // kleines Throttle, Amazon mag's gemütlich
        await page.waitForTimeout(400);
      }

      // Nächste Seite
      const lastLi = page.locator('li.a-last').first();
      const lastLiClass = (await lastLi.getAttribute('class').catch(() => '')) || '';
      if (lastLiClass.includes('a-disabled')) {
        log.debug(`[amazon] Letzte Seite erreicht für Jahr ${year}`);
        return;
      }
      const nextLink = lastLi.locator('a').first();
      if (!(await nextLink.isVisible({ timeout: 1000 }).catch(() => false))) {
        log.debug(`[amazon] Kein Next-Link — Jahr ${year} fertig`);
        return;
      }
      log.debug(`[amazon] Klicke "Weiter" → Seite ${pageNum + 1}`);
      await nextLink.click();
      await page.waitForLoadState('networkidle', { timeout: 15_000 }).catch(() => {});
      await page.waitForTimeout(1200);
      pageNum++;
    }
  }

  async *iterateStatements() {
    const currentYear = new Date().getFullYear();
    // Default: 7 Jahre rueckwaerts. Steuerliche Aufbewahrungsfristen variieren (Privat 2J,
    // Selbststaendige 6-10J), 7J ist ein sicherer Mittelweg. Nur beim ersten Lauf zieht das
    // viele Bestellungen — Pre-Skip macht Folgelaeufe billig. User kann via account.years
    // explizit ueberschreiben (z.B. nur [2024,2025] fuer einen Catch-up-Lauf).
    const years = this.account.years || Array.from({ length: 7 }, (_, k) => currentYear - 6 + k);
    log.info(`[amazon] Iteriere Jahre: ${years.join(', ')}`);
    for (const year of years) {
      yield* this.iterateYearOrders(year);
    }
  }
}
