"""Tests for the global factory CLI entrypoint."""

from __future__ import annotations

import json
import os
import shlex
import shutil
import sqlite3
import subprocess
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[1]
FACTORY_BIN = ROOT / "bin" / "factory"
PROBE_ADW = Path(__file__).resolve().parent / "fixtures" / "adw_session_probe.py"
CONFIG_TEMPLATE = Path(__file__).resolve().parent / "fixtures" / "test-sssf.config.yaml"


@pytest.fixture
def factory_env(tmp_path: Path) -> dict[str, str]:
    factory_root = tmp_path / "factory-root"
    factory_root.mkdir()
    for name in ROOT.glob("adw_*.py"):
        (factory_root / name.name).symlink_to(name)
    (factory_root / "adw_modules").symlink_to(ROOT / "adw_modules")
    (factory_root / "prompt_engineering").symlink_to(ROOT / "prompt_engineering")
    (factory_root / "pyproject.toml").symlink_to(ROOT / "pyproject.toml")
    if (ROOT / "uv.lock").exists():
        (factory_root / "uv.lock").symlink_to(ROOT / "uv.lock")
    probe_target = factory_root / "adw_session_probe.py"
    probe_target.write_text(PROBE_ADW.read_text())
    probe_target.chmod(0o755)
    (factory_root / "bin").mkdir()
    (factory_root / "bin" / "factory").symlink_to(FACTORY_BIN)

    data_dir = tmp_path / "factory-data"
    db_path = tmp_path / "sssf.db"
    config_text = CONFIG_TEMPLATE.read_text()
    config_text = config_text.replace("PLACEHOLDER_DATA_DIR", str(data_dir))
    config_text = config_text.replace("PLACEHOLDER_DB", str(db_path))
    default_config = factory_root / "sssf.config.yaml"
    default_config.write_text(config_text)

    return {
        "FACTORY_ROOT": str(factory_root),
        "FACTORY_TEST_DB": str(db_path),
        "HOME": str(tmp_path / "home"),
        "PATH": os.environ.get("PATH", "/usr/bin:/bin"),
    }


def _init_git_repo(path: Path) -> None:
    subprocess.run(["git", "init", "-q", "-b", "main", str(path)], check=True)
    subprocess.run(
        ["git", "-C", str(path), "config", "user.email", "factory@test"],
        check=True,
    )
    subprocess.run(
        ["git", "-C", str(path), "config", "user.name", "factory"],
        check=True,
    )


def _run_factory(
    *args: str,
    cwd: Path,
    env: dict[str, str],
) -> subprocess.CompletedProcess[str]:
    return subprocess.run(
        [str(FACTORY_BIN), *args],
        cwd=cwd,
        env={**os.environ, **env},
        text=True,
        capture_output=True,
    )


def _install_uv_argv_probe(tmp_path: Path, env: dict[str, str]) -> Path:
    real_uv = shutil.which("uv")
    assert real_uv is not None, "uv must be on PATH for factory CLI tests"
    probe_dir = tmp_path / "uv-probe-bin"
    probe_dir.mkdir()
    probe_log = tmp_path / "uv-argv.jsonl"
    shim = probe_dir / "uv"
    probe_log_str = str(probe_log)
    shim.write_text(
        f"""#!/usr/bin/env bash
set -euo pipefail
python3 -c "import json,sys; open({probe_log_str!r}, 'a').write(json.dumps(sys.argv[1:]) + chr(10))" uv "$@"
exec {shlex.quote(real_uv)} "$@"
"""
    )
    shim.chmod(0o755)
    env["PATH"] = f"{probe_dir}{os.pathsep}{env['PATH']}"
    return probe_log


def _latest_uv_argv(probe_log: Path) -> list[str]:
    lines = [line for line in probe_log.read_text().splitlines() if line.strip()]
    assert lines, "expected factory to invoke uv exactly once"
    return json.loads(lines[-1])


def _session_repo(db_path: Path, adw_id: str = "probe-session") -> str | None:
    conn = sqlite3.connect(db_path)
    try:
        row = conn.execute(
            "SELECT repo FROM sessions WHERE adw_id=?",
            (adw_id,),
        ).fetchone()
        return row[0] if row else None
    finally:
        conn.close()


