#!/usr/bin/env node
/**
 * Vygeneruje obrázek přes Gemini na přihlášeném účtu uživatele.
 * Výchozí model je Flash (vysoký denní limit). Pro jen na vyžádání: --model pro.
 *
 *   node scripts/generuj.mjs "<prompt>" [přepínače]
 *
 * Přepínače:
 *   --pomer 16:9        poměr stran (1:1 16:9 9:16 4:3 3:4 2:3 3:2), výchozí 1:1
 *   --model pro|flash   který model použít; VÝCHOZÍ flash.
 *                       Flash má denní strop mnohem výš než Pro — Pro dojde
 *                       i s předplatným AI Pro. Pro jen na výslovné přání.
 *                       'auto' = nepřepínat, nechat co má uživatel vybrané.
 *   --rozliseni 2K      1K | 2K | 4K, výchozí NEUVÁDÍME (necháme na Gemini).
 *                       Vyšší rozlišení = pomalejší generování a rychleji
 *                       vyčerpaný denní limit Pro modelu. Kdo chce, dopíše si ho.
 *   --kam "<složka>"    kam uložit, výchozí ~/Pictures/VojtaCode-obrazky
 *   --ref "<soubor>"    referenční obrázek (jde uvést vícekrát) — image-to-image
 *   --okno              nechá okno prohlížeče viditelné (na ladění)
 *
 * Značky na stdout: LOGIN_REQUIRED / MODE:<režim> / REFS_UPLOADED / SAVED:<cesta>
 */
import fs from 'node:fs';
import path from 'node:path';
import {
  otevriProhlizec, jePrihlasen, SELEKTOR_VSTUPU, GEMINI,
  vychoziVystup, sirkaObrazku, nazevZPromptu, PROFIL_PROHLIZECE
} from './lib/spolecne.mjs';

/* ── argumenty ─────────────────────────────────────────────────────────── */
const argv = process.argv.slice(2);
const prompt = argv.find((a) => !a.startsWith('--'));
if (!prompt) {
  console.error('Chybí prompt.\nPoužití: node scripts/generuj.mjs "popis obrázku" [--pomer 16:9] [--model flash] [--rozliseni 2K]');
  process.exit(1);
}
function prepinac(jmeno, vychozi) {
  const i = argv.indexOf('--' + jmeno);
  return i >= 0 && argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[i + 1] : vychozi;
}
const pomer = prepinac('pomer', '1:1').trim();
const rozliseni = prepinac('rozliseni', 'auto').trim();
// auto = nepřepínat model (výchozí). pro | flash = přepnout natvrdo.
// VÝCHOZÍ = flash. Flash má denní strop mnohem výš než Pro (Pro dojde i na
// předplatném AI Pro). Kdo chce Pro, řekne si: --model pro | 'použij Pro model'.
const model = prepinac('model', 'flash').trim().toLowerCase();
const kam = prepinac('kam', vychoziVystup());
const prohlizec = prepinac('prohlizec', null);
const viditelny = argv.includes('--okno');
const reference = [];
for (let i = 0; i < argv.length; i++) {
  if (argv[i] === '--ref' && argv[i + 1]) reference.push(argv[++i]);
}

/* ── text, který napíšeme do Gemini ────────────────────────────────────── */
function zprava() {
  const dodatky = [];
  if (pomer && pomer.toLowerCase() !== 'auto') dodatky.push(`poměr stran ${pomer}`);
  if (rozliseni && rozliseni.toLowerCase() !== 'auto') dodatky.push(`co nejvyšší rozlišení (${rozliseni})`);
  return `Vygeneruj obrázek: ${prompt}${dodatky.length ? ' — ' + dodatky.join(', ') : ''}`;
}

/**
 * Přepne model v Gemini. Výchozí je 'auto' = NEPŘEPÍNAT, necháme ten,
 * který má uživatel vybraný. Pro model má vlastní denní strop (nižší než
 * Flash) i na předplatném AI Pro, takže si ho nikdo nesmí zapnout omylem.
 */
async function nastavModel(page, cil) {
  if (!cil || cil === 'auto') {
    try {
      const b = page.locator('button[aria-label^="Open mode picker" i], button[data-test-id="bard-mode-menu-button"]').first();
      if (!(await b.count())) return 'neznámý';
      const popis = (await b.getAttribute('aria-label')) || '';
      return popis.replace(/.*currently\s*/i, '').trim() || 'neznámý';
    } catch (e) { return 'neznámý'; }
  }
  try {
    const vyberModelu = page.locator(
      'button[aria-label^="Open mode picker" i], button[data-test-id="bard-mode-menu-button"]'
    ).first();
    if (!(await vyberModelu.count())) return 'neznámý';
    let popis = (await vyberModelu.getAttribute('aria-label')) || '';
    const aktivni = () => (popis.replace(/.*currently\s*/i, '').trim() || 'neznámý');
    const uzTam = cil === 'pro'
      ? /currently\s*.*pro/i.test(popis)
      : /currently\s*.*(flash|lite|fast)/i.test(popis);
    if (uzTam) return aktivni();
    await vyberModelu.click({ timeout: 4000 }).catch(() => {});
    await page.waitForTimeout(1000);
    const polozky = page.locator('[role="menuitem"],[role="option"],[role="menuitemradio"]');
    let volba = cil === 'pro'
      ? polozky.filter({ hasText: /pro/i }).filter({ hasNotText: /flash|lite/i }).first()
      // Nejlehci model — Gemini ho podle verze zve Flash, Lite nebo Fast.
      : polozky.filter({ hasText: /flash|lite|fast/i }).filter({ hasNotText: /thinking|pro/i }).first();
    if (!(await volba.count()) && cil !== 'pro') {
      // Nabidka se prejmenovala? Vezmi cokoli, co NENI Pro — lehci byva niz.
      volba = polozky.filter({ hasNotText: /pro/i }).last();
    }
    if (await volba.count()) { await volba.click({ timeout: 3000 }).catch(() => {}); await page.waitForTimeout(1000); }
    else await page.keyboard.press('Escape').catch(() => {});
    popis = (await vyberModelu.getAttribute('aria-label')) || popis;
    return aktivni();
  } catch (e) { return 'neznámý'; }
}

/** Nový chat — ať je na stránce právě JEDEN vygenerovaný obrázek. */
async function novyChat(page) {
  try {
    const b = page.locator(
      'button[aria-label*="New chat" i], a[aria-label*="New chat" i], ' +
      'button[aria-label*="Nový chat" i], a[aria-label*="Nový chat" i]'
    ).first();
    if (await b.count().catch(() => 0)) { await b.click({ timeout: 3000 }).catch(() => {}); await page.waitForTimeout(1300); }
  } catch (e) { /* nevadí */ }
}

/* ── běh ───────────────────────────────────────────────────────────────── */
const ctx = await otevriProhlizec({ viditelny: true, prohlizec: prohlizec ? prohlizec.toLowerCase() : undefined });
const page = ctx.pages()[0] ?? (await ctx.newPage());

