"""
Capture screenshots of every view, modal, and dialog in the TrainUs app.

The app is driven in English by default (use --lang fr for French screenshots)
with a seeded database so each screenshot shows meaningful content rather
than empty states.

Intended for visual investigations and documentation figures — not a test.

Prerequisites:
    - Playwright for Python installed (see `tests/requirements.txt`)
    - Playwright browsers installed: `python -m playwright install firefox chromium`
    - Vite dev server running: `npm run dev -- --port 5199 --strictPort`

Usage (from project root):

    python scripts/screenshots/all_pages.py
    python scripts/screenshots/all_pages.py --browser chromium
    python scripts/screenshots/all_pages.py --lang fr
    python scripts/screenshots/all_pages.py --out-dir /tmp/screenshots
    python scripts/screenshots/all_pages.py --no-mobile

Options:

    --url URL        App URL (default: http://localhost:5199)
    --browser NAME   firefox | chromium | webkit (default: firefox)
    --lang LANG      en | fr (default: en)
    --out-dir PATH   Output directory root (default: tmp/screenshots)
    --no-mobile      Skip the mobile (390×844) pass

Exit code: 0 on success, 1 on failure.
"""

from __future__ import annotations

import argparse
import os
import sys
import time
import traceback
from pathlib import Path

from playwright.sync_api import Page, sync_playwright


# ---------------------------------------------------------------------------
# Seed helpers
# ---------------------------------------------------------------------------

_SEED_SCRIPT = """
async () => {
    const uuid = await window.__repos.settings.get('user_uuid');

    // ── exercises ──────────────────────────────────────────────────────────
    const ex1 = await window.__repos.exercises.create({
        title: 'Push-ups',
        description: '**Classic** bodyweight push-up.\\n\\n- Keep your back straight\\n- Full range of motion',
        asymmetric: false, alternate: false, image_urls: [], video_urls: [],
        default_reps: 15, default_weight: 0, created_by: uuid
    });
    const ex2 = await window.__repos.exercises.create({
        title: 'Squats',
        description: 'Bodyweight squat — great for legs and glutes.',
        asymmetric: false, alternate: false, image_urls: [], video_urls: [],
        default_reps: 20, default_weight: 0, created_by: uuid
    });
    const ex3 = await window.__repos.exercises.create({
        title: 'Deadlift',
        description: 'Barbell deadlift — compound posterior-chain movement.',
        asymmetric: false, alternate: false, image_urls: [], video_urls: [],
        default_reps: 5, default_weight: 100, created_by: uuid
    });
    const ex4 = await window.__repos.exercises.create({
        title: 'Single-leg Romanian Deadlift',
        description: 'Balance and hamstring exercise.\\n\\n*Asymmetric — alternate sides.*',
        asymmetric: true, alternate: true, image_urls: [], video_urls: [],
        default_reps: 10, default_weight: 20, created_by: uuid
    });

    // ── regular combo ──────────────────────────────────────────────────────
    const comboId = await window.__repos.combos.create({
        title: 'Morning HIIT',
        description: 'Quick high-intensity warm-up combo.',
        type: 'NONE', timer_config: null, created_by: uuid
    });
    await window.__repos.combos.setExercises(comboId, [
        { exerciseId: ex1, position: 0, defaultReps: 15, defaultWeight: 0 },
        { exerciseId: ex2, position: 1, defaultReps: 20, defaultWeight: 0 }
    ]);

    // ── AMRAP combo ────────────────────────────────────────────────────────
    const amrapComboId = await window.__repos.combos.create({
        title: 'AMRAP Blast',
        description: '3-minute AMRAP: push-ups and squats. As many rounds as possible!',
        type: 'AMRAP', timer_config: {}, created_by: uuid
    });
    await window.__repos.combos.setExercises(amrapComboId, [
        { exerciseId: ex1, position: 0, defaultReps: 10, defaultWeight: 0 },
        { exerciseId: ex2, position: 1, defaultReps: 15, defaultWeight: 0 }
    ]);

    // ── regular session ────────────────────────────────────────────────────
    const sessId = await window.__repos.sessions.create({
        title: 'Full-Body Workout A',
        description: 'A balanced full-body session combining strength and cardio.',
        created_by: uuid
    });
    await window.__repos.sessions.setItems(sessId, [
        { item_id: ex3, item_type: 'exercise', repetitions: 5, weight: 100.0 },
        { item_id: comboId, item_type: 'combo', repetitions: 3, weight: 0.0 }
    ]);

    // ── AMRAP session (repetitions = minutes for AMRAP type) ───────────────
    const amrapSessId = await window.__repos.sessions.create({
        title: 'AMRAP Cardio',
        description: '3-minute AMRAP push-up/squat circuit.',
        created_by: uuid
    });
    await window.__repos.sessions.setItems(amrapSessId, [
        { item_id: amrapComboId, item_type: 'combo', repetitions: 3, weight: 0.0 }
    ]);

    // ── collection ─────────────────────────────────────────────────────────
    const colId = await window.__repos.collections.create({
        label: 'Week 1 Plan', created_by: uuid
    });
    await window.__repos.collections.setSessions(colId, [sessId]);

    // ── execution log so the home page shows history ───────────────────────
    const now = Date.now();
    const logId = await window.__repos.executionLogs.create({
        session_id: sessId,
        started_at: new Date(now - 3600000).toISOString(),
        finished_at: new Date(now - 3000000).toISOString()
    });
    await window.__repos.executionLogs.addItem({
        log_id: logId, exercise_id: ex3, combo_id: null,
        round_number: 1, reps_done: 5, weight_used: 100, side: null,
        completed: true, timestamp: new Date(now - 3500000).toISOString()
    });
    await window.__repos.executionLogs.addItem({
        log_id: logId, exercise_id: ex1, combo_id: comboId,
        round_number: 1, reps_done: 15, weight_used: 0, side: null,
        completed: true, timestamp: new Date(now - 3400000).toISOString()
    });

    return { ex1, ex2, ex3, ex4, comboId, amrapComboId, sessId, amrapSessId, colId, logId };
}
"""