def test_temp_repo_without_project_config_uses_defaults_and_records_repo(
    tmp_path: Path,
    factory_env: dict[str, str],
) -> None:
    repo = tmp_path / "project"
    repo.mkdir()
    _init_git_repo(repo)

    db_path = Path(factory_env["FACTORY_TEST_DB"])
    result = _run_factory(
        "adw_session_probe",
        cwd=repo,
        env=factory_env,
    )

    assert result.returncode == 0, result.stderr
    assert _session_repo(db_path) == str(repo.resolve())


def test_nested_cwd_resolves_repo_root(
    tmp_path: Path,
    factory_env: dict[str, str],
) -> None:
    repo = tmp_path / "project"
    nested = repo / "packages" / "app"
    nested.mkdir(parents=True)
    _init_git_repo(repo)

    db_path = Path(factory_env["FACTORY_TEST_DB"])
    result = _run_factory(
        "adw_session_probe",
        cwd=nested,
        env=factory_env,
    )

    assert result.returncode == 0, result.stderr
    assert _session_repo(db_path) == str(repo.resolve())


def test_explicit_repo_and_config(
    tmp_path: Path,
    factory_env: dict[str, str],
) -> None:
    repo = tmp_path / "explicit-repo"
    other = tmp_path / "other-repo"
    repo.mkdir()
    other.mkdir()
    _init_git_repo(repo)
    _init_git_repo(other)

    custom_db = tmp_path / "custom.db"
    custom_data = tmp_path / "custom-data"
    custom_config = tmp_path / "custom.config.yaml"
    custom_config.write_text(
        CONFIG_TEMPLATE.read_text()
        .replace("PLACEHOLDER_DATA_DIR", str(custom_data))
        .replace("PLACEHOLDER_DB", str(custom_db))
    )

    result = _run_factory(
        "--repo",
        str(other),
        "--config",
        str(custom_config),
        "adw_session_probe",
        cwd=repo,
        env=factory_env,
    )

    assert result.returncode == 0, result.stderr
    assert _session_repo(custom_db) == str(other.resolve())


def test_project_factory_config_is_selected(
    tmp_path: Path,
    factory_env: dict[str, str],
) -> None:
    repo = tmp_path / "project"
    project_config_dir = repo / ".factory"
    project_config_dir.mkdir(parents=True)
    _init_git_repo(repo)

    project_db = tmp_path / "project.db"
    project_data = tmp_path / "project-data"
    project_config = project_config_dir / "sssf.config.yaml"
    project_config.write_text(
        CONFIG_TEMPLATE.read_text()
        .replace("PLACEHOLDER_DATA_DIR", str(project_data))
        .replace("PLACEHOLDER_DB", str(project_db))
    )

    result = _run_factory(
        "adw_session_probe",
        cwd=repo,
        env=factory_env,
    )

    assert result.returncode == 0, result.stderr
    assert _session_repo(project_db) == str(repo.resolve())
    assert not Path(factory_env["FACTORY_TEST_DB"]).exists()


def test_rejects_non_git_target(
    tmp_path: Path,
    factory_env: dict[str, str],
) -> None:
    not_repo = tmp_path / "plain-dir"
    not_repo.mkdir()

    result = _run_factory(
        "--repo",
        str(not_repo),
        "adw_session_probe",
        cwd=tmp_path,
        env=factory_env,
    )

    assert result.returncode == 2
    assert "not a git repository" in result.stderr


def test_rejects_missing_config(
    tmp_path: Path,
    factory_env: dict[str, str],
) -> None:
    repo = tmp_path / "project"
    repo.mkdir()
    _init_git_repo(repo)

    result = _run_factory(
        "--config",
        str(tmp_path / "missing.yaml"),
        "adw_session_probe",
        cwd=repo,
        env=factory_env,
    )

    assert result.returncode == 2
    assert "config not found" in result.stderr


def test_rejects_unknown_adw(
    tmp_path: Path,
    factory_env: dict[str, str],
) -> None:
    repo = tmp_path / "project"
    repo.mkdir()
    _init_git_repo(repo)

    result = _run_factory(
        "not-a-real-adw",
        cwd=repo,
        env=factory_env,
    )

    assert result.returncode == 2
    assert "unknown ADW" in result.stderr


