"""Local accounts → browser-issued HTTP keys → git push → merge request."""

from __future__ import annotations

import os
import shutil
import subprocess
from collections.abc import Iterator
from dataclasses import dataclass
from pathlib import Path

import pytest

from integration.harness import running_server

sync_api = pytest.importorskip("playwright.sync_api")

_AUTH_CONFIG_SRC = Path(__file__).with_name("local_auth.json")
_ALICE = "alice"
_ALICE_PASSWORD = "secretsecret"
_ALICE_EMAIL = "alice@example.com"
_BOB = "bob"
_BOB_PASSWORD = "secretsecret"
_BOB_EMAIL = "bob@example.com"
_KEY_ID = "laptop"
_PROJECT = "emptyproj"
_FEATURE = "feature"


@dataclass
class LocalAuthHarness:
    base: str
    tmp_path: Path
    bare: Path
    alice_secret: str = ""
    bob_secret: str = ""
    feature_tip: str = ""


def _install_auth_config(dest: Path) -> None:
    shutil.copyfile(_AUTH_CONFIG_SRC, dest)
    if os.name != "nt":
        dest.chmod(0o600)


def _run_git(repo_dir: Path, *args: str, git_config: tuple[str, ...] = ()) -> str:
    completed = subprocess.run(
        ["git", *git_config, *args],
        cwd=repo_dir,
        capture_output=True,
        text=True,
        check=True,
        env={**os.environ, "GIT_TERMINAL_PROMPT": "0"},
    )
    return completed.stdout.strip()


def _key_git_config(username: str, secret: str) -> tuple[str, ...]:
    helper = f"!f() {{ echo username={username}; echo password={secret}; }}; f"
    return ("-c", "credential.helper=", "-c", f"credential.helper={helper}")


def _login(page: sync_api.Page, base: str, username: str, password: str) -> None:
    page.goto(f"{base}login?next=/user", wait_until="domcontentloaded")
    page.fill("#login_username", username)
    page.fill("#login_password", password)
    page.get_by_role("button", name="Sign in").click()
    page.wait_for_url("**/user")
    sync_api.expect(page.get_by_role("heading", name="Account")).to_be_visible()


def _issue_key_on_page(page: sync_api.Page, key_id: str) -> str:
    page.fill("#api-key-id", key_id)
    page.get_by_role("button", name="Create key").click()
    page.locator("#api-key-secret-panel").wait_for(state="visible")
    secret = page.locator("#api-key-secret").input_value()
    assert secret, "expected a plaintext API key after Create key"
    return secret


@pytest.fixture(scope="module")
def harness(tmp_path_factory: pytest.TempPathFactory) -> Iterator[LocalAuthHarness]:
    tmp_path = tmp_path_factory.mktemp("local_auth_push")
    projects = tmp_path / "projects"
    projects.mkdir()
    bare = projects / _PROJECT
    subprocess.run(["git", "init", "--bare", "-b", "main", str(bare)], check=True, capture_output=True)

    auth_path = tmp_path / "auth.json"
    _install_auth_config(auth_path)

    with running_server(
        tmp_path=tmp_path,
        projects=projects,
        auth=True,
        auth_config_file=auth_path,
    ) as base:
        yield LocalAuthHarness(base=base, tmp_path=tmp_path, bare=bare)


@pytest.mark.playwright
def test_auth_setup(harness: LocalAuthHarness) -> None:
    with sync_api.sync_playwright() as playwright:
        browser = playwright.chromium.launch(headless=True)
        try:
            page = browser.new_page()
            _login(page, harness.base, _ALICE, _ALICE_PASSWORD)
            harness.alice_secret = _issue_key_on_page(page, _KEY_ID)

            page.goto(f"{harness.base}logout?next=/", wait_until="domcontentloaded")
            _login(page, harness.base, _BOB, _BOB_PASSWORD)
            harness.bob_secret = _issue_key_on_page(page, _KEY_ID)
        finally:
            browser.close()

    assert harness.alice_secret
    assert harness.bob_secret
    assert harness.alice_secret != harness.bob_secret


