import json, re, zipfile
from pathlib import Path
from datetime import date

ROOT = Path(__file__).parent
PUBLIC = ROOT / "public"
OUT = ROOT.parents[1] / "outputs" / "aimars-upgraded-site-v2.zip"
TODAY = date.today().isoformat()

KEY_PAGES = {
    "best-free-ai-tools.html": ("Best Free AI Tools", "free AI tools"),
    "best-ai-image-generators.html": ("Best AI Image Generators", "AI image generators"),
    "chatgpt-alternatives.html": ("ChatGPT Alternatives", "ChatGPT alternatives"),
    "best-ai-tools-for-coding.html": ("Best AI Tools for Coding", "AI coding tools"),
    "best-ai-tools-for-students.html": ("Best AI Tools for Students", "AI tools for students"),
    "ai-tools-for-business.html": ("AI Tools for Business", "AI tools for business"),
}

def write(rel, text):
    p = PUBLIC / rel
    p.parent.mkdir(parents=True, exist_ok=True)
    p.write_text(text, encoding="utf-8")

def schema(obj):
    return '<script type="application/ld+json">' + json.dumps(obj, separators=(",",":")) + "</script>"

def load_tools():
    tools_path = PUBLIC / "tools.json"
    tools = json.loads(tools_path.read_text(encoding="utf-8"))
    for i, t in enumerate(tools):
        t.setdefault("featured", i < 6)
        t.setdefault("trending", i < 8)
        t.setdefault("pending_review", False)
        t.setdefault("votes", None)
        t.setdefault("stars", None)
        t.setdefault("downloads", None)
        t.setdefault("launch_date", None)
        t.setdefault("last_checked", TODAY)
        t.setdefault("manual_cpa_url", t.get("cpa_url", ""))
        t.setdefault("manual_affiliate_url", t.get("affiliate_url", ""))
    tools_path.write_text(json.dumps(tools, indent=2), encoding="utf-8")

def append_css():
    css = PUBLIC / "css/style.css"
    extra = """
.trust-strip{display:flex;gap:10px;flex-wrap:wrap;margin:18px 0}.trust-pill{display:inline-flex;align-items:center;gap:6px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.06);border-radius:999px;padding:7px 10px;color:var(--soft);font-size:12px;font-weight:800}.verified{color:var(--lime)}.admin-grid{display:grid;grid-template-columns:280px 1fr;gap:16px}.admin-list{max-height:70vh;overflow:auto}.admin-list button{width:100%;text-align:left;margin:4px 0}.admin-form label{display:block;margin:10px 0 4px;color:var(--soft);font-weight:800}.admin-form input,.admin-form textarea,.admin-form select{width:100%;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.14);border-radius:8px;color:var(--text);padding:10px}.popular-searches a{margin:4px}.rank-box{counter-reset:rank}.rank-box li{margin:8px 0}.rank-box li::marker{color:var(--cyan);font-weight:900}.pending-badge{background:rgba(255,209,102,.12);border-color:rgba(255,209,102,.35);color:var(--amber)}@media(max-width:760px){.admin-grid{grid-template-columns:1fr}.admin-list{max-height:none}}
"""
    if ".trust-strip" not in css.read_text(encoding="utf-8"):
        css.write_text(css.read_text(encoding="utf-8") + "\n" + extra, encoding="utf-8")

