import { SourceAdapter } from './base.mjs';
import { log } from '../logger.mjs';
import { join } from 'node:path';
import { ensureDir, LOG_DIR, fileExistsAtAnyLayout, safeFilename } from '../paths.mjs';

// Einstieg übers Banking-Portal — leitet auf access.ing.de/delogin/w/login?t=<session-token> weiter.
// Direkte Navigation auf die Login-Seite bringt nichts, weil der Token session-gebunden ist.
const ENTRY_URL = 'https://banking.ing.de/app/obligo';

export class IngAdapter extends SourceAdapter {
  static key = 'ing';
  static label = 'ING';
  static group = 'Banken';

  async login() {
    const { page } = this;
    await page.goto(ENTRY_URL, { waitUntil: 'commit' });
    await page.waitForTimeout(1500);
    log.info(`[ing] URL nach goto: ${page.url()}`);

    // Session noch aktiv? Dann leitet ING nicht auf access.ing.de um.
    if (!/\/(de)?login/i.test(page.url())) {
      log.info(`[ing] Session noch aktiv`);
      return;
    }

    if (await this.dismissConsent()) log.info('[ing] Consent-Banner bestätigt');

    // Stabile Feld-Namen aus ING's Form-Framework
    const user = page.locator('input[name="view:alias:alias"]').first();
    const pw = page.locator('input[name="view:secret:secret"]').first();
    const submit = page.locator('button[name="view:next-inline"]').first();

    try {
      await user.waitFor({ state: 'visible', timeout: 180_000 });
    } catch {
      const shot = join(ensureDir(LOG_DIR), `ing-no-form-${Date.now()}.png`);
      await page.screenshot({ path: shot, fullPage: true }).catch(() => {});
      throw new Error(`[ing] Login-Form nicht sichtbar. URL: ${page.url()} — Screenshot: ${shot}`);
    }

    await user.fill(this.account.credentials.username);
    await pw.fill(this.account.credentials.password);
    await page.waitForTimeout(500);

    await submit.waitFor({ state: 'visible', timeout: 10_000 });
    log.info('[ing] Drücke Anmelden');
    await submit.click();

    log.info('[ing] Warte auf 2FA-Bestätigung (ING-App / iTAN / Mobile-Bestätigung) — bis zu 3 Min ...');
    try {
      // Login ist durch, sobald wir access.ing.de/delogin verlassen — Redirect zurück zu banking.ing.de
      await page.waitForFunction(
        () => !/access\.ing\.de\/delogin/.test(location.href) && !/\/login/.test(location.pathname),
        null,
        { timeout: 180_000 },
      );
    } catch (e) {
      const shot = join(ensureDir(LOG_DIR), `ing-no-redirect-${Date.now()}.png`);
      await page.screenshot({ path: shot, fullPage: true }).catch(() => {});
      log.warn(`[ing] Kein Redirect nach 3 Min. URL: ${page.url()} — Screenshot: ${shot}`);
      throw e;
    }
    log.info(`[ing] Login erfolgreich (URL: ${page.url()})`);
  }

