"""v0.64.0 Part F — License advisor tests."""

from __future__ import annotations

import dataclasses

import pytest
from typer.testing import CliRunner

runner = CliRunner()


def test_module_imports():
    from soup_cli.utils import license_advisor

    assert hasattr(license_advisor, "DEPLOY_TARGETS")
    assert hasattr(license_advisor, "validate_deploy_target")
    assert hasattr(license_advisor, "LicenseRecommendation")
    assert hasattr(license_advisor, "advise_license_for_target")
    assert hasattr(license_advisor, "flag_downstream_risk")
    assert hasattr(license_advisor, "DownstreamRisk")


# ---------------------------------------------------------------------------
# DEPLOY_TARGETS
# ---------------------------------------------------------------------------


def test_deploy_targets_exact():
    from soup_cli.utils.license_advisor import DEPLOY_TARGETS

    assert "b2c" in DEPLOY_TARGETS
    assert "defense" in DEPLOY_TARGETS
    assert "embedded" in DEPLOY_TARGETS


def test_deploy_targets_is_frozenset():
    from soup_cli.utils.license_advisor import DEPLOY_TARGETS

    assert isinstance(DEPLOY_TARGETS, frozenset)


# ---------------------------------------------------------------------------
# validate_deploy_target
# ---------------------------------------------------------------------------


@pytest.mark.parametrize("v", ["b2c", "defense", "embedded"])
def test_validate_deploy_target_happy(v):
    from soup_cli.utils.license_advisor import validate_deploy_target

    assert validate_deploy_target(v) == v


def test_validate_deploy_target_case_insensitive():
    from soup_cli.utils.license_advisor import validate_deploy_target

    assert validate_deploy_target("B2C") == "b2c"


@pytest.mark.parametrize(
    "bad",
    [True, False, None, "", "consumer", "x" * 33],
)
def test_validate_deploy_target_rejects(bad):
    from soup_cli.utils.license_advisor import validate_deploy_target

    with pytest.raises((TypeError, ValueError)):
        validate_deploy_target(bad)


def test_validate_deploy_target_null_byte():
    from soup_cli.utils.license_advisor import validate_deploy_target

    with pytest.raises(ValueError, match="null"):
        validate_deploy_target("b\x002c")


# ---------------------------------------------------------------------------
# LicenseRecommendation
# ---------------------------------------------------------------------------


def test_license_recommendation_frozen():
    from soup_cli.utils.license_advisor import LicenseRecommendation

    r = LicenseRecommendation(
        target="b2c",
        recommended_licenses=("apache-2.0", "mit"),
        forbidden_licenses=("cc-by-nc-4.0",),
        reason="permissive only",
    )
    with pytest.raises(dataclasses.FrozenInstanceError):
        r.target = "other"  # type: ignore[misc]


def test_license_recommendation_tuples():
    from soup_cli.utils.license_advisor import LicenseRecommendation

    # Lists rejected (frozen=True doesn't make lists immutable)
    with pytest.raises(TypeError, match="tuple"):
        LicenseRecommendation(
            target="b2c",
            recommended_licenses=["apache-2.0"],  # type: ignore[arg-type]
            forbidden_licenses=(),
            reason="x",
        )


# ---------------------------------------------------------------------------
# advise_license_for_target
# ---------------------------------------------------------------------------


def test_advise_license_b2c():
    from soup_cli.utils.license_advisor import advise_license_for_target

    rec = advise_license_for_target("b2c")
    # B2C must include broadly-permissive options
    assert "apache-2.0" in rec.recommended_licenses
    assert "mit" in rec.recommended_licenses
    # Non-commercial licenses must be forbidden for B2C
    assert "cc-by-nc-4.0" in rec.forbidden_licenses


def test_advise_license_defense():
    from soup_cli.utils.license_advisor import advise_license_for_target

    rec = advise_license_for_target("defense")
    # Defense forbids restricted-use community licenses
    assert "llama-3.1" in rec.forbidden_licenses or "llama-2" in rec.forbidden_licenses


def test_advise_license_embedded():
    from soup_cli.utils.license_advisor import advise_license_for_target

    rec = advise_license_for_target("embedded")
    # Embedded systems should avoid strong copyleft
    assert "gpl-3.0" in rec.forbidden_licenses


def test_advise_license_rejects_unknown_target():
    from soup_cli.utils.license_advisor import advise_license_for_target

    with pytest.raises(ValueError):
        advise_license_for_target("bogus")


def test_advise_license_rejects_bool():
    from soup_cli.utils.license_advisor import advise_license_for_target

    with pytest.raises(TypeError):
        advise_license_for_target(True)  # type: ignore[arg-type]


# ---------------------------------------------------------------------------
# DownstreamRisk
# ---------------------------------------------------------------------------