_COUNT_SCRIPT = """
async () => (await window.__repos.exercises.getAll()).length
"""


def wait_for_db(page: Page, url: str) -> None:
    page.goto(url)
    page.wait_for_function(
        "document.documentElement.getAttribute('data-db-ready') === 'true'",
        timeout=15000,
    )


def ensure_language(page: Page, lang: str) -> None:
    current = page.evaluate("async () => window.__repos.settings.get('language')")
    if current != lang:
        page.evaluate(f"async () => window.__repos.settings.set('language', '{lang}')")
        page.reload()
        page.wait_for_function(
            "document.documentElement.getAttribute('data-db-ready') === 'true'",
            timeout=15000,
        )


def has_data(page: Page) -> bool:
    return page.evaluate(_COUNT_SCRIPT) > 0


# ---------------------------------------------------------------------------
# Screenshot helpers
# ---------------------------------------------------------------------------

def shot(page: Page, out_dir: Path, name: str, full_page: bool = True) -> None:
    time.sleep(0.4)
    path = out_dir / f"{name}.png"
    page.screenshot(path=str(path), full_page=full_page)
    print(f"  ✓  {path.name}")


def goto(page: Page, url: str, route: str, wait_selector: str | None = None) -> None:
    page.goto(f"{url}/#/{route}", wait_until="domcontentloaded")
    if wait_selector:
        page.wait_for_selector(wait_selector, timeout=8000)
    else:
        time.sleep(0.3)


# ---------------------------------------------------------------------------
# Main capture flow
# ---------------------------------------------------------------------------