def replace_app_js():
    write("js/app.js", r"""const AIMarsConfig=window.AIMARS_CONFIG||{};const CPA_LOCKER_URL=AIMarsConfig.CPA_LOCKER_URL||'PASTE_MY_ADBLUEMEDIA_LOCKER_LINK_HERE';const endpoint=AIMarsConfig.LEADS_ENDPOINT||'leads.php';async function postJSON(payload){try{await fetch(endpoint,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)})}catch(e){const key=payload.email?'aimars_leads_fallback':'aimars_events_fallback';const rows=JSON.parse(localStorage.getItem(key)||'[]');rows.push(payload);localStorage.setItem(key,JSON.stringify(rows))}}const track=(name,params={})=>{const payload={type:'event',event:name,page_url:location.href,date:new Date().toISOString(),user_agent:navigator.userAgent,...params};window.dataLayer=window.dataLayer||[];window.dataLayer.push(payload);if(window.gtag)gtag('event',name,params);postJSON(payload);console.info('tracking placeholder',name,params)};document.addEventListener('click',e=>{const a=e.target.closest('[data-track]');if(!a)return;track(a.dataset.track,{label:a.textContent.trim(),href:a.href||'',source_cta:a.dataset.sourceCta||a.textContent.trim()});if(a.dataset.track==='cpa_offer_click'&&CPA_LOCKER_URL&&!CPA_LOCKER_URL.includes('PASTE_MY')){e.preventDefault();window.open(CPA_LOCKER_URL,'_blank','noopener');setTimeout(()=>{location.href=a.dataset.afterCpa||'thank-you.html'},900)}});document.addEventListener('submit',async e=>{if(!e.target.matches('[data-email-form]'))return;e.preventDefault();const form=e.target;const email=form.querySelector('input[type=email]')?.value.trim();if(!email)return;await postJSON({type:'lead',email,page_url:location.href,source_cta:form.dataset.sourceCta||form.querySelector('button')?.textContent.trim()||'email form',date:new Date().toISOString(),user_agent:navigator.userAgent});track('email_capture_submit',{source_cta:form.dataset.sourceCta||'email form'});form.innerHTML='<p><strong>Thanks. Your free AI list access link is ready.</strong></p><a class="btn primary" href="free-ai-tools-access.html">Continue</a>'});document.querySelector('.nav-toggle')?.addEventListener('click',()=>document.querySelector('.links')?.classList.toggle('open'));let exitShown=false;document.addEventListener('mouseleave',e=>{if(e.clientY<=0&&!exitShown){exitShown=true;document.querySelector('#exitModal')?.classList.add('show')}});document.querySelectorAll('[data-close-modal]').forEach(b=>b.addEventListener('click',()=>b.closest('.modal').classList.remove('show')));document.querySelectorAll('[data-search]').forEach(input=>input.addEventListener('input',()=>{const q=input.value.toLowerCase();document.querySelectorAll('[data-tool-card]').forEach(c=>{c.style.display=c.textContent.toLowerCase().includes(q)?'flex':'none'})}));async function generateImage(){const provider=document.querySelector('#provider')?.value||'demo';const prompt=document.querySelector('#prompt')?.value||'';const loader=document.querySelector('.loader');loader?.classList.add('show');try{const r=await fetch('generate-image.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({provider,prompt,style:document.querySelector('#style')?.value,aspect_ratio:document.querySelector('#ratio')?.value})});const data=await r.json();document.querySelector('#genNote').textContent=data.message||'Demo images ready.'}catch(e){document.querySelector('#genNote').textContent='Demo mode: sample images generated. Add API keys in config.php to enable live image generation.'}finally{setTimeout(()=>loader?.classList.remove('show'),350)}}const gen=document.querySelector('#generateBtn');if(gen){gen.addEventListener('click',()=>{track('image_generate_click');generateImage()})}""")

def config_and_endpoints():
    write("config.js", 'window.AIMARS_CONFIG={CPA_LOCKER_URL:"PASTE_MY_ADBLUEMEDIA_LOCKER_LINK_HERE",LEADS_ENDPOINT:"leads.php"};\n')
    write("config.example.js", 'window.AIMARS_CONFIG={CPA_LOCKER_URL:"PASTE_MY_ADBLUEMEDIA_LOCKER_LINK_HERE",LEADS_ENDPOINT:"leads.php"};\n')
    write("config.php", """<?php
const CPA_LOCKER_URL = 'PASTE_MY_ADBLUEMEDIA_LOCKER_LINK_HERE';
const ADMIN_TOKEN = 'CHANGE_ME_ADMIN_TOKEN';
const OPENAI_API_KEY = '';
const STABILITY_API_KEY = '';
const HUGGINGFACE_API_KEY = '';
const REPLICATE_API_TOKEN = '';
?>""")
    write("leads.json", "[]\n")
    write("events.json", "[]\n")
    write("leads.php", """<?php
header('Content-Type: application/json');
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (!$data) { http_response_code(400); echo json_encode(['ok'=>false,'error'=>'Invalid JSON']); exit; }
$type = ($data['type'] ?? '') === 'lead' ? 'lead' : 'event';
$file = __DIR__ . ($type === 'lead' ? '/leads.json' : '/events.json');
$rows = file_exists($file) ? json_decode(file_get_contents($file), true) : [];
if (!is_array($rows)) $rows = [];
$row = [
  'date' => $data['date'] ?? gmdate('c'),
  'page_url' => $data['page_url'] ?? ($_SERVER['HTTP_REFERER'] ?? ''),
  'source_cta' => $data['source_cta'] ?? ($data['label'] ?? ''),
  'user_agent' => $data['user_agent'] ?? ($_SERVER['HTTP_USER_AGENT'] ?? '')
];
if ($type === 'lead') {
  $email = filter_var($data['email'] ?? '', FILTER_VALIDATE_EMAIL);
  if (!$email) { http_response_code(422); echo json_encode(['ok'=>false,'error'=>'Invalid email']); exit; }
  $row['email'] = $email;
} else {
  $row['event'] = preg_replace('/[^a-zA-Z0-9_\\-]/', '', $data['event'] ?? 'unknown_event');
  $row['href'] = $data['href'] ?? '';
}
$rows[] = $row;
file_put_contents($file, json_encode($rows, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
echo json_encode(['ok'=>true]);
?>""")
    write("admin.php", """<?php
require __DIR__ . '/config.php';
header('Content-Type: application/json');
$token = $_GET['token'] ?? ($_SERVER['HTTP_X_ADMIN_TOKEN'] ?? '');
if ($token !== ADMIN_TOKEN) { http_response_code(401); echo json_encode(['ok'=>false,'error'=>'Bad token']); exit; }
$file = __DIR__ . '/tools.json';
$tools = json_decode(file_get_contents($file), true);
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET') { echo json_encode(['ok'=>true,'tools'=>$tools]); exit; }
$data = json_decode(file_get_contents('php://input'), true);
if (!$data) { http_response_code(400); echo json_encode(['ok'=>false,'error'=>'Invalid JSON']); exit; }
$action = $data['action'] ?? 'save';
$tool = $data['tool'] ?? [];
$slug = preg_replace('/[^a-z0-9\\-]/', '', strtolower($tool['slug'] ?? ''));
if (!$slug) { http_response_code(422); echo json_encode(['ok'=>false,'error'=>'Missing slug']); exit; }
$found = false;
foreach ($tools as &$t) {
  if ($t['slug'] === $slug) {
    $manualCpa = $t['cpa_url'] ?? '';
    $manualAff = $t['affiliate_url'] ?? '';
    $t = array_merge($t, $tool);
    if (empty($tool['cpa_url'])) $t['cpa_url'] = $manualCpa;
    if (empty($tool['affiliate_url'])) $t['affiliate_url'] = $manualAff;
    if ($action === 'approve') $t['pending_review'] = false;
    $t['last_updated'] = gmdate('Y-m-d');
    $found = true;
    break;
  }
}
if (!$found) {
  $tool['slug'] = $slug;
  $tool['pending_review'] = $tool['pending_review'] ?? false;
  $tool['last_updated'] = gmdate('Y-m-d');
  $tool['last_verified'] = $tool['last_verified'] ?? gmdate('Y-m-d');
  $tools[] = $tool;
}
file_put_contents($file, json_encode($tools, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
echo json_encode(['ok'=>true]);
?>""")
    write("generate-image.php", """<?php
require __DIR__ . '/config.php';
header('Content-Type: application/json');
$data = json_decode(file_get_contents('php://input'), true) ?: [];
$provider = $data['provider'] ?? 'demo';
$hasKey = [
  'openai' => OPENAI_API_KEY !== '',
  'stability' => STABILITY_API_KEY !== '',
  'huggingface' => HUGGINGFACE_API_KEY !== '',
  'replicate' => REPLICATE_API_TOKEN !== ''
][$provider] ?? false;
if (!$hasKey) {
  echo json_encode(['ok'=>true,'demo'=>true,'images'=>['assets/sample-neon.svg','assets/sample-aurora.svg','assets/sample-circuit.svg'],'message'=>'Demo mode is active. Add API keys in config.php to enable '.$provider.' image generation.']);
  exit;
}
echo json_encode(['ok'=>false,'demo'=>false,'message'=>'Provider hook ready. Add the live API request for '.$provider.' here.']);
?>""")