def test_downstream_risk_frozen():
    from soup_cli.utils.license_advisor import DownstreamRisk

    r = DownstreamRisk(ok=True, severity="ok", reason="permissive license")
    with pytest.raises(dataclasses.FrozenInstanceError):
        r.ok = False  # type: ignore[misc]


def test_downstream_risk_severity_allowlist():
    from soup_cli.utils.license_advisor import DownstreamRisk

    with pytest.raises(ValueError, match="severity"):
        DownstreamRisk(ok=False, severity="weird", reason="x")


# ---------------------------------------------------------------------------
# flag_downstream_risk
# ---------------------------------------------------------------------------


def test_flag_downstream_risk_apache_b2c_ok():
    from soup_cli.utils.license_advisor import flag_downstream_risk

    r = flag_downstream_risk(license_id="apache-2.0", target="b2c", monthly_active_users=10_000)
    assert r.ok is True
    assert r.severity == "ok"


def test_flag_downstream_risk_llama_community_high_mau():
    """Llama community license + commercial use > 700M MAU is the canonical risk."""
    from soup_cli.utils.license_advisor import flag_downstream_risk

    r = flag_downstream_risk(
        license_id="llama-3", target="b2c", monthly_active_users=800_000_000
    )
    assert r.ok is False
    assert r.severity in ("warn", "block")


def test_flag_downstream_risk_llama_community_low_mau_warns():
    """Llama community on small B2C: warning, not block."""
    from soup_cli.utils.license_advisor import flag_downstream_risk

    r = flag_downstream_risk(
        license_id="llama-3", target="b2c", monthly_active_users=10_000
    )
    # Either OK with note or a warn — not a hard block
    assert r.severity in ("ok", "warn")


def test_flag_downstream_risk_nc_b2c_blocked():
    from soup_cli.utils.license_advisor import flag_downstream_risk

    r = flag_downstream_risk(
        license_id="cc-by-nc-4.0", target="b2c", monthly_active_users=100
    )
    assert r.ok is False


def test_flag_downstream_risk_rejects_negative_mau():
    from soup_cli.utils.license_advisor import flag_downstream_risk

    with pytest.raises(ValueError, match="monthly_active_users"):
        flag_downstream_risk(
            license_id="apache-2.0", target="b2c", monthly_active_users=-1
        )


def test_flag_downstream_risk_rejects_bool_mau():
    from soup_cli.utils.license_advisor import flag_downstream_risk

    with pytest.raises(TypeError, match="bool"):
        flag_downstream_risk(
            license_id="apache-2.0", target="b2c",
            monthly_active_users=True,  # type: ignore[arg-type]
        )


def test_flag_downstream_risk_unknown_license_warns():
    from soup_cli.utils.license_advisor import flag_downstream_risk

    r = flag_downstream_risk(
        license_id="unknown-soup-test-license", target="b2c", monthly_active_users=1
    )
    # Unknown license should not block silently — surface as a warn
    assert r.severity in ("warn", "block")


# ---------------------------------------------------------------------------
# CLI smoke
# ---------------------------------------------------------------------------


def test_cli_license_advisor_help():
    from soup_cli.cli import app

    result = runner.invoke(app, ["license-advisor", "--help"])
    assert result.exit_code == 0, (result.output, repr(result.exception))


def test_cli_license_advisor_b2c():
    from soup_cli.cli import app

    result = runner.invoke(app, ["license-advisor", "--target", "b2c"])
    assert result.exit_code == 0, (result.output, repr(result.exception))


def test_cli_license_advisor_unknown_target():
    from soup_cli.cli import app

    result = runner.invoke(app, ["license-advisor", "--target", "bogus"])
    assert result.exit_code != 0


def test_cli_license_advisor_check_license():
    """--license is the per-license-id risk-check mode."""
    from soup_cli.cli import app

    result = runner.invoke(
        app,
        [
            "license-advisor",
            "--target",
            "b2c",
            "--license",
            "apache-2.0",
            "--monthly-active-users",
            "10000",
        ],
    )
    assert result.exit_code == 0, (result.output, repr(result.exception))


def test_cli_license_advisor_check_llama_block():
    from soup_cli.cli import app

    result = runner.invoke(
        app,
        [
            "license-advisor",
            "--target",
            "b2c",
            "--license",
            "llama-3",
            "--monthly-active-users",
            "800000000",
        ],
    )
    # Either exits non-zero on block, OR prints a block message
    assert result.exit_code != 0 or "block" in result.output.lower()


# ---------------------------------------------------------------------------
# Source-wiring regression
# ---------------------------------------------------------------------------


def test_no_heavy_top_level_imports():
    from pathlib import Path

    src = Path(__file__).resolve().parent.parent / "soup_cli" / "utils" / "license_advisor.py"
    text = src.read_text(encoding="utf-8")
    import re
    for bad in ["^import torch", "^from torch", "^import transformers", "^from transformers"]:
        assert not re.search(bad, text, re.MULTILINE)