def capture_all(page: Page, url: str, out_dir: Path, lang: str) -> None:
    wait_for_db(page, url)
    ensure_language(page, lang)

    if not has_data(page):
        print("  Seeding database…")
        ids = page.evaluate(_SEED_SCRIPT)
        print("  Seed complete.")
    else:
        raise RuntimeError("DB already has data — start with a fresh browser context")

    sess_id      = ids['sessId']
    amrap_sess_id = ids['amrapSessId']
    col_id       = ids['colId']

    # ── home (shows recent execution history after seed) ──────────────────
    goto(page, url, "home")
    shot(page, out_dir, "home")

    # ── exercises ─────────────────────────────────────────────────────────
    goto(page, url, "exercises", '[data-testid="exercise-new-btn"]')
    shot(page, out_dir, "exercises")

    exercises = page.evaluate("async () => window.__repos.exercises.getAll()")
    ex_id = exercises[0]["id"]

    goto(page, url, f"exercises/{ex_id}", '[data-testid="exercise-detail-title"]')
    shot(page, out_dir, "exercise-detail")

    goto(page, url, f"exercises/{ex_id}/edit", '[data-testid="exercise-title"]')
    shot(page, out_dir, "exercise-edit")

    goto(page, url, "exercises/new", '[data-testid="exercise-title"]')
    shot(page, out_dir, "exercise-new")

    # ── combos ────────────────────────────────────────────────────────────
    goto(page, url, "combos", '[data-testid="combo-new-btn"]')
    shot(page, out_dir, "combos")

    combos = page.evaluate("async () => window.__repos.combos.getAll()")
    combo_id = combos[0]["id"]

    goto(page, url, f"combos/{combo_id}", '[data-testid="combo-detail-title"]')
    shot(page, out_dir, "combo-detail")

    goto(page, url, f"combos/{combo_id}/edit", '[data-testid="combo-title"]')
    shot(page, out_dir, "combo-edit")

    goto(page, url, "combos/new", '[data-testid="combo-title"]')
    shot(page, out_dir, "combo-new")

    # ── sessions ──────────────────────────────────────────────────────────
    goto(page, url, "sessions", '[data-testid="session-new-btn"]')
    shot(page, out_dir, "sessions")

    goto(page, url, f"sessions/{sess_id}", '[data-testid="session-detail-title"]')
    shot(page, out_dir, "session-detail")

    goto(page, url, f"sessions/{sess_id}/edit", '[data-testid="session-title"]')
    shot(page, out_dir, "session-edit")

    goto(page, url, "sessions/new", '[data-testid="session-title"]')
    shot(page, out_dir, "session-new")

    # ── session execute — regular (progress bar + current step) ───────────
    goto(page, url, f"sessions/{sess_id}/execute", '[data-testid="execute-progress"]')
    shot(page, out_dir, "session-execute")

    # ── session execute — AMRAP timer running ─────────────────────────────
    # Navigate away first so Vue Router remounts SessionExecuteView (it reuses
    # the component when only params change, so onMounted/init() won't re-run
    # unless we force a different route in between).
    goto(page, url, "sessions", '[data-testid="session-new-btn"]')
    goto(page, url, f"sessions/{amrap_sess_id}/execute", '[data-testid="execute-timer"]')
    shot(page, out_dir, "session-execute-amrap")

    # ── collections ───────────────────────────────────────────────────────
    goto(page, url, "collections", '[data-testid="collection-new-btn"]')
    shot(page, out_dir, "collections")

    goto(page, url, f"collections/{col_id}", '[data-testid="collection-detail-title"]')
    shot(page, out_dir, "collection-detail")

    goto(page, url, f"collections/{col_id}/edit", '[data-testid="collection-label"]')
    shot(page, out_dir, "collection-edit")

    goto(page, url, "collections/new", '[data-testid="collection-label"]')
    shot(page, out_dir, "collection-new")

    # ── stats ─────────────────────────────────────────────────────────────
    goto(page, url, "stats", '[data-testid="stats-date-range"]')
    shot(page, out_dir, "stats")

    # ── settings — viewport-height slices (page is tall) ──────────────────
    goto(page, url, "settings", '[data-testid="settings-language"]')
    page.evaluate("window.scrollTo(0, 0)")
    shot(page, out_dir, "settings-top", full_page=False)
    page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
    time.sleep(0.2)
    shot(page, out_dir, "settings-bottom", full_page=False)

    # ── about dialog ──────────────────────────────────────────────────────
    page.evaluate("window.scrollTo(0, 0)")
    page.click('[data-testid="settings-about"]')
    page.wait_for_selector('[data-testid="modal-card"]')
    shot(page, out_dir, "about-dialog")

    # ── license dialog (stacked on about) ─────────────────────────────────
    page.click('[data-testid="about-license-btn"]')
    page.wait_for_selector('[data-testid="license-dialog-content"]')
    shot(page, out_dir, "license-dialog")

    close_btns = page.locator('[data-testid="modal-close"]')
    close_btns.last.click()
    close_btns.first.click()


# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------

def parse_args(argv: list[str]) -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument("--url", default=os.environ.get("APP_URL", "http://localhost:5199"))
    parser.add_argument("--browser", default="firefox", choices=["firefox", "chromium", "webkit"])
    parser.add_argument("--lang", default="en", choices=["en", "fr"])
    parser.add_argument("--out-dir", default=None)
    parser.add_argument("--no-mobile", action="store_true")
    return parser.parse_args(argv)


def default_out_dir(browser: str, lang: str, width: int, height: int) -> Path:
    project_root = Path(__file__).resolve().parents[2]
    prefix = browser if lang == "en" else f"{lang}-{browser}"
    return project_root / "tmp" / "screenshots" / f"{prefix}-{width}x{height}"


def main(argv: list[str] | None = None) -> int:
    args = parse_args(argv or sys.argv[1:])

    passes = [(1920, 1080)]
    if not args.no_mobile:
        passes.append((390, 844))

    with sync_playwright() as p:
        for (w, h) in passes:
            out_dir = (
                Path(args.out_dir) / f"{args.browser}-{w}x{h}"
                if args.out_dir
                else default_out_dir(args.browser, args.lang, w, h)
            )
            out_dir.mkdir(parents=True, exist_ok=True)

            print(f"\n── {args.browser}  {w}×{h}  →  {out_dir}")

            browser_type = getattr(p, args.browser)
            browser = browser_type.launch(headless=True)
            try:
                context = browser.new_context(viewport={"width": w, "height": h})
                page = context.new_page()
                page.on("dialog", lambda dialog: dialog.accept())
                capture_all(page, args.url, out_dir, args.lang)
                count = len(list(out_dir.glob("*.png")))
                print(f"\n  Done — {count} screenshots saved")
            except Exception as exc:
                print(f"\n  ERROR: {exc}", file=sys.stderr)
                traceback.print_exc()
                return 1
            finally:
                browser.close()

    return 0


if __name__ == "__main__":
    sys.exit(main())