def admin_page():
    write("admin.html", """<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex,nofollow"><title>AIMars Admin</title><link rel="stylesheet" href="css/style.css"></head><body><nav class="nav"><div class="wrap nav-inner"><a class="brand" href="index.html"><span class="mark">AI</span><span>AIMars Admin</span></a></div></nav><main class="wrap"><section><span class="eyebrow">JSON admin</span><h1>Manage AI tools</h1><p class="muted">Use your admin token from config.php. This edits tools.json through admin.php and preserves existing CPA and affiliate links unless you intentionally change them.</p><div class="form"><input id="token" placeholder="Admin token"><button class="btn primary" id="load">Load tools</button><button class="btn secondary" id="new">New tool</button></div><div class="admin-grid"><div class="card admin-list" id="list"></div><form class="card admin-form" id="form"><label>Name</label><input name="name"><label>Slug</label><input name="slug"><label>Category</label><input name="category"><label>Short description</label><textarea name="short_description"></textarea><label>Pricing</label><input name="pricing"><label>CPA link</label><input name="cpa_url"><label>Affiliate link</label><input name="affiliate_url"><label>Rating</label><input name="rating" type="number" step="0.1"><label>Pros, one per line</label><textarea name="pros"></textarea><label>Cons, one per line</label><textarea name="cons"></textarea><label>Last verified</label><input name="last_verified" type="date"><label><input name="featured" type="checkbox"> Featured</label><label><input name="trending" type="checkbox"> Trending</label><label><input name="pending_review" type="checkbox"> Pending review</label><button class="btn primary" type="submit">Save tool</button><button class="btn secondary" type="button" id="approve">Approve pending tool</button><p id="status" class="notice"></p></form></div></section></main><script>
let tools=[], current=null;const $=s=>document.querySelector(s);function token(){return $('#token').value.trim()}function render(){ $('#list').innerHTML=tools.map(t=>`<button class="btn ghost" data-slug="${t.slug}">${t.pending_review?'[pending] ':''}${t.name}</button>`).join('')}function fill(t){current=t;for(const el of $('#form').elements){if(!el.name)continue;let v=t[el.name];if(Array.isArray(v))v=v.join('\\n');if(el.type==='checkbox')el.checked=!!v;else el.value=v??''}}$('#load').onclick=async()=>{const r=await fetch('admin.php?token='+encodeURIComponent(token()));const d=await r.json();tools=d.tools||[];render()};$('#new').onclick=()=>fill({name:'',slug:'',category:'',short_description:'',pricing:'',rating:0,pros:[],cons:[],pending_review:false,featured:false,trending:false,last_verified:new Date().toISOString().slice(0,10)});$('#list').onclick=e=>{const b=e.target.closest('button');if(b)fill(tools.find(t=>t.slug===b.dataset.slug))};$('#form').onsubmit=async e=>{e.preventDefault();const fd=new FormData(e.target);const tool=Object.fromEntries(fd.entries());tool.pros=(tool.pros||'').split('\\n').filter(Boolean);tool.cons=(tool.cons||'').split('\\n').filter(Boolean);tool.rating=parseFloat(tool.rating||0);tool.featured=$('[name=featured]').checked;tool.trending=$('[name=trending]').checked;tool.pending_review=$('[name=pending_review]').checked;const r=await fetch('admin.php?token='+encodeURIComponent(token()),{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'save',tool})});$('#status').textContent=(await r.json()).ok?'Saved':'Save failed'};$('#approve').onclick=()=>{ $('[name=pending_review]').checked=false; $('#form').requestSubmit() };
</script></body></html>""")

def updater_script():
    scripts = ROOT / "scripts"
    scripts.mkdir(exist_ok=True)
    (scripts / "update_tools.py").write_text(r'''#!/usr/bin/env python3
import json, os, re, hashlib, urllib.request, urllib.parse, xml.etree.ElementTree as ET
from datetime import date
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
TOOLS_PATH = ROOT / "public" / "tools.json"
TODAY = date.today().isoformat()

RSS_FEEDS = [
  "https://openai.com/news/rss.xml",
  "https://www.anthropic.com/news/rss.xml",
  "https://blog.google/technology/ai/rss/",
  "https://huggingface.co/blog/feed.xml",
]

def get(url, headers=None):
    req = urllib.request.Request(url, headers=headers or {"User-Agent":"AIMarsBot/1.0"})
    with urllib.request.urlopen(req, timeout=25) as r:
        return r.read().decode("utf-8", "replace")

def slugify(s):
    return re.sub(r"[^a-z0-9]+", "-", s.lower()).strip("-")[:80]

def summary(text):
    text = re.sub(r"<[^>]+>", " ", text or "")
    text = re.sub(r"\s+", " ", text).strip()
    if len(text) > 180: text = text[:177].rsplit(" ",1)[0] + "..."
    return text or "Public metadata imported for editorial review."

def load():
    return json.loads(TOOLS_PATH.read_text(encoding="utf-8"))

def save(tools):
    TOOLS_PATH.write_text(json.dumps(tools, indent=2, ensure_ascii=False), encoding="utf-8")

def base_tool(name, slug, category, desc, url, source, **extra):
    return {
      "name": name, "slug": slug, "category": category,
      "short_description": summary(desc),
      "long_description": summary(desc),
      "features": extra.pop("features", []),
      "pricing": "Unknown", "free_plan": "Unknown",
      "official_url": url, "cpa_url": "", "affiliate_url": "",
      "rating": 0, "pros": [], "cons": [], "best_for": category,
      "alternatives": [], "image": "assets/sample-circuit.svg",
      "source": source, "pending_review": True,
      "last_verified": TODAY, "last_updated": TODAY, **extra
    }

def merge(existing, incoming):
    by_slug = {t["slug"]: t for t in existing}
    changed = False
    for item in incoming:
        old = by_slug.get(item["slug"])
        if old:
            for protected in ("cpa_url","affiliate_url","manual_cpa_url","manual_affiliate_url"):
                if old.get(protected): item[protected] = old[protected]
            merged = {**old, **{k:v for k,v in item.items() if v not in ("", None, [])}}
            if merged != old:
                merged["last_updated"] = TODAY
                by_slug[item["slug"]] = merged
                changed = True
        else:
            existing.append(item)
            by_slug[item["slug"]] = item
            changed = True
    return existing, changed

def product_hunt():
    token = os.getenv("PRODUCT_HUNT_TOKEN")
    if not token: return []
    query = '{"query":"query { posts(first: 20, topic: \\"artificial-intelligence\\") { edges { node { name tagline url votesCount createdAt topics { edges { node { name } } } } } } }"}'
    data = json.loads(get("https://api.producthunt.com/v2/api/graphql", {"Authorization":"Bearer "+token, "Content-Type":"application/json", "User-Agent":"AIMarsBot/1.0"}))
    out = []
    for edge in data["data"]["posts"]["edges"]:
        n = edge["node"]; slug = "ph-" + slugify(n["name"])
        topics = [x["node"]["name"] for x in n.get("topics",{}).get("edges",[])]
        out.append(base_tool(n["name"], slug, "Product Hunt launch", n.get("tagline"), n.get("url"), "Product Hunt API", votes=n.get("votesCount"), launch_date=(n.get("createdAt") or "")[:10], tags=topics))
    return out

def huggingface():
    out = []
    for kind, url in {
        "Hugging Face model":"https://huggingface.co/api/models?sort=likes&direction=-1&limit=20",
        "Hugging Face Space":"https://huggingface.co/api/spaces?sort=likes&direction=-1&limit=20",
        "Hugging Face dataset":"https://huggingface.co/api/datasets?sort=likes&direction=-1&limit=10",
    }.items():
        try: data = json.loads(get(url))
        except Exception: continue
        for n in data:
            name = n.get("modelId") or n.get("id") or n.get("name")
            if not name: continue
            out.append(base_tool(name, "hf-" + slugify(name), kind, "Trending public Hugging Face metadata for editorial review.", "https://huggingface.co/" + name, "Hugging Face API", tags=n.get("tags",[]), likes=n.get("likes"), downloads=n.get("downloads"), last_modified=(n.get("lastModified") or "")[:10]))
    return out

def github():
    q = urllib.parse.quote('topic:ai topic:artificial-intelligence stars:>500')
    headers = {"User-Agent":"AIMarsBot/1.0"}
    if os.getenv("GITHUB_TOKEN"): headers["Authorization"] = "Bearer " + os.getenv("GITHUB_TOKEN")
    data = json.loads(get(f"https://api.github.com/search/repositories?q={q}&sort=stars&order=desc&per_page=30", headers))
    out = []
    for r in data.get("items", []):
        out.append(base_tool(r["name"], "gh-" + slugify(r["full_name"]), "Open-source AI tool", r.get("description"), r.get("homepage") or r.get("html_url"), "GitHub API", repo_url=r.get("html_url"), stars=r.get("stargazers_count"), forks=r.get("forks_count"), language=r.get("language"), last_modified=(r.get("updated_at") or "")[:10]))
    return out

def rss():
    out = []
    for feed in RSS_FEEDS:
        try: root = ET.fromstring(get(feed))
        except Exception: continue
        for item in root.findall(".//item")[:8]:
            title = item.findtext("title") or "AI update"
            link = item.findtext("link") or feed
            desc = item.findtext("description") or title
            slug = "rss-" + hashlib.sha1(link.encode()).hexdigest()[:12]
            out.append(base_tool(title, slug, "AI news metadata", desc, link, "RSS feed", launch_date=(item.findtext("pubDate") or "")[:16]))
    return out

def main():
    existing = load()
    incoming = product_hunt() + huggingface() + github() + rss()
    merged, changed = merge(existing, incoming)
    if changed: save(merged)
    print(json.dumps({"fetched": len(incoming), "changed": changed, "tools": len(merged)}, indent=2))

if __name__ == "__main__":
    main()
''', encoding="utf-8")