@pytest.mark.playwright
def test_push(harness: LocalAuthHarness) -> None:
    assert harness.alice_secret and harness.bob_secret, "run test_auth_setup first"

    remote = f"{harness.base}http/{_PROJECT}.git"
    alice_cfg = _key_git_config(_ALICE, harness.alice_secret)
    bob_cfg = _key_git_config(_BOB, harness.bob_secret)

    alice_work = harness.tmp_path / "alice_work"
    alice_work.mkdir()
    _run_git(alice_work, "init", "-b", "main")
    _run_git(alice_work, "config", "user.name", _ALICE)
    _run_git(alice_work, "config", "user.email", _ALICE_EMAIL)
    (alice_work / "README.md").write_text("from key\n", encoding="utf-8")
    _run_git(alice_work, "add", "README.md")
    _run_git(alice_work, "commit", "-m", "initial via http key")
    _run_git(alice_work, "remote", "add", "origin", remote)
    _run_git(alice_work, "push", "-u", "origin", "main", git_config=alice_cfg)

    head = _run_git(harness.bare, "rev-parse", "refs/heads/main")
    assert head
    subject = _run_git(harness.bare, "log", "-1", "--format=%s", "refs/heads/main")
    assert subject == "initial via http key"

    bob_work = harness.tmp_path / "bob_work"
    _run_git(harness.tmp_path, "clone", remote, "bob_work", git_config=bob_cfg)
    _run_git(bob_work, "config", "user.name", _BOB)
    _run_git(bob_work, "config", "user.email", _BOB_EMAIL)
    _run_git(bob_work, "checkout", "-b", _FEATURE)
    (bob_work / "FEATURE.md").write_text("bob feature\n", encoding="utf-8")
    _run_git(bob_work, "add", "FEATURE.md")
    _run_git(bob_work, "commit", "-m", "feature via http key")
    _run_git(bob_work, "push", "-u", "origin", _FEATURE, git_config=bob_cfg)

    harness.feature_tip = _run_git(harness.bare, "rev-parse", f"refs/heads/{_FEATURE}")
    assert harness.feature_tip
    feature_subject = _run_git(harness.bare, "log", "-1", "--format=%s", f"refs/heads/{_FEATURE}")
    assert feature_subject == "feature via http key"


@pytest.mark.playwright
def test_mr_flow(harness: LocalAuthHarness) -> None:
    assert harness.feature_tip, "run test_push first"

    with sync_api.sync_playwright() as playwright:
        browser = playwright.chromium.launch(headless=True)
        try:
            page = browser.new_page()
            _login(page, harness.base, _BOB, _BOB_PASSWORD)
            page.goto(f"{harness.base}project/{_PROJECT}", wait_until="domcontentloaded")
            page.locator("#summary-mr-theirs").select_option(f"refs/heads/{_FEATURE}")
            page.locator("#summary-mr-ours").select_option("refs/heads/main")
            page.get_by_role("button", name="Open merge request").click()
            sync_api.expect(page.get_by_role("heading", name="Merge request")).to_be_visible()
            sync_api.expect(page.get_by_role("heading", name=f"Merge {_FEATURE} into main")).to_be_visible()

            page.goto(f"{harness.base}logout?next=/", wait_until="domcontentloaded")
            _login(page, harness.base, _ALICE, _ALICE_PASSWORD)
            page.goto(f"{harness.base}project/{_PROJECT}", wait_until="domcontentloaded")
            page.get_by_role("link", name=f"Merge {_FEATURE} into main").click()
            sync_api.expect(page.get_by_role("heading", name="Merge request")).to_be_visible()
            page.get_by_role("button", name="Merge (fast-forward only)").click()
            sync_api.expect(page.get_by_role("heading", name="Merge request")).to_be_visible()
            sync_api.expect(page.get_by_text("Already up to date")).to_be_visible()
        finally:
            browser.close()

    main_tip = _run_git(harness.bare, "rev-parse", "refs/heads/main")
    assert main_tip == harness.feature_tip
    subject = _run_git(harness.bare, "log", "-1", "--format=%s", "refs/heads/main")
    assert subject == "feature via http key"