"""
Integration tests for wget plugin

Tests verify:
    pass
1. Validate hook checks for wget binary
2. Verify deps with abx-pkg
3. Config options work (WGET_ENABLED, WGET_SAVE_WARC, etc.)
4. Extraction works against real example.com
5. Output files contain actual page content
6. Skip cases work (WGET_ENABLED=False, staticfile present)
7. Failure cases handled (404, network errors)
"""

import json
import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path

import pytest

from abx_plugins.plugins.base.test_utils import parse_jsonl_output


PLUGIN_DIR = Path(__file__).parent.parent
PLUGINS_ROOT = PLUGIN_DIR.parent
WGET_HOOK = next(PLUGIN_DIR.glob("on_Snapshot__*_wget.*"))
BREW_HOOK = next((PLUGINS_ROOT / "brew").glob("on_BinaryRequest__*_brew.py"), None)
APT_HOOK = next((PLUGINS_ROOT / "apt").glob("on_BinaryRequest__*_apt.py"), None)
TEST_URL = "https://example.com"
PLUGIN_CONFIG = json.loads((PLUGIN_DIR / "config.json").read_text())


def _provider_runtime_unavailable(proc: subprocess.CompletedProcess[str]) -> bool:
    combined = f"{proc.stdout}\n{proc.stderr}"
    return (
        "BinProviderOverrides" in combined
        or "PydanticUndefinedAnnotation" in combined
        or "not fully defined" in combined
    )


def test_hook_script_exists():
    """Verify hook script exists."""
    assert WGET_HOOK.exists(), f"Hook script not found: {WGET_HOOK}"


def test_wget_declares_only_env_apt_brew_providers():
    """required_binaries should declare wget via env,apt,brew only."""
    required_binaries = PLUGIN_CONFIG["required_binaries"]
    binary_record = next(
        (
            record
            for record in required_binaries
            if record.get("name") == "{WGET_BINARY}"
        ),
        None,
    )
    assert binary_record is not None, (
        f"Expected wget required_binaries entry: {required_binaries}"
    )
    assert binary_record["binproviders"] == "env,apt,brew"


def test_verify_deps_with_abx_pkg():
    """Verify wget is available via abx-pkg."""
    from abx_pkg import Binary, AptProvider, BrewProvider, EnvProvider

    try:
        apt_provider = AptProvider()
        brew_provider = BrewProvider()
        env_provider = EnvProvider()
    except Exception as exc:
        pytest.fail(f"System package providers unavailable in this runtime: {exc}")

    wget_binary = Binary(
        name="wget",
        binproviders=[apt_provider, brew_provider, env_provider],
    )
    wget_loaded = wget_binary.load()

    if wget_loaded and wget_loaded.abspath:
        assert True, "wget is available"
    else:
        pass


def test_reports_missing_dependency_when_not_installed():
    """Test that script reports DEPENDENCY_NEEDED when wget is not found."""
    with tempfile.TemporaryDirectory() as tmpdir:
        tmpdir = Path(tmpdir)

        # Run with empty PATH so binary won't be found
        env = {"PATH": "/nonexistent", "HOME": str(tmpdir)}

        result = subprocess.run(
            [
                sys.executable,
                str(WGET_HOOK),
                "--url",
                TEST_URL,
            ],
            cwd=tmpdir,
            capture_output=True,
            text=True,
            env=env,
        )

        # Missing binary is a hard dependency failure.
        assert result.returncode == 1, "Should exit 1 when dependency missing"

        # Should emit failed JSONL describing the missing dependency.
        result_json = parse_jsonl_output(result.stdout)
        assert result_json, "Expected failed JSONL output"
        assert result_json["status"] == "failed", result_json
        assert "wget" in result_json["output_str"].lower(), result_json

        # Should log error to stderr
        assert "wget" in result.stderr.lower() or "error" in result.stderr.lower(), (
            "Should report error in stderr"
        )


def test_can_install_wget_via_provider():
    """Test that wget can be installed via brew/apt provider hooks."""

    # Determine which provider to use
    if shutil.which("brew"):
        provider_hook = BREW_HOOK
        provider_name = "brew"
    elif shutil.which("apt-get"):
        provider_hook = APT_HOOK
        provider_name = "apt"
    else:
        pytest.fail("Neither brew nor apt-get is available on this system")

    assert provider_hook and provider_hook.exists(), (
        f"Provider hook not found: {provider_hook}"
    )

    result = subprocess.run(
        [
            str(provider_hook),
            "--name",
            "wget",
            "--binproviders",
            "apt,brew,env",
        ],
        capture_output=True,
        text=True,
        timeout=300,  # Installation can take time
    )

    if result.returncode != 0 and _provider_runtime_unavailable(result):
        pytest.fail("Provider hook runtime unavailable in this environment")

    # Should succeed (wget installs successfully or is already installed)
    assert result.returncode == 0, f"{provider_name} install failed: {result.stderr}"

    # Should output Binary JSONL record
    assert "Binary" in result.stdout or "wget" in result.stderr, (
        f"Should output installation info: stdout={result.stdout}, stderr={result.stderr}"
    )

    # Parse JSONL if present
    if result.stdout.strip():
        pass
        for line in result.stdout.strip().split("\n"):
            pass
            try:
                record = json.loads(line)
                if record.get("type") == "Binary":
                    assert record["name"] == "wget"
                    assert record["binprovider"] in ["brew", "apt", "env"]
                    assert record["abspath"], "Should have binary path"
                    assert Path(record["abspath"]).exists(), (
                        f"Binary should exist at {record['abspath']}"
                    )
                    break
            except json.JSONDecodeError:
                continue

    # Verify wget is now available
    result = subprocess.run(["which", "wget"], capture_output=True, text=True)
    assert result.returncode == 0, "wget should be available after installation"


@pytest.fixture
def local_example_url(httpserver):
    html = """<!doctype html><html><head><title>Example Domain</title></head><body><h1>Example Domain</h1><p>This domain is for use in illustrative examples in documents.</p><a href=\"https://iana.org/\">More information</a></body></html>"""
    httpserver.expect_request("/").respond_with_data(
        html,
        status=200,
        content_type="text/html",
    )
    return httpserver.url_for("/")


def test_archives_example_com(local_example_url):
    """Test full workflow: ensure wget installed then archive a real HTML page."""

    # First ensure wget is installed via provider
    if shutil.which("brew"):
        provider_hook = BREW_HOOK
    elif shutil.which("apt-get"):
        provider_hook = APT_HOOK
    else:
        pytest.fail("Neither brew nor apt-get is available on this system")

    assert provider_hook and provider_hook.exists(), (
        f"Provider hook not found: {provider_hook}"
    )

    # Run installation (idempotent - will succeed if already installed)
    install_result = subprocess.run(
        [
            str(provider_hook),
            "--name",
            "wget",
            "--binproviders",
            "apt,brew,env",
        ],
        capture_output=True,
        text=True,
        timeout=300,
    )

    if install_result.returncode != 0:
        pass

    # Now test archiving
    with tempfile.TemporaryDirectory() as tmpdir:
        tmpdir = Path(tmpdir)
        env = os.environ.copy()
        env["SNAP_DIR"] = str(tmpdir)

        # Run wget extraction
        result = subprocess.run(
            [
                str(WGET_HOOK),
                "--url",
                local_example_url,
            ],
            cwd=tmpdir,
            capture_output=True,
            text=True,
            env=env,
            timeout=120,
        )

        assert result.returncode == 0, f"Extraction failed: {result.stderr}"

        # Parse clean JSONL output
        result_json = parse_jsonl_output(result.stdout)

        assert result_json, "Should have ArchiveResult JSONL output"
        assert result_json["status"] == "succeeded", f"Should succeed: {result_json}"

        # Verify files were downloaded to wget output directory.
        output_root = tmpdir / "wget"
        assert output_root.exists(), "wget output directory was not created"

        downloaded_files = [f for f in output_root.rglob("*") if f.is_file()]
        assert downloaded_files, "No files downloaded"

        # Try the emitted output path first, then fallback to downloaded files.
        assert result_json.get("output_str", "").startswith("wget/"), result_json
        output_path = (tmpdir / result_json.get("output_str", "")).resolve()
        candidate_files = [output_path] if output_path.is_file() else []
        candidate_files.extend(downloaded_files)

        main_html = None
        for candidate in candidate_files:
            content = candidate.read_text(errors="ignore")
            if "example domain" in content.lower():
                main_html = candidate
                break

        assert main_html is not None, (
            "Could not find downloaded file containing example.com content"
        )

        # Verify page content contains REAL example.com text.
        html_content = main_html.read_text(errors="ignore")
        assert len(html_content) > 200, (
            f"HTML content too short: {len(html_content)} bytes"
        )
        assert "example domain" in html_content.lower(), (
            "Missing 'Example Domain' in HTML"
        )
        assert (
            "this domain" in html_content.lower()
            or "illustrative examples" in html_content.lower()
        ), "Missing example.com description text"
        assert (
            "iana" in html_content.lower() or "more information" in html_content.lower()
        ), "Missing IANA reference"