def inject_config_script(s, path):
    prefix = "../" if "/" in path else ""
    if "config.js" not in s:
        s = s.replace('<script src="'+prefix+'js/app.js" defer></script>', f'<script src="{prefix}config.js"></script><script src="{prefix}js/app.js" defer></script>')
    return s

def trust_sections():
    trust = f"""<section><div class="wrap"><div class="trust-strip"><span class="trust-pill">Updated daily</span><span class="trust-pill verified">Tool verified</span><span class="trust-pill">Last checked: {TODAY}</span><span class="trust-pill">Sponsored links disclosed</span></div><div class="split"><div class="card"><h2>Why trust AIMars?</h2><p class="muted">AIMars separates editorial recommendations from monetization. We track pricing, free plan limits, product status, public launch metadata, GitHub activity, Hugging Face signals, and official company updates. Sponsored and CPA links are allowed, but they do not replace the comparison table, direct answer block, or the pros and cons that help readers choose quickly.</p></div><div class="card"><h2>How we rank tools</h2><ol class="list rank-box"><li>Usefulness of the free plan or trial.</li><li>Quality of output for the target workflow.</li><li>Fresh public signals such as updates, stars, likes, downloads, votes, and launch recency.</li><li>Clear pricing, official documentation, and trustworthy product pages.</li><li>Manual editorial review before fetched tools become featured.</li></ol></div></div><div class="popular-searches"><h2>Popular searches</h2><a class="btn ghost" href="best-free-ai-tools.html">free AI tools</a><a class="btn ghost" href="chatgpt-alternatives.html">ChatGPT alternatives</a><a class="btn ghost" href="best-ai-image-generators.html">AI image generator</a><a class="btn ghost" href="best-ai-tools-for-coding.html">AI coding tools</a><a class="btn ghost" href="ai-tools-for-business.html">AI tools for business</a></div></div></section>"""
    for f in PUBLIC.glob("*.html"):
        s = f.read_text(encoding="utf-8")
        s = inject_config_script(s, f.name)
        if "Why trust AIMars?" not in s:
            s = s.replace("</main>", trust + "</main>", 1)
        s = s.replace('href="free-ai-tools-access.html" data-track="cpa_offer_click"', 'href="thank-you.html" data-track="cpa_offer_click" data-after-cpa="thank-you.html"')
        f.write_text(s, encoding="utf-8")
    for f in (PUBLIC / "tools").glob("*.html"):
        s = f.read_text(encoding="utf-8")
        s = inject_config_script(s, "tools/" + f.name)
        if "Tool verified" not in s:
            s = s.replace('<div class="answer">', f'<div class="trust-strip"><span class="trust-pill verified">Tool verified</span><span class="trust-pill">Last checked: {TODAY}</span><span class="trust-pill">Sponsored links disclosed</span></div><div class="answer">', 1)
        f.write_text(s, encoding="utf-8")

