from __future__ import annotations

from pathlib import Path

import yaml

ROOT = Path(__file__).resolve().parents[2]


def test_docker_workflow_is_manual_only():
    workflow = (ROOT / ".github" / "workflows" / "docker.yml").read_text(encoding="utf-8")
    parsed = yaml.load(workflow, Loader=yaml.BaseLoader)

    assert set(parsed["on"]) == {"workflow_dispatch"}
    for path in (ROOT / ".github" / "workflows").glob("*.yml"):
        if path.name == "docker.yml":
            continue
        contents = path.read_text(encoding="utf-8")
        assert "docker/build-push-action" not in contents
        assert "docker build" not in contents


def test_docker_build_inputs_are_not_excluded_from_context():
    dockerignore = (ROOT / ".dockerignore").read_text(encoding="utf-8").splitlines()
    active_patterns = {
        line.strip() for line in dockerignore if line.strip() and not line.lstrip().startswith("#")
    }

    assert "docker/" not in active_patterns
    assert "envs/*.yml" not in active_patterns
    assert "integrations/" not in active_patterns


def test_conda_mirror_maps_defaults_to_existing_repositories():
    condarc = yaml.safe_load((ROOT / "docker" / ".condarc").read_text(encoding="utf-8"))

    assert "defaults" not in condarc["custom_channels"]
    assert condarc["default_channels"] == [
        "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main",
        "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r",
    ]


def test_non_push_docker_build_has_the_local_smoke_test_tag():
    workflow = (ROOT / ".github" / "workflows" / "docker.yml").read_text(encoding="utf-8")

    assert "tags: ${{ matrix.image-name }}:latest" in workflow
    assert "docker run --rm ${{ matrix.image-name }}:latest list-types" in workflow


def test_manual_docker_workflow_exposes_build_and_publish_controls():
    workflow = (ROOT / ".github" / "workflows" / "docker.yml").read_text(encoding="utf-8")
    parsed = yaml.load(workflow, Loader=yaml.BaseLoader)
    inputs = parsed["on"]["workflow_dispatch"]["inputs"]
    options = inputs["plugin"]["options"]
    matrix_plugins = [
        entry["plugin"] for entry in parsed["jobs"]["build"]["strategy"]["matrix"]["include"]
    ]

    assert set(inputs) == {"plugin", "push", "push_to_dockerhub"}
    assert options == ["all", *matrix_plugins]
    assert "inputs.plugin == 'all' || inputs.plugin == matrix.plugin" in workflow


def test_registry_publish_requires_a_version_tag_after_local_smoke():
    workflow = (ROOT / ".github" / "workflows" / "docker.yml").read_text(encoding="utf-8")

    assert 'if [[ "${{ github.ref }}" != refs/tags/v* ]]; then' in workflow
    assert "Publishing requires a manually selected v* tag ref." in workflow
    assert workflow.index("- name: Build validation image") < workflow.index(
        "- name: Smoke-test validation image"
    )
    assert workflow.index("- name: Smoke-test validation image") < workflow.index(
        "- name: Build and push registry image"
    )


def test_python_ci_validates_docker_compose_configuration():
    workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8")

    assert "docker compose -f docker/docker-compose.yml config --quiet" in workflow


def test_python_ci_certifies_native_linux_arm64_without_macos_runners():
    workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8")
    parsed = yaml.load(workflow, Loader=yaml.BaseLoader)
    arm_job = parsed["jobs"]["linux-arm64"]
    arm_steps = "\n".join(
        str(step.get("run", "")) for step in arm_job["steps"] if isinstance(step, dict)
    )

    assert arm_job["runs-on"] == "ubuntu-24.04-arm"
    assert "platform.machine()" in arm_steps
    assert "python -m pytest tests/ src/abi/sciplot/tests/" in arm_steps
    assert "python -m build" in arm_steps
    assert "verify_linux_wheel_capabilities.py" in arm_steps
    assert '--mamba-root "/tmp/abi ci arm managed root"' in arm_steps
    assert "abi env install" in arm_steps
    assert "--dry-run" not in arm_steps
    assert 'cd "$wheel_smoke_dir"' in arm_steps
    assert "linux-arm64" in parsed["jobs"]["migration-gate"]["needs"]
    assert workflow.count("abi env install") >= 2
    assert workflow.count('cd "$wheel_smoke_dir"') >= 2
    assert workflow.count('test -f "$spec_path"') >= 2
    assert "macos-" not in workflow.lower()


def test_wheel_ci_validates_packaged_linux_capability_matrix_and_fail_closed_policy():
    workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8")

    assert workflow.count("verify_linux_wheel_capabilities.py") == 2
    assert '--architecture "x86_64"' in workflow
    assert '--architecture "aarch64"' in workflow
    assert "abi env doctor --type viral_viwrap" not in workflow
    assert '"allowed_statuses"] == ["certified", "partial", "unsupported"]' not in workflow