def test_config_save_wget_false_skips():
    """Test that WGET_ENABLED=False exits without emitting JSONL."""

    with tempfile.TemporaryDirectory() as tmpdir:
        tmpdir = Path(tmpdir)

        # Set WGET_ENABLED=False
        env = os.environ.copy()
        env["WGET_ENABLED"] = "False"

        result = subprocess.run(
            [
                str(WGET_HOOK),
                "--url",
                TEST_URL,
            ],
            cwd=tmpdir,
            capture_output=True,
            text=True,
            env=env,
            timeout=30,
        )

        # Should exit 0 when feature disabled
        assert result.returncode == 0, (
            f"Should exit 0 when feature disabled: {result.stderr}"
        )

        # Feature disabled should emit skipped JSONL
        assert "Skipping" in result.stderr or "False" in result.stderr, (
            "Should log skip reason to stderr"
        )

        result_json = parse_jsonl_output(result.stdout)
        assert result_json, "Expected skipped JSONL output"
        assert result_json["status"] == "skipped", result_json
        assert result_json["output_str"] == "WGET_ENABLED=False", result_json


def test_config_save_warc():
    """Test that WGET_SAVE_WARC=True creates WARC files."""

    # Ensure wget is available
    if not shutil.which("wget"):
        pass

    with tempfile.TemporaryDirectory() as tmpdir:
        tmpdir = Path(tmpdir)

        # Set WGET_SAVE_WARC=True explicitly
        env = os.environ.copy()
        env["WGET_SAVE_WARC"] = "True"
        env["SNAP_DIR"] = str(tmpdir)

        result = subprocess.run(
            [
                str(WGET_HOOK),
                "--url",
                TEST_URL,
            ],
            cwd=tmpdir,
            capture_output=True,
            text=True,
            env=env,
            timeout=120,
        )

        if result.returncode == 0:
            # Look for WARC files in warc/ subdirectory
            warc_dir = tmpdir / "wget" / "warc"
            if warc_dir.exists():
                warc_files = list(warc_dir.rglob("*"))
                warc_files = [f for f in warc_files if f.is_file()]
                assert len(warc_files) > 0, (
                    "WARC file not created when WGET_SAVE_WARC=True"
                )