def expand_key_pages():
    content = {
        "free AI tools": ["Free AI tools are strongest when they solve one workflow clearly instead of pretending to replace a full software stack. The best choices usually let you test real output quality before you pay, show clear limits, and make it easy to export or continue work elsewhere.", "For writing and research, start with a general assistant and a cited search tool. For creative work, combine a design tool with a specialist image or video model. For coding, use an editor assistant for repository context and a chat assistant for explanations.", "The practical rule is simple: use free plans for exploration, recurring lightweight tasks, and early workflow design. Upgrade only when you know the tool saves time every week or unlocks a feature that affects revenue, grades, production speed, or client delivery."],
        "AI image generators": ["AI image generators should be judged by prompt control, repeatability, commercial safety, editing workflow, and export quality. A beautiful first image matters, but teams also need consistent brand direction, reliable aspect ratios, and predictable licensing.", "Midjourney is often a strong creative choice when visual style matters most. DALL-E is convenient when the prompt is part of a chat workflow. Adobe Firefly and Canva AI are useful for marketers who need fast design assets inside broader creative systems.", "Before choosing an image generator, test the exact content you plan to create: product shots, thumbnails, ad concepts, editorial art, or social posts. Each tool has different strengths, and the best result often comes from pairing generation with a human design pass."],
        "ChatGPT alternatives": ["A ChatGPT alternative should not be chosen only because it is different. The better question is what job ChatGPT is not handling well enough: long document analysis, cited research, coding inside a repository, image quality, privacy controls, or cost.", "Claude is a strong alternative for careful writing and analysis. Gemini fits users already deep in Google workflows. Perplexity is useful when citations and current web research matter. Cursor and GitHub Copilot are better choices when the work happens inside code.", "Many teams keep ChatGPT as a general assistant and add alternatives by workflow. That prevents tool switching from becoming chaotic while still giving specialists the right product for research, coding, images, or business automation."],
        "AI coding tools": ["AI coding tools work best when they understand the project context and when developers review every change. Autocomplete is useful, but the real productivity gain comes from codebase-aware chat, refactoring help, test generation, and explaining unfamiliar files.", "Cursor is suited to developers who want an AI-first editor experience. GitHub Copilot is strong for broad IDE support and enterprise adoption. Windsurf offers a friendly route for budget-conscious builders. Claude and ChatGPT remain useful for architecture thinking and debugging explanations.", "The safest workflow is to ask the tool for a small change, inspect the diff, run tests, and then expand the task. Teams should avoid pasting secrets into prompts, should keep generated code under review, and should measure whether AI assistance improves delivery quality, not just typing speed."],
        "AI tools for students": ["Students should choose AI tools that improve learning rather than replace it. The most useful tools explain concepts, summarize source material, create study guides, help outline essays, and make revision less painful while still leaving the student in control.", "NotebookLM is excellent when the student has PDFs, lecture notes, or research sources. Perplexity helps with cited discovery. ChatGPT, Claude, and Gemini can tutor, quiz, rewrite, and explain. Canva AI helps turn research into presentations and visual assignments.", "A good student workflow starts with sources, asks for explanations, checks claims, and then writes in the student's own voice. AI output should be treated as a draft or study partner, not a final answer to submit without review."],
        "AI tools for business": ["Business AI tools should be evaluated by measurable workflow impact: faster research, better customer responses, lower content production time, cleaner internal documentation, stronger sales enablement, or more reliable automation.", "ChatGPT and Claude are flexible assistants for operations, strategy, and content. Perplexity supports market research. Canva AI and Adobe Firefly help marketing teams create assets quickly. Zapier AI connects lead capture, CRM actions, notifications, and repetitive business processes.", "The best business rollout starts with one high-frequency workflow, clear data rules, and a review process. Avoid adopting tools only because they are trending. Choose the tool that shortens a real task and can be used safely by the team."],
    }
    for fname, (_, topic) in KEY_PAGES.items():
        p = PUBLIC / fname
        s = p.read_text(encoding="utf-8")
        if "Editorial buying guide" in s:
            continue
        paras = content[topic]
        expanded = f"""<section><div class="wrap"><span class="eyebrow">Last updated {TODAY}</span><h2>Editorial buying guide</h2>{''.join(f'<p class="muted">{x}</p>' for x in paras)}<div class="split"><div class="card"><h2>Best for</h2><p class="muted">Use this page when you need a shortlist, a quick comparison table, and enough context to decide which tool deserves a trial. AIMars favors tools with transparent pricing, useful free access, recent product activity, and a clear official URL.</p></div><div class="card"><h2>What to check before clicking</h2><ul class="list"><li>Whether the free plan is enough for your expected usage.</li><li>Whether outputs can be exported or reused commercially.</li><li>Whether the product has recent updates and reliable support.</li><li>Whether a sponsored or affiliate link changes the destination.</li></ul></div></div><p class="muted">This guide intentionally uses short summaries rather than copied product copy or full articles. Public metadata can inform the ranking, but tools marked pending review should be approved manually before they become featured recommendations.</p></div></section>"""
        s = s.replace("<section><div class=\"wrap\"><h2>Comparison table</h2>", expanded + "<section><div class=\"wrap\"><h2>Comparison table</h2>", 1)
        if '"@type":"BreadcrumbList"' not in s:
            crumb = schema({"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://aimars.online/"},{"@type":"ListItem","position":2,"name":KEY_PAGES[fname][0],"item":"https://aimars.online/"+fname}]})
            s = s.replace("</head>", crumb + "</head>", 1)
        p.write_text(s, encoding="utf-8")

def image_generator_upgrade():
    p = PUBLIC / "ai-image-generator.html"
    s = p.read_text(encoding="utf-8")
    if 'id="provider"' not in s:
        s = s.replace('<div class="split"><select><option>Cyberpunk</option><option>Photoreal</option><option>Product render</option><option>Anime</option></select><select><option>1:1 square</option><option>16:9 landscape</option><option>9:16 vertical</option></select></div>', '<div class="split"><select id="provider"><option value="demo">Demo mode</option><option value="openai">OpenAI Images</option><option value="stability">Stability AI</option><option value="huggingface">Hugging Face</option><option value="replicate">Replicate</option></select><select id="style"><option>Cyberpunk</option><option>Photoreal</option><option>Product render</option><option>Anime</option></select></div><div class="split"><select id="ratio"><option>1:1 square</option><option>16:9 landscape</option><option>9:16 vertical</option></select><a class="btn ghost" href="config.example.js">API config example</a></div>')
        s = s.replace("Prepared for OpenAI Images API, Stability AI, Hugging Face, and Replicate.", "Prepared for OpenAI Images API, Stability AI, Hugging Face, and Replicate via generate-image.php.")
    p.write_text(s, encoding="utf-8")

def final_zip():
    if OUT.exists():
        OUT.unlink()
    with zipfile.ZipFile(OUT, "w", zipfile.ZIP_DEFLATED) as z:
        for f in PUBLIC.rglob("*"):
            z.write(f, f.relative_to(PUBLIC))

def main():
    load_tools()
    append_css()
    replace_app_js()
    config_and_endpoints()
    admin_page()
    updater_script()
    trust_sections()
    expand_key_pages()
    image_generator_upgrade()
    final_zip()
    print(f"Phase 2 upgrade complete: {OUT}")

if __name__ == "__main__":
    main()
