"""
Visual regression tests using Playwright.

Takes screenshots of key pages and verifies styling invariants
(heading colors, backgrounds, nav layout, logo presence).

Skipped by default (requires `playwright install chromium`).
Run explicitly with:
    uv run pytest -m visual tests/test_visual.py -v

Screenshots are saved to tests/screenshots/ for manual review.
"""
import re
from pathlib import Path

import pytest
from playwright.sync_api import expect

pytestmark = pytest.mark.visual

SCREENSHOTS_DIR = Path(__file__).parent / "screenshots"
SCREENSHOTS_DIR.mkdir(exist_ok=True)


def parse_rgb(color_str):
    """Parse rgb(r,g,b) or rgba(r,g,b,a) into [r, g, b]."""
    nums = re.findall(r"[\d.]+", color_str)
    return [int(float(x)) for x in nums[:3]]


@pytest.fixture
def page(browser):
    """Desktop page with dark OS preference to catch theme leaks."""
    pg = browser.new_page(
        viewport={"width": 1280, "height": 900},
        color_scheme="dark",
    )
    yield pg
    pg.close()


# ── Landing page tests ──────────────────────────────────────


def test_landing_light_background(live_server, page):
    """Verify the page has a light background, not dark."""
    page.goto(live_server)
    page.wait_for_load_state("networkidle")

    bg_color = page.evaluate("""
        (() => {
            const html_bg = getComputedStyle(document.documentElement).backgroundColor;
            const body_bg = getComputedStyle(document.body).backgroundColor;
            if (body_bg && !body_bg.includes('0, 0, 0, 0')) return body_bg;
            return html_bg;
        })()
    """)
    rgb = parse_rgb(bg_color)
    brightness = sum(rgb) / 3
    assert brightness > 200, f"Background too dark: {bg_color} (brightness={brightness})"


def test_landing_heading_colors(live_server, page):
    """Verify headings are readable (dark on light, white on dark hero)."""
    page.goto(live_server)
    page.wait_for_load_state("networkidle")

    # H1 is intentionally white (#fff) on the dark hero — verify it has a color set.
    h1_color = page.evaluate(
        "getComputedStyle(document.querySelector('h1')).color"
    )
    rgb = parse_rgb(h1_color)
    assert rgb != [0, 0, 0] or sum(rgb) > 0, f"H1 appears unset: {h1_color}"

    # H2s on light sections should be dark; skip h2s inside dark containers.
    h2_data = page.evaluate("""
        Array.from(document.querySelectorAll('h2')).map(el => {
            const inDark = el.closest('.hero-dark, .cta-card') !== null;
            return {color: getComputedStyle(el).color, inDark};
        })
    """)
    for i, item in enumerate(h2_data):
        if item["inDark"]:
            continue
        rgb = parse_rgb(item["color"])
        brightness = sum(rgb) / 3
        assert brightness < 100, f"H2[{i}] too light: {item['color']} (brightness={brightness})"

    # H3s on light sections should be dark
    h3_data = page.evaluate("""
        Array.from(document.querySelectorAll('h3')).map(el => {
            const inDark = el.closest('.hero-dark, .cta-card') !== null;
            return {color: getComputedStyle(el).color, inDark};
        })
    """)
    for i, item in enumerate(h3_data):
        if item["inDark"]:
            continue
        rgb = parse_rgb(item["color"])
        brightness = sum(rgb) / 3
        assert brightness < 150, f"H3[{i}] too light: {item['color']} (brightness={brightness})"


def test_landing_logo_present(live_server, page):
    """Verify the nav logo link is visible."""
    page.goto(live_server)
    page.wait_for_load_state("networkidle")

    logo = page.locator("nav a.nav-logo")
    expect(logo).to_be_visible()

    text = logo.inner_text()
    assert len(text) > 0, "Nav logo link has no text"


def test_landing_nav_no_overlap(live_server, page):
    """Verify nav items don't overlap each other."""
    page.goto(live_server)
    page.wait_for_load_state("networkidle")

    boxes = page.evaluate("""
        (() => {
            const navDiv = document.querySelector('nav > div');
            if (!navDiv) return [];
            const items = navDiv.children;
            return Array.from(items)
                .map(el => {
                    const r = el.getBoundingClientRect();
                    return {top: r.top, bottom: r.bottom, left: r.left, right: r.right, width: r.width};
                })
                .filter(r => r.width > 0);
        })()
    """)
    for i in range(len(boxes) - 1):
        a, b = boxes[i], boxes[i + 1]
        h_overlap = a["right"] - b["left"]
        assert h_overlap < 10, (
            f"Nav items {i} and {i+1} overlap horizontally by {h_overlap:.0f}px"
        )