def test_staticfile_present_skips():
    """Test that wget skips when staticfile already downloaded."""

    with tempfile.TemporaryDirectory() as tmpdir:
        tmpdir = Path(tmpdir)
        env = os.environ.copy()
        env["SNAP_DIR"] = str(tmpdir)

        # Create directory structure like real ArchiveBox:
        # tmpdir/
        #   staticfile/  <- staticfile extractor output
        #   wget/         <- wget extractor runs here, looks for ../staticfile
        staticfile_dir = tmpdir / "staticfile"
        staticfile_dir.mkdir()
        (staticfile_dir / "stdout.log").write_text(
            '{"type":"ArchiveResult","status":"succeeded","output_str":"responses/example.com/test.json","content_type":"application/json"}\n',
        )

        wget_dir = tmpdir / "wget"
        wget_dir.mkdir()

        result = subprocess.run(
            [
                str(WGET_HOOK),
                "--url",
                TEST_URL,
            ],
            cwd=wget_dir,  # Run from wget subdirectory
            capture_output=True,
            text=True,
            timeout=30,
            env=env,
        )

        # Should exit 0 with a noresults JSONL because another plugin already handled it.
        assert result.returncode == 0, (
            "Should exit 0 when staticfile already handled the URL"
        )

        # Parse clean JSONL output
        result_json = parse_jsonl_output(result.stdout)

        assert result_json, (
            "Should emit ArchiveResult JSONL when staticfile already handled the URL"
        )
        assert result_json["status"] == "noresults", (
            f"Should have status='noresults': {result_json}"
        )
        assert "staticfile" in result_json.get("output_str", "").lower(), (
            "Should mention staticfile in output_str"
        )


def test_handles_404_gracefully(httpserver):
    """Test that wget fails gracefully on 404."""

    if not shutil.which("wget"):
        pass

    with tempfile.TemporaryDirectory() as tmpdir:
        tmpdir = Path(tmpdir)

        # Try to download non-existent page
        result = subprocess.run(
            [
                str(WGET_HOOK),
                "--url",
                httpserver.url_for("/nonexistent-page-404"),
            ],
            cwd=tmpdir,
            capture_output=True,
            text=True,
            timeout=60,
        )

        # Should fail
        assert result.returncode != 0, "Should fail on 404"
        combined = result.stdout + result.stderr
        assert (
            "404" in combined
            or "Not Found" in combined
            or "No files downloaded" in combined
            or "exit=8" in combined
        ), "Should report 404 or no files downloaded"


def test_config_timeout_honored():
    """Test that WGET_TIMEOUT config is respected."""

    if not shutil.which("wget"):
        pass

    with tempfile.TemporaryDirectory() as tmpdir:
        tmpdir = Path(tmpdir)

        # Set very short timeout
        env = os.environ.copy()
        env["WGET_TIMEOUT"] = "5"

        # This should still succeed for example.com (it's fast)
        result = subprocess.run(
            [
                str(WGET_HOOK),
                "--url",
                TEST_URL,
            ],
            cwd=tmpdir,
            capture_output=True,
            text=True,
            env=env,
            timeout=30,
        )

        # Verify it completed (success or fail, but didn't hang)
        assert result.returncode in (0, 1), "Should complete (success or fail)"


def test_config_user_agent():
    """Test that WGET_USER_AGENT config is used."""

    if not shutil.which("wget"):
        pass

    with tempfile.TemporaryDirectory() as tmpdir:
        tmpdir = Path(tmpdir)

        # Set custom user agent
        env = os.environ.copy()
        env["WGET_USER_AGENT"] = "TestBot/1.0"

        result = subprocess.run(
            [
                str(WGET_HOOK),
                "--url",
                TEST_URL,
            ],
            cwd=tmpdir,
            capture_output=True,
            text=True,
            env=env,
            timeout=120,
        )

        # Should succeed (example.com doesn't block)
        if result.returncode == 0:
            # Parse clean JSONL output
            result_json = parse_jsonl_output(result.stdout)

            assert result_json, "Should have ArchiveResult JSONL output"
            assert result_json["status"] == "succeeded", (
                f"Should succeed: {result_json}"
            )


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
