from __future__ import annotations

import json
from pathlib import Path

import polars as pl

from honestroles.cli.main import main
from honestroles.plugins.registry import PluginRegistry


def test_docs_home_surfaces_app_first_links() -> None:
    root = Path(__file__).resolve().parents[2]
    readme = (root / "README.md").read_text(encoding="utf-8")
    docs_home = (root / "docs" / "index.md").read_text(encoding="utf-8")
    mkdocs_config = (root / "mkdocs.yml").read_text(encoding="utf-8")
    pyproject = (root / "pyproject.toml").read_text(encoding="utf-8")

    assert "https://honestroles.com" in readme
    assert "https://honestroles.com" in docs_home
    assert 'site_url: https://honestroles.com/docs/' in mkdocs_config
    assert 'Documentation = "https://honestroles.com/docs/"' in pyproject
    assert "App:" in mkdocs_config


def _write_sample_parquet(path: Path) -> None:
    pl.DataFrame(
        {
            "id": ["1", "2", "3"],
            "title": ["Data Engineer", "Senior ML Engineer", "Intern Analyst"],
            "company": ["A", "B", "C"],
            "location": ["Remote", "NYC", "Remote"],
            "remote": ["true", "false", "1"],
            "description_text": [
                "Python SQL data pipelines",
                "Build ML systems with Python and AWS",
                "Excel and reporting",
            ],
            "description_html": ["<p>Python SQL</p>", "<b>ML</b>", "<i>intern</i>"],
            "skills": ["python,sql", "python,aws", None],
            "salary_min": [120000, 180000, None],
            "salary_max": [160000, 220000, None],
            "apply_url": ["https://example.com/1", "https://example.com/2", "https://example.com/3"],
            "posted_at": ["2026-01-01", "2026-01-02", "2026-01-03"],
        }
    ).write_parquet(path)


def _write_pipeline(
    path: Path,
    input_path: Path,
    output_path: Path,
    *,
    fail_fast: bool,
    top_k: int,
) -> None:
    text = f"""
[input]
kind = "parquet"
path = "{input_path}"

[output]
path = "{output_path}"

[stages.clean]
enabled = true

[stages.filter]
enabled = true
remote_only = false

[stages.label]
enabled = true

[stages.rate]
enabled = true

[stages.match]
enabled = true
top_k = {top_k}

[runtime]
fail_fast = {str(fail_fast).lower()}
random_seed = 0
""".strip()
    path.write_text(text, encoding="utf-8")


def test_docs_first_run_cli_snippet(tmp_path: Path, capsys) -> None:
    input_path = tmp_path / "jobs.parquet"
    output_path = tmp_path / "jobs_scored.parquet"
    pipeline_path = tmp_path / "pipeline.toml"
    manifest_path = tmp_path / "plugins.toml"

    _write_sample_parquet(input_path)
    _write_pipeline(pipeline_path, input_path, output_path, fail_fast=True, top_k=10)

    manifest_path.write_text(
        """
[[plugins]]
name = "label_note"
kind = "label"
callable = "tests.plugins.fixture_plugins:label_note"
enabled = true
order = 1
""".strip(),
        encoding="utf-8",
    )

    code = main(
        [
            "run",
            "--pipeline-config",
            str(pipeline_path),
            "--plugins",
            str(manifest_path),
        ]
    )
    assert code == 0

    payload = json.loads(capsys.readouterr().out)
    assert {"stage_rows", "plugin_counts", "runtime", "final_rows"}.issubset(payload.keys())
    assert "input_adapter" in payload
    assert output_path.exists()


def test_docs_invalid_top_k_config_validation(tmp_path: Path) -> None:
    pipeline_path = tmp_path / "bad_pipeline.toml"
    pipeline_path.write_text(
        """
[input]
kind = "parquet"
path = "./jobs.parquet"

[stages.match]
enabled = true
top_k = 0
""".strip(),
        encoding="utf-8",
    )

    code = main(["config", "validate", "--pipeline", str(pipeline_path)])
    assert code == 2


def test_docs_fail_fast_and_recovery_behavior(tmp_path: Path, capsys) -> None:
    input_path = tmp_path / "jobs.parquet"
    output_path = tmp_path / "jobs_scored.parquet"
    pipeline_fail_fast_path = tmp_path / "pipeline_fail_fast.toml"
    pipeline_recovery_path = tmp_path / "pipeline_recovery.toml"
    manifest_path = tmp_path / "plugins.toml"

    _write_sample_parquet(input_path)
    _write_pipeline(
        pipeline_fail_fast_path,
        input_path,
        output_path,
        fail_fast=True,
        top_k=10,
    )
    _write_pipeline(
        pipeline_recovery_path,
        input_path,
        output_path,
        fail_fast=False,
        top_k=10,
    )

    manifest_path.write_text(
        """
[[plugins]]
name = "failing_filter"
kind = "filter"
callable = "tests.plugins.fixture_plugins:fail_filter"
enabled = true
order = 1
""".strip(),
        encoding="utf-8",
    )

    fail_fast_code = main(
        [
            "run",
            "--pipeline-config",
            str(pipeline_fail_fast_path),
            "--plugins",
            str(manifest_path),
        ]
    )
    assert fail_fast_code == 3
    _ = capsys.readouterr()

    recovery_code = main(
        [
            "run",
            "--pipeline-config",
            str(pipeline_recovery_path),
            "--plugins",
            str(manifest_path),
        ]
    )
    assert recovery_code == 0
    payload = json.loads(capsys.readouterr().out)
    assert "non_fatal_errors" in payload
    assert payload["non_fatal_errors"][0]["stage"] == "filter"