def test_landing_cards_have_colored_borders(live_server, page):
    """Verify landing page cards have a visible left border accent."""
    page.goto(live_server)
    page.wait_for_load_state("networkidle")

    border_widths = page.evaluate("""
        Array.from(document.querySelectorAll('.card')).map(
            el => parseFloat(getComputedStyle(el).borderLeftWidth)
        )
    """)
    assert len(border_widths) > 0, "No .card elements found"
    cards_with_accent = [w for w in border_widths if w >= 4]
    assert len(cards_with_accent) >= 6, (
        f"Expected >=6 cards with 4px left border, got {len(cards_with_accent)}"
    )


def test_landing_logo_links_to_landing(live_server, page):
    """Verify nav-logo links to the landing page (language-prefixed or root)."""
    page.goto(live_server)
    page.wait_for_load_state("networkidle")

    href = page.locator("nav a.nav-logo").get_attribute("href")
    assert href == "/" or (href.startswith("/") and href.endswith("/")), (
        f"Nav logo href unexpected: {href}"
    )


def test_landing_teaser_light_theme(live_server, page):
    """Verify the ROI calc card has a white/light background."""
    page.goto(live_server)
    page.wait_for_load_state("networkidle")

    teaser_bg = page.evaluate(
        "getComputedStyle(document.querySelector('.roi-calc')).backgroundColor"
    )
    rgb = parse_rgb(teaser_bg)
    brightness = sum(rgb) / 3
    assert brightness > 240, f"ROI calc background too dark: {teaser_bg}"


def test_landing_no_dark_remnants(live_server, page):
    """Check that no major elements have dark backgrounds."""
    page.goto(live_server)
    page.wait_for_load_state("networkidle")

    dark_elements = page.evaluate("""
        (() => {
            const dark = [];
            const allowedClasses = ['hero-dark', 'cta-card'];
            const els = document.querySelectorAll('article, section, header, footer, main, div');
            for (const el of els) {
                const cls = el.className || '';
                if (allowedClasses.some(c => cls.includes(c))) continue;
                const bg = getComputedStyle(el).backgroundColor;
                if (bg === 'rgba(0, 0, 0, 0)' || bg === 'transparent') continue;
                const m = bg.match(/\\d+/g);
                if (m) {
                    const [r, g, b] = m.map(Number);
                    const brightness = (r + g + b) / 3;
                    if (brightness < 50) {
                        dark.push({tag: el.tagName, class: cls, bg, brightness});
                    }
                }
            }
            return dark;
        })()
    """)
    assert len(dark_elements) == 0, (
        f"Found {len(dark_elements)} unexpected dark-background elements: "
        f"{dark_elements[:3]}"
    )


# ── Screenshots (all pages in one pass) ─────────────────────


def test_capture_screenshots(live_server, browser):
    """Save reference screenshots for manual review (desktop + mobile + auth)."""
    desktop = browser.new_page(viewport={"width": 1280, "height": 900})
    mobile = browser.new_page(viewport={"width": 375, "height": 812})

    # Landing desktop
    desktop.goto(live_server)
    desktop.wait_for_load_state("networkidle")
    desktop.screenshot(path=str(SCREENSHOTS_DIR / "landing_full.png"), full_page=True)

    # Auth pages
    desktop.goto(f"{live_server}/auth/login")
    desktop.wait_for_load_state("networkidle")
    desktop.screenshot(path=str(SCREENSHOTS_DIR / "login.png"), full_page=True)

    desktop.goto(f"{live_server}/auth/signup")
    desktop.wait_for_load_state("networkidle")
    desktop.screenshot(path=str(SCREENSHOTS_DIR / "signup.png"), full_page=True)

    # Landing mobile
    mobile.goto(live_server)
    mobile.wait_for_load_state("networkidle")
    mobile.screenshot(path=str(SCREENSHOTS_DIR / "landing_mobile.png"), full_page=True)

    desktop.close()
    mobile.close()