  async *iterateStatements() {
    const { page } = this;

    // Zur Postbox: Click auf den Quicklink (URL enthält session-gebundenen Token, nicht stabil)
    log.info('[ing] Klicke Post-Box');
    const postboxLink = page.locator('a.quicklink-postbox').first();
    try {
      await postboxLink.waitFor({ state: 'visible', timeout: 30_000 });
    } catch {
      const shot = join(ensureDir(LOG_DIR), `ing-no-postbox-link-${Date.now()}.png`);
      await page.screenshot({ path: shot, fullPage: true }).catch(() => {});
      throw new Error(`[ing] Post-Box-Link nicht sichtbar. URL: ${page.url()} — Screenshot: ${shot}`);
    }
    await postboxLink.click();
    await page.waitForLoadState('networkidle', { timeout: 15_000 }).catch(() => {});
    await page.waitForTimeout(500);
    log.info(`[ing] Postbox-URL: ${page.url()}`);

    // Personen-Chips (Gemeinschaftskonto, Kinder, ...) — alle durchgehen
    const chipsLocator = page.locator('.chips-nav a.button.chip');
    const chipTexts = (await chipsLocator.allInnerTexts()).map((t) => t.trim()).filter(Boolean);
    if (chipTexts.length === 0) chipTexts.push(''); // Keine Chips → aktuelle Ansicht iterieren
    log.info(`[ing] ${chipTexts.length} Inhaber-Tab(s): ${chipTexts.join(' | ') || '(nur aktuelle Ansicht)'}`);

    for (const chipText of chipTexts) {
      if (chipText) {
        const chip = page.locator('.chips-nav a.button.chip', { hasText: chipText }).first();
        const cls = (await chip.getAttribute('class').catch(() => '')) || '';
        if (!cls.includes('selected')) {
          log.info(`[ing] Wechsle zu: ${chipText}`);
          await chip.click();
          await page.waitForLoadState('networkidle', { timeout: 15_000 }).catch(() => {});
          await page.waitForTimeout(500);
        } else {
          log.info(`[ing] Aktiv: ${chipText}`);
        }
      }

      // Zeitraum auf 10 Jahre setzen (Default ist 1 Jahr), um auch ältere Dokumente zu sehen
      try {
        const zeitraum = page.locator('select[name="erweiterterFilter:zeitraum:zeitraum"]').first();
        if (!(await zeitraum.isVisible({ timeout: 1000 }).catch(() => false))) {
          // Filter-Panel ausklappen. Der ID-basierte Selektor "#id3c95a55" ist serverseitig
          // generiert und kann sich bei jedem ING-Frontend-Deploy aendern → mehrere Fallbacks
          // probieren, damit der Filter auch nach UI-Updates noch klickt:
          //   1) Original-ID-Selektor (schnellster Weg solange er stimmt)
          //   2) Beliebiges Element mit data-toggle-selector mit erweitert/filter-Hint im Text
          //   3) Button per accessible Name (Erweiterte Suche / Mehr Filter)
          const toggleCandidates = [
            page.locator('[data-toggle-selector="#id3c95a55"]').first(),
            page.locator('[data-toggle-selector]').filter({ hasText: /erweitert|filter|mehr/i }).first(),
            page.getByRole('button', { name: /erweiterter? filter|erweiterte such|mehr filter/i }).first(),
          ];
          for (const t of toggleCandidates) {
            if (await t.isVisible({ timeout: 800 }).catch(() => false)) {
              await t.click().catch(() => {});
              await page.waitForTimeout(300);
              break;
            }
          }
        }
        if (await zeitraum.isVisible({ timeout: 2000 }).catch(() => false)) {
          const current = await zeitraum.inputValue().catch(() => '');
          if (current !== 'MONATE120') {
            await zeitraum.selectOption('MONATE120');
            const applyBtn = page.locator('button[name="erweiterterFilter:filtern"]').first();
            await applyBtn.click();
            await page.waitForLoadState('networkidle', { timeout: 15_000 }).catch(() => {});
            await page.waitForTimeout(500);
            log.info('[ing] Zeitraum auf 10 Jahre erweitert');
          }
        }
      } catch (e) {
        log.warn(`[ing] Zeitraum-Filter nicht gesetzt: ${e.message}`);
      }

      const rows = page.locator('.ibbr-table-body .ibbr-table-row[role="row"]');
      const total = await rows.count();
      log.info(`[ing] ${total} Dokumente${chipText ? ` für "${chipText}"` : ''}`);

      for (let idx = 0; idx < total; idx++) {
        const row = rows.nth(idx);
        const docType = (await row.locator('.postbox-grid-left > span').last().innerText().catch(() => '')).replace(/\s+/g, ' ').trim();
        const description = (await row.locator('.postbox-grid-description').innerText().catch(() => '')).replace(/\s+/g, ' ').trim();
        const dateStr = (await row.locator('.postbox-grid-right').innerText().catch(() => '')).trim();

        const dateMatch = dateStr.match(/(\d{2})\.(\d{2})\.(\d{4})/);
        const year = dateMatch ? Number(dateMatch[3]) : new Date().getFullYear();

        // Kontonummer aus description extrahieren (z.B. "Girokonto 5402327479 / DE77 ...")
        const kontoMatch = description.match(/\b(\d{10})\b/);
        let subSuffix;
        if (/Steuerbescheinigung|Jahressteuerbescheinigung|Erträgnisaufstellung/i.test(docType)) {
          subSuffix = 'Steuer';
        } else if (kontoMatch) {
          const kontoNr = kontoMatch[1];
          subSuffix = this.account.kontoAliases?.[kontoNr] || kontoNr;
        } else if (/Information|Sicherheitshinweis|Nachricht/i.test(docType)) {
          subSuffix = 'Mitteilungen';
        } else {
          subSuffix = 'Sonstiges';
        }
        // Inhaber als oberste Ordnerebene (falls Chips/Personen vorhanden): "Leonard Glück/5402327479"
        const subfolder = chipText ? `${chipText}/${subSuffix}` : subSuffix;

        // Dateiname: "JJJJ-MM-TT DocType - Betreff.pdf" — Datum vorn fuer chronologische Sortierung.
        const hasIbanDesc = /IBAN|DE\d{20}|^\w+konto/i.test(description);
        const descPart = description && !hasIbanDesc ? ` - ${description.slice(0, 80)}` : '';
        const dateIso = dateMatch ? `${dateMatch[3]}-${dateMatch[2]}-${dateMatch[1]}` : '';
        const datePrefix = dateIso ? `${dateIso} ` : '';
        const filename = `${datePrefix}${docType}${descPart}.pdf`;

        // Pre-Skip
        if (this.cfg) {
          const safeSub = subfolder ? safeFilename(subfolder) : undefined;
          const existing = fileExistsAtAnyLayout(this.cfg, this.account.folder, year, safeSub, filename);
          if (existing) {
            log.info(`[${this.account.id}] skip (bereits vorhanden): ${existing}`);
            continue;
          }
        }

        // Download-Link in der (ggf. eingeklappten) Actions-Leiste
        const dlLink = row.locator('.postbox-grid-actions a.button.outline', { hasText: /^Download$/i }).first();
        const href = await dlLink.getAttribute('href').catch(() => null);
        if (!href) {
          log.warn(`[ing] Kein Download-Link bei "${docType}" (${dateStr})`);
          continue;
        }

        const absUrl = new URL(href, page.url()).href;
        try {
          const resp = await this.context.request.get(absUrl);
          if (!resp.ok()) {
            log.warn(`[ing] HTTP ${resp.status()} bei "${filename}"`);
            continue;
          }
          yield { filename, year, buffer: await resp.body(), subfolder };
        } catch (e) {
          log.warn(`[ing] Download fehlgeschlagen bei "${filename}": ${e.message}`);
        }
      }
    }
  }
}