def test_x86_ci_builds_and_installs_wheel_on_every_supported_python():
    workflow = yaml.load(
        (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8"),
        Loader=yaml.BaseLoader,
    )
    test_job = workflow["jobs"]["test"]
    steps = {step.get("name"): step for step in test_job["steps"] if isinstance(step, dict)}

    build = steps["Build distribution on Python ${{ matrix.python-version }}"]
    smoke = steps["Smoke-test wheel on Python ${{ matrix.python-version }}"]
    assert "if" not in build
    assert "if" not in smoke
    assert "python -m build" in build["run"]
    assert 'python -m pip install --force-reinstall "${wheels[0]}[mcp]"' in smoke["run"]
    assert "verify_linux_wheel_capabilities.py" in smoke["run"]


def test_local_docker_export_disables_registry_attestations():
    workflow = (ROOT / ".github" / "workflows" / "docker.yml").read_text(encoding="utf-8")

    validation_step = workflow.split("- name: Build validation image", maxsplit=1)[1].split(
        "- name: Smoke-test validation image", maxsplit=1
    )[0]
    publish_step = workflow.split("- name: Build and push registry image", maxsplit=1)[1]

    assert "push: false" in validation_step
    assert "load: true" in validation_step
    assert "provenance: false" in validation_step
    assert "sbom: false" in validation_step
    assert "push: true" in publish_step
    assert "provenance: mode=max" in publish_step
    assert "sbom: true" in publish_step


def test_rnaseq_registry_build_is_amd64_only_until_arm64_is_validated():
    workflow = (ROOT / ".github" / "workflows" / "docker.yml").read_text(encoding="utf-8")

    assert "matrix.plugin != 'rnaseq' && 'linux/amd64,linux/arm64' || 'linux/amd64'" in workflow


def test_sdist_contains_files_forced_into_the_wheel():
    pyproject = (ROOT / "pyproject.toml").read_text(encoding="utf-8")
    sdist_section = pyproject.split("[tool.hatch.build.targets.sdist]", maxsplit=1)[1]
    force_include_header = "[tool.hatch.build.targets.sdist.force-include]"
    sdist_section = sdist_section.split(force_include_header, maxsplit=1)[0]

    assert '"environments.yaml"' in sdist_section
    assert '"integrations"' in sdist_section


def test_sciplot_tests_are_excluded_from_wheel():
    pyproject = (ROOT / "pyproject.toml").read_text(encoding="utf-8")
    wheel_section = pyproject.split("[tool.hatch.build.targets.wheel]", maxsplit=1)[1]
    wheel_section = wheel_section.split(
        "[tool.hatch.build.targets.wheel.force-include]", maxsplit=1
    )[0]

    assert 'exclude = ["src/abi/sciplot/tests/"]' in wheel_section


def test_plasmidfinder_uses_the_installed_python_module():
    registry = yaml.safe_load(
        (ROOT / "plugins" / "metagenomic_plasmid" / "tool_registry.yaml").read_text(
            encoding="utf-8"
        )
    )
    tool = next(item for item in registry["tools"] if item["id"] == "plasmidfinder")

    assert tool["executable"] == "python"
    assert tool["command_template"].startswith("python -m plasmidfinder ")
    assert "--legacy -x" in tool["command_template"]


def test_python_script_tools_use_the_canonical_autoplasm_root():
    plugin_root = ROOT / "plugins" / "metagenomic_plasmid"
    registry = yaml.safe_load((plugin_root / "tool_registry.yaml").read_text(encoding="utf-8"))

    for tool_id, relative_path in (
        ("plasme", "PLASMe/PLASMe.py"),
        ("recycler", "Recycler/bin/recycle.py"),
    ):
        tool = next(item for item in registry["tools"] if item["id"] == tool_id)
        expected_script = f"{{autoplasm_root}}/{relative_path}"
        contract = yaml.safe_load(
            (plugin_root / "tool_contracts" / f"{tool_id}.yaml").read_text(encoding="utf-8")
        )

        assert tool["script_path"] == expected_script
        expected_command_path = f"{{resource_root}}/{relative_path}"
        assert expected_command_path in tool["command_template"]
        assert expected_command_path in contract["execution"]["command_template"]


def test_every_dockerfile_copy_source_exists():
    required_sources = (
        "docker/.condarc",
        "pyproject.toml",
        "README.md",
        "environments.yaml",
        "src",
        "plugins",
        "config",
        "scripts",
        "data",
        "envs",
        "examples",
        "golden_traces",
        "integrations",
    )

    for source in required_sources:
        assert (ROOT / source).exists(), f"Docker build input is missing: {source}"


def test_every_dockerfile_copies_the_root_environment_manifest():
    dockerfiles = sorted((ROOT / "docker").glob("Dockerfile.*"))

    assert dockerfiles
    for dockerfile in dockerfiles:
        contents = dockerfile.read_text(encoding="utf-8")
        assert "COPY environments.yaml /app/" in contents, dockerfile.name
        assert "/app/environments.yaml" in contents.split("rm -rf", maxsplit=1)[1], dockerfile.name


def test_every_dockerfile_copies_packaged_agent_integrations():
    dockerfiles = sorted((ROOT / "docker").glob("Dockerfile.*"))

    assert dockerfiles
    for dockerfile in dockerfiles:
        contents = dockerfile.read_text(encoding="utf-8")
        assert "COPY integrations/ /app/integrations/" in contents, dockerfile.name
        assert "/app/integrations" in contents.split("rm -rf", maxsplit=1)[1], dockerfile.name


def test_ci_and_docker_images_install_cjk_font_for_sciplot():
    workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8")
    assert "apt-get install -y --no-install-recommends fonts-wqy-zenhei" in workflow

    dockerfiles = sorted((ROOT / "docker").glob("Dockerfile.*"))
    assert dockerfiles
    for dockerfile in dockerfiles:
        contents = dockerfile.read_text(encoding="utf-8")
        assert "apt-get install -y --no-install-recommends fonts-wqy-zenhei" in contents, (
            dockerfile.name
        )


def test_incompatible_plasmid_annotators_do_not_share_an_environment():
    manifest = yaml.safe_load((ROOT / "environments.yaml").read_text(encoding="utf-8"))
    environments = manifest["environments"]
    assignments = manifest["tool_assignments"]["metagenomic_plasmid"]

    assert assignments["bakta"] == "autoplasm-annotation"
    assert assignments["rgi"] == "autoplasm-rgi"
    assert "prokka" not in environments["autoplasm-annotation"]["dependencies"]
    assert "rgi" not in environments["autoplasm-annotation"]["dependencies"]

    dockerfile = (ROOT / "docker" / "Dockerfile.metagenomic_plasmid").read_text(encoding="utf-8")
    for env_name in ("autoplasm-annotation", "autoplasm-rgi"):
        assert f"COPY envs/{env_name}.yml " in dockerfile
        assert f"mamba env create -f /tmp/envs/{env_name}.yml" in dockerfile


def test_scapp_uses_the_cloud_verified_environment_in_plasmid_image():
    manifest = yaml.safe_load((ROOT / "environments.yaml").read_text(encoding="utf-8"))
    assignments = manifest["tool_assignments"]["metagenomic_plasmid"]
    dependencies = manifest["environments"]["autoplasm-scapp"]["dependencies"]

    assert assignments["scapp"] == "autoplasm-scapp"
    assert "scapp" not in manifest["environments"]
    assert not any(
        isinstance(dependency, dict) and "pip" in dependency for dependency in dependencies
    )
    for dependency in (
        "numpy=1.22.0",
        "scipy=1.10.0",
        "scikit-learn=0.22.2.post1",
        "pysam=0.19.1",
    ):
        assert dependency in dependencies

    dockerfile = (ROOT / "docker" / "Dockerfile.metagenomic_plasmid").read_text(encoding="utf-8")
    assert "COPY envs/autoplasm-scapp.yml " in dockerfile
    assert "mamba env create -f /tmp/envs/autoplasm-scapp.yml" in dockerfile
    assert "install_scapp_packages.sh /opt/conda/envs/autoplasm-scapp/bin/python" in dockerfile

    installer = (ROOT / "scripts" / "install_scapp_packages.sh").read_text(encoding="utf-8")
    assert "pip install --no-deps" in installer
    assert "/refs/heads/master" not in installer
    assert "plasclass-dpellow @" in installer
    assert "scapp-dpellow @" in installer

    cloud_bootstrap = (ROOT / "scripts" / "cloud" / "01_envs.sh").read_text(encoding="utf-8")
    assert "install_scapp_packages.sh" in cloud_bootstrap


def test_checkm2_isolated_from_modern_statistics_environment():
    manifest = yaml.safe_load((ROOT / "environments.yaml").read_text(encoding="utf-8"))
    environments = manifest["environments"]
    assignments = manifest["tool_assignments"]["metagenomic_plasmid"]

    assert assignments["checkm2"] == "autoplasm-checkm2"
    assert "checkm2" not in environments["stats"]["dependencies"]
    assert "python=3.8" in environments["autoplasm-checkm2"]["dependencies"]
    assert "checkm2=1.1.0=pyh7e72e81_0" in environments["autoplasm-checkm2"]["dependencies"]

    dockerfile = (ROOT / "docker" / "Dockerfile.metagenomic_plasmid").read_text(encoding="utf-8")
    assert "COPY envs/autoplasm-checkm2.yml " in dockerfile
    assert "mamba env create -f /tmp/envs/autoplasm-checkm2.yml" in dockerfile