def test_project_owned_adw_invoked_by_short_name_from_nested_cwd(
    tmp_path: Path,
    factory_env: dict[str, str],
) -> None:
    repo = tmp_path / "project"
    nested = repo / "packages" / "app"
    nested.mkdir(parents=True)
    project_config_dir = repo / ".factory"
    project_config_dir.mkdir(parents=True)
    _init_git_repo(repo)

    project_db = tmp_path / "project-owned.db"
    project_data = tmp_path / "project-owned-data"
    project_config = project_config_dir / "sssf.config.yaml"
    project_config.write_text(
        CONFIG_TEMPLATE.read_text()
        .replace("PLACEHOLDER_DATA_DIR", str(project_data))
        .replace("PLACEHOLDER_DB", str(project_db))
    )

    project_probe = project_config_dir / "adw_project_probe.py"
    project_probe.write_text(PROBE_ADW.read_text())
    project_probe.chmod(0o755)

    result = _run_factory(
        "project_probe",
        cwd=nested,
        env=factory_env,
    )

    assert result.returncode == 0, result.stderr
    assert _session_repo(project_db) == str(repo.resolve())
    assert not Path(factory_env["FACTORY_TEST_DB"]).exists()


def test_exec_uses_single_uv_run_argv(
    tmp_path: Path,
    factory_env: dict[str, str],
) -> None:
    repo = tmp_path / "project"
    repo.mkdir()
    _init_git_repo(repo)

    probe_log = _install_uv_argv_probe(tmp_path, factory_env)
    result = _run_factory(
        "adw_session_probe",
        cwd=repo,
        env=factory_env,
    )

    assert result.returncode == 0, result.stderr
    argv = _latest_uv_argv(probe_log)
    assert argv[:3] == ["uv", "run", "--project"]
    assert argv[3] == factory_env["FACTORY_ROOT"]
    assert argv[4].endswith("/adw_session_probe.py")
    assert argv.count("run") == 1
    assert "uv" not in argv[4:]


def test_rejects_project_adw_symlink_escape(
    tmp_path: Path,
    factory_env: dict[str, str],
) -> None:
    repo = tmp_path / "project"
    outside = tmp_path / "outside-adw.py"
    project_config_dir = repo / ".factory"
    project_config_dir.mkdir(parents=True)
    _init_git_repo(repo)
    outside.write_text("#!/usr/bin/env python3\nprint('escaped')\n")
    outside.chmod(0o755)
    (project_config_dir / "adw_escape_probe.py").symlink_to(outside)

    result = _run_factory(
        "escape_probe",
        cwd=repo,
        env=factory_env,
    )

    assert result.returncode == 2
    assert "resolves outside" in result.stderr


def test_bootstrap_symlinks_factory_to_path(
    tmp_path: Path,
) -> None:
    harness_root = tmp_path / "harness"
    harness_root.mkdir()
    factory_root = harness_root / "factory"
    (factory_root / "bin").mkdir(parents=True)
    (factory_root / "bin" / "factory").write_text(FACTORY_BIN.read_text())
    (factory_root / "bin" / "factory").chmod(0o755)

    home = tmp_path / "home"
    home.mkdir()

    artifact = home / ".local" / "opt" / "overdeck" / "harness" / "current"
    artifact.mkdir(parents=True)
    (artifact / "factory" / "bin").mkdir(parents=True)
    (artifact / "factory" / "bin" / "factory").symlink_to(factory_root / "bin" / "factory")

    local_bin = home / ".local" / "bin"
    local_bin.mkdir(parents=True)

    env = {
        **os.environ,
        "HOME": str(home),
    }
    bootstrap = Path(__file__).resolve().parents[2] / "bootstrap.sh"
    result = subprocess.run(
        ["bash", str(bootstrap)],
        cwd=harness_root,
        env=env,
        text=True,
        capture_output=True,
    )
    assert result.returncode == 0, result.stdout + result.stderr

    link = local_bin / "factory"
    assert link.is_symlink()
    assert link.resolve() == (artifact / "factory" / "bin" / "factory").resolve()