try {
  console.log('Otevírám Gemini…');
  await page.goto(GEMINI, { waitUntil: 'domcontentloaded', timeout: 60000 });

  const konecPrihlaseni = Date.now() + 5 * 60 * 1000;
  let ohlaseno = false, pokusy = 0;
  for (;;) {
    if (await jePrihlasen(page)) break;
    if (!ohlaseno && pokusy >= 2) {
      console.log('LOGIN_REQUIRED: přihlas se v otevřeném okně (počkám 5 minut).');
      console.log('  Tip: jednorázově to vyřeší `node scripts/prihlaseni.mjs`.');
      ohlaseno = true;
    }
    if (Date.now() > konecPrihlaseni) throw new Error('Přihlášení nedoběhlo do 5 minut.');
    pokusy++;
    await page.waitForTimeout(2000);
  }

  await novyChat(page);
  console.log('MODE:' + (await nastavModel(page, model)));

  // Referenční obrázky (image-to-image) — nahrát PŘED promptem.
  if (reference.length) {
    const existujici = reference.filter((r) => { try { return fs.existsSync(r); } catch (e) { return false; } });
    if (existujici.length) {
      console.log(`Nahrávám ${existujici.length} referenční obrázek(y)…`);
      let hotovo = false;
      try {
        const vstup = page.locator('input[type="file"]');
        if (await vstup.count().catch(() => 0)) { await vstup.first().setInputFiles(existujici, { timeout: 6000 }); hotovo = true; }
      } catch (e) { /* zkusíme přes tlačítko */ }
      if (!hotovo) {
        try {
          const cekaniNaDialog = page.waitForEvent('filechooser', { timeout: 15000 }).catch(() => null);
          const pridat = page.locator(
            'button[aria-label*="Add files" i],button[aria-label*="Upload" i],button[aria-label*="Nahrát" i],' +
            'button[aria-label*="Add photos" i],button[aria-label*="Insert" i],button[aria-label*="attach" i],' +
            'button[aria-label*="Add content" i],button[aria-label*="Open upload" i],button[data-test-id*="upload" i]'
          ).first();
          if (await pridat.count().catch(() => 0)) await pridat.click({ timeout: 4000 }).catch(() => {});
          await page.waitForTimeout(800);
          const polozka = page.locator('[role="menuitem"],[role="option"],button,li')
            .filter({ hasText: /upload|nahrát|image|photo|obráz|soubor|file|z počítače|from computer/i }).first();
          if (await polozka.count().catch(() => 0)) await polozka.click({ timeout: 3000 }).catch(() => {});
          const dialog = await cekaniNaDialog;
          if (dialog) { await dialog.setFiles(existujici); hotovo = true; }
        } catch (e) { /* vzdáme to a jedeme bez referencí */ }
      }
      if (hotovo) { await page.waitForTimeout(7000); console.log('REFS_UPLOADED'); }
      else console.log('VAROVÁNÍ: reference se nepodařilo nahrát, generuji bez nich.');
    }
  }

  const pocetPred = await page.evaluate(() => document.querySelectorAll('img').length);

  console.log('Posílám prompt…');
  const vstup = page.locator(SELEKTOR_VSTUPU).first();
  await vstup.click({ timeout: 15000 });
  await page.keyboard.insertText(zprava());
  await page.keyboard.press('Enter');

  console.log('Čekám na obrázek (max 4 minuty)…');
  const konec = Date.now() + 4 * 60 * 1000;
  let ulozeno = null;

  while (Date.now() < konec && !ulozeno) {
    await page.waitForTimeout(3000);

    // Došel denní limit modelu? Gemini to napíše do odpovědi. Nemá smysl čekat
    // čtyři minuty na obrázek, který nepřijde — řekni to hned a nabídni Flash.
    // (Pro model má vlastní, výrazně nižší denní strop než Flash — platí i na
    //  předplatném AI Pro. Zjištěno 1. 8. 2026 přímo při natáčení.)
    const limit = await page.evaluate(() => {
      const t = document.body.innerText || '';
      return /jakmile se váš limit obnoví|limit se obnoví|your limit resets|reached your limit|you've hit your limit|limit for .*images/i.test(t);
    }).catch(() => false);
    if (limit) {
      console.error('LIMIT_MODELU: Gemini hlásí vyčerpaný denní limit pro tenhle model.');
      console.error('');
      console.error('Není to chyba skillu ani účtu. Pro model (Nano Banana Pro) má vlastní');
      console.error('denní strop — nižší než Flash — a platí i s předplatným AI Pro.');
      console.error('');
      console.error('Co s tím:');
      console.error('  • generuj s Flash modelem:  --model flash');
      console.error('  • nebo počkej, až se limit obnoví (Gemini → Nastavení → využití)');
      process.exit(3);
    }

    const obrazky = page.locator('img');
    const pocet = await obrazky.count();

    for (let i = pocet - 1; i >= Math.max(0, pocetPred - 2); i--) {
      const img = obrazky.nth(i);
      const info = await img
        .evaluate((el) => ({ w: el.naturalWidth, h: el.naturalHeight, src: el.currentSrc || el.src || '' }))
        .catch(() => null);
      // Vygenerovaný obrázek je velký; avatary a ikonky přeskočíme.
      if (!info || Math.max(info.w, info.h) < 480 || Math.min(info.w, info.h) < 240) continue;
      if (!/^(blob:|https:\/\/lh\d|https:\/\/.*googleusercontent)/.test(info.src)) continue;

      fs.mkdirSync(kam, { recursive: true });
      let soubor = path.join(kam, `${nazevZPromptu(prompt)}.png`);
      let n = 2;
      while (fs.existsSync(soubor)) soubor = path.join(kam, `${nazevZPromptu(prompt)}-${n++}.png`);

      // <img> na stránce je jen zmenšený náhled. Skutečné rozlišení dá tlačítko
      // „Download full-sized image" — hledáme ho v rámci TOHOTO obrázku, ne globálně,
      // ať nestáhneme miniaturu nahrané reference.
      let plnaVelikost = false;
      for (let pokus = 0; pokus < 3 && !plnaVelikost; pokus++) {
        try {
          await img.scrollIntoViewIfNeeded().catch(() => {});
          await img.hover().catch(() => {});
          await page.waitForTimeout(900);
          const obal = img.locator('xpath=ancestor-or-self::*[self::div or self::figure][.//button][1]');
          let tlacitko = obal.locator(
            'button[aria-label="Download full-sized image" i], button[aria-label*="Download full" i]'
          ).first();
          if (!(await tlacitko.count().catch(() => 0))) {
            tlacitko = page.locator(
              'button[aria-label="Download full-sized image" i], button[aria-label*="Download full" i]'
            ).last();
          }
          if (await tlacitko.count().catch(() => 0)) {
            const cekani = page.waitForEvent('download', { timeout: 25000 }).catch(() => null);
            await tlacitko.click({ timeout: 5000, force: true }).catch(() => {});
            const stazeni = await cekani;
            if (stazeni) {
              await stazeni.saveAs(soubor).catch(() => {});
              if (fs.existsSync(soubor) && sirkaObrazku(soubor) >= 700) plnaVelikost = true;
            }
          }
        } catch (e) { /* zkusíme znovu */ }
        if (!plnaVelikost) await page.waitForTimeout(1500);
      }

      // Záloha: stáhnout bajty přímo z toho ověřeného <img>.
      if (!plnaVelikost) {
        const b64 = await img.evaluate(async (el) => {
          try {
            const res = await fetch(el.currentSrc || el.src);
            const buf = await res.arrayBuffer();
            let bin = ''; const by = new Uint8Array(buf);
            for (let j = 0; j < by.length; j++) bin += String.fromCharCode(by[j]);
            return btoa(bin);
          } catch (e) { return null; }
        }).catch(() => null);
        if (b64) fs.writeFileSync(soubor, Buffer.from(b64, 'base64'));
        else { try { await img.screenshot({ path: soubor }); } catch (e) { /* nic */ } }
      }

      // Pojistka: pokud je uložené pořád malé, nebyl to render — hledej dál.
      if (!fs.existsSync(soubor) || sirkaObrazku(soubor) < 480) {
        try { fs.unlinkSync(soubor); } catch (e) { /* nic */ }
        continue;
      }
      ulozeno = soubor;
      break;
    }
  }

  if (!ulozeno) throw new Error('Obrázek se neobjevil do 4 minut — koukni do okna Gemini, jestli tam není hláška.');
  console.log(`SAVED:${ulozeno}`);
} catch (e) {
  const hlaska = String(e?.message || e);
  if (/has been closed|Target closed|browser has been closed/i.test(hlaska)) {
    console.error('\nCHYBA: okno prohlížeče se zavřelo dřív, než byl obrázek hotový.');
    console.error('Během generování ho nezavírej. Pokud se zavřelo samo, je nejspíš');
    console.error('poškozený profil — smaž tuhle složku a přihlas se znovu:');
    console.error(`  ${PROFIL_PROHLIZECE}`);
  } else {
    console.error('\nCHYBA: ' + hlaska);
  }
  process.exitCode = 1;
} finally {
  if (!viditelny) await ctx.close().catch(() => {});
}