def test_docs_plugin_manifest_disabled_and_ordering(tmp_path: Path) -> None:
    manifest_path = tmp_path / "plugins.toml"
    manifest_path.write_text(
        """
[[plugins]]
name = "label_b"
kind = "label"
callable = "tests.plugins.fixture_plugins:label_note"
enabled = true
order = 20

[[plugins]]
name = "label_a"
kind = "label"
callable = "tests.plugins.fixture_plugins:label_note"
enabled = true
order = 20

[[plugins]]
name = "label_disabled"
kind = "label"
callable = "tests.plugins.fixture_plugins:label_note"
enabled = false
order = 0
""".strip(),
        encoding="utf-8",
    )

    registry = PluginRegistry.from_manifest(manifest_path)
    assert registry.list("label") == ("label_a", "label_b")
    assert "label_disabled" not in registry.list("label")


def test_docs_alias_and_quality_profile_snippet(tmp_path: Path, capsys) -> None:
    input_path = tmp_path / "jobs.parquet"
    pipeline_path = tmp_path / "pipeline.toml"
    _write_sample_parquet(input_path)

    pipeline_path.write_text(
        f"""
[input]
kind = "parquet"
path = "{input_path}"

[input.aliases]
location = ["location_raw"]
remote = ["remote_flag"]

[stages.clean]
enabled = false

[stages.filter]
enabled = false

[stages.label]
enabled = false

[stages.rate]
enabled = false

[stages.match]
enabled = false

[runtime.quality]
profile = "core_fields_weighted"

[runtime.quality.field_weights]
posted_at = 0.6
salary_min = 0.2
""".strip(),
        encoding="utf-8",
    )

    validate_code = main(["config", "validate", "--pipeline", str(pipeline_path)])
    assert validate_code == 0
    _ = capsys.readouterr()

    quality_code = main(["report-quality", "--pipeline-config", str(pipeline_path)])
    assert quality_code == 0
    payload = json.loads(capsys.readouterr().out)
    assert "profile" in payload
    assert "weighted_null_percent" in payload
    assert "effective_weights" in payload


def test_docs_adapter_infer_snippet(tmp_path: Path, capsys) -> None:
    input_path = tmp_path / "jobs.parquet"
    output_file = tmp_path / "adapter-draft.toml"
    _write_sample_parquet(input_path)

    code = main(
        [
            "adapter",
            "infer",
            "--input-parquet",
            str(input_path),
            "--output-file",
            str(output_file),
            "--sample-rows",
            "100",
            "--top-candidates",
            "3",
            "--min-confidence",
            "0.5",
        ]
    )
    assert code == 0
    payload = json.loads(capsys.readouterr().out)
    assert Path(payload["adapter_draft"]).exists()
    assert Path(payload["inference_report"]).exists()
    assert payload["field_suggestions"] >= 1


def test_docs_eda_generate_snippet(tmp_path: Path, capsys) -> None:
    input_path = tmp_path / "jobs.parquet"
    artifacts_dir = tmp_path / "eda"
    _write_sample_parquet(input_path)

    code = main(
        [
            "eda",
            "generate",
            "--input-parquet",
            str(input_path),
            "--output-dir",
            str(artifacts_dir),
            "--top-k",
            "5",
        ]
    )
    assert code == 0
    payload = json.loads(capsys.readouterr().out)
    assert Path(payload["manifest"]).exists()
    assert Path(payload["summary"]).exists()
    assert Path(payload["report"]).exists()


def test_docs_eda_diff_and_gate_snippet(tmp_path: Path, capsys) -> None:
    baseline_input = tmp_path / "jobs_baseline.parquet"
    candidate_input = tmp_path / "jobs_candidate.parquet"
    baseline_dir = tmp_path / "baseline_eda"
    candidate_dir = tmp_path / "candidate_eda"
    diff_dir = tmp_path / "diff_eda"

    _write_sample_parquet(baseline_input)
    _write_sample_parquet(candidate_input)

    gen_base_code = main(
        [
            "eda",
            "generate",
            "--input-parquet",
            str(baseline_input),
            "--output-dir",
            str(baseline_dir),
        ]
    )
    assert gen_base_code == 0
    _ = capsys.readouterr()

    gen_candidate_code = main(
        [
            "eda",
            "generate",
            "--input-parquet",
            str(candidate_input),
            "--output-dir",
            str(candidate_dir),
        ]
    )
    assert gen_candidate_code == 0
    _ = capsys.readouterr()

    diff_code = main(
        [
            "eda",
            "diff",
            "--baseline-dir",
            str(baseline_dir),
            "--candidate-dir",
            str(candidate_dir),
            "--output-dir",
            str(diff_dir),
        ]
    )
    assert diff_code == 0
    diff_payload = json.loads(capsys.readouterr().out)
    assert Path(diff_payload["diff_json"]).exists()

    gate_code = main(
        [
            "eda",
            "gate",
            "--candidate-dir",
            str(candidate_dir),
            "--baseline-dir",
            str(baseline_dir),
        ]
    )
    assert gate_code == 0
    gate_payload = json.loads(capsys.readouterr().out)
    assert "status" in gate_payload
    assert "evaluated_rules" in gate_payload
