# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
"""Unit tests for the CLI registry commands."""

from __future__ import annotations

import os
import subprocess
import sys
from pathlib import Path

import pytest

from airbyte_ops_mcp.registry import (
    ConnectorMetadata,
    ConnectorPublishResult,
    is_release_candidate,
    strip_rc_suffix,
)


@pytest.mark.unit
@pytest.mark.parametrize(
    "version,expected",
    [
        pytest.param("1.2.3-rc.1", True, id="valid_rc_version"),
        pytest.param("1.2.3-rc.10", True, id="valid_rc_version_double_digit"),
        pytest.param("0.0.1-rc.1", True, id="valid_rc_version_zero"),
        pytest.param("1.2.3", False, id="stable_version"),
        pytest.param("1.2.3-preview.abc123", False, id="preview_version"),
        pytest.param("1.2.3-alpha.1", False, id="alpha_version"),
        pytest.param("1.2.3-beta.1", False, id="beta_version"),
    ],
)
def test_is_release_candidate(version: str, expected: bool) -> None:
    """Test release candidate version detection."""
    assert is_release_candidate(version) == expected


@pytest.mark.unit
@pytest.mark.parametrize(
    "version,expected",
    [
        pytest.param("1.2.3-rc.1", "1.2.3", id="rc_version"),
        pytest.param("1.2.3-rc.10", "1.2.3", id="rc_version_double_digit"),
        pytest.param("0.0.1-rc.1", "0.0.1", id="rc_version_zero"),
        pytest.param("1.2.3", "1.2.3", id="stable_version_unchanged"),
    ],
)
def test_strip_rc_suffix(version: str, expected: str) -> None:
    """Test stripping release candidate suffix from version."""
    assert strip_rc_suffix(version) == expected


@pytest.mark.unit
def test_connector_metadata_model() -> None:
    """Test ConnectorMetadata Pydantic model."""
    metadata = ConnectorMetadata(
        name="source-github",
        docker_repository="airbyte/source-github",
        docker_image_tag="1.2.3-rc.1",
        support_level="certified",
        definition_id="abc123",
    )
    assert metadata.name == "source-github"
    assert metadata.docker_repository == "airbyte/source-github"
    assert metadata.docker_image_tag == "1.2.3-rc.1"
    assert metadata.support_level == "certified"
    assert metadata.definition_id == "abc123"


@pytest.mark.unit
def test_connector_publish_result_model() -> None:
    """Test ConnectorPublishResult Pydantic model."""
    result = ConnectorPublishResult(
        connector="source-github",
        version="1.2.3",
        action="apply-version-override",
        status="success",
        docker_image="airbyte/source-github:1.2.3",
        registry_updated=True,
        message="Applied version override",
    )
    assert result.connector == "source-github"
    assert result.version == "1.2.3"
    assert result.action == "apply-version-override"
    assert result.status == "success"
    assert result.docker_image == "airbyte/source-github:1.2.3"
    assert result.registry_updated is True
    assert result.message == "Applied version override"


def run_cli(
    *args: str, cwd: str | Path | None = None
) -> subprocess.CompletedProcess[str]:
    """Run the airbyte-ops CLI with the given arguments."""
    cmd = [sys.executable, "-m", "airbyte_ops_mcp.cli.app", *args]
    return subprocess.run(
        cmd,
        capture_output=True,
        text=True,
        cwd=cwd,
        env={**os.environ, "PYTHONPATH": str(Path(__file__).parent.parent / "src")},
    )


@pytest.mark.unit
def test_cli_help() -> None:
    """Test CLI help output."""
    result = run_cli("--help")
    assert result.returncode == 0
    assert "airbyte-ops" in result.stdout.lower()
    assert "registry" in result.stdout


@pytest.mark.unit
@pytest.mark.parametrize(
    "base_version,sha,expected",
    [
        pytest.param(
            "1.2.3", "abcdef1234567890", "1.2.3-preview.abcdef1", id="standard"
        ),
        pytest.param("0.1.0", "1234567", "0.1.0-preview.1234567", id="short_sha"),
        pytest.param("0.6.0", "a6370d9275", "0.6.0-preview.a6370d9", id="real_world"),
    ],
)
def test_registry_connector_version_next_with_base_version(
    base_version: str, sha: str, expected: str
) -> None:
    """Test connector-version next CLI command with explicit base-version."""
    result = run_cli(
        "registry",
        "connector-version",
        "next",
        "--name",
        "source-test",  # Connector name is required but not used when base-version is provided
        "--sha",
        sha,
        "--base-version",
        base_version,
    )
    assert result.returncode == 0
    assert result.stdout.strip() == expected


@pytest.mark.unit
def test_registry_connector_version_next_help() -> None:
    """Test connector-version next help output."""
    result = run_cli("registry", "connector-version", "next", "--help")
    assert result.returncode == 0
    assert "name" in result.stdout
    assert "sha" in result.stdout
    assert "base-version" in result.stdout


@pytest.mark.unit
def test_registry_connector_version_next_missing_name() -> None:
    """Test connector-version next fails without name."""
    result = run_cli(
        "registry",
        "connector-version",
        "next",
        "--sha",
        "abcdef1",
    )
    assert result.returncode != 0


@pytest.mark.unit
def test_registry_help() -> None:
    """Test registry subcommand help output."""
    result = run_cli("registry", "--help")
    assert result.returncode == 0
    # Check for command groups
    assert "rc" in result.stdout
    assert "connector" in result.stdout
    assert "connector-version" in result.stdout
    assert "mirror" in result.stdout


@pytest.mark.unit
def test_registry_rc_promote_help() -> None:
    """Test registry rc promote help output."""
    result = run_cli("registry", "rc", "promote", "--help")
    assert result.returncode == 0
    assert "repo-path" in result.stdout.lower()
    assert "name" in result.stdout.lower()
    assert "dry-run" in result.stdout


@pytest.mark.unit
def test_registry_rc_rollback_help() -> None:
    """Test registry rc rollback help output."""
    result = run_cli("registry", "rc", "rollback", "--help")
    assert result.returncode == 0
    assert "repo-path" in result.stdout.lower()
    assert "name" in result.stdout.lower()
    assert "dry-run" in result.stdout


@pytest.mark.unit
def test_registry_rc_promote_missing_required_options() -> None:
    """Test that missing required options causes an error."""
    result = run_cli("registry", "rc", "promote")
    assert result.returncode != 0


@pytest.mark.unit
def test_registry_rc_rollback_missing_required_options() -> None:
    """Test that missing required options causes an error."""
    result = run_cli("registry", "rc", "rollback")
    assert result.returncode != 0


@pytest.mark.unit
def test_registry_rc_promote_dry_run(tmp_path: Path) -> None:
    """Test RC promote with dry-run."""
    connector_dir = tmp_path / "airbyte-integrations" / "connectors" / "source-test"
    connector_dir.mkdir(parents=True)
    (connector_dir / "metadata.yaml").write_text(
        "data:\n  dockerRepository: airbyte/source-test\n  dockerImageTag: 1.0.0-rc.1\n"
    )
    result = run_cli(
        "registry",
        "rc",
        "promote",
        "--name",
        "source-test",
        "--repo-path",
        str(tmp_path),
        "--dry-run",
        cwd=tmp_path,
    )
    assert result.returncode == 0
    assert "dry-run" in result.stdout
    assert "source-test" in result.stdout
    assert "1.0.0" in result.stdout


@pytest.mark.unit
def test_registry_rc_promote_non_rc_version(
    tmp_path: Path,
) -> None:
    """Test RC promote fails for non-RC version."""
    connector_dir = tmp_path / "airbyte-integrations" / "connectors" / "source-test"
    connector_dir.mkdir(parents=True)
    (connector_dir / "metadata.yaml").write_text(
        "data:\n  dockerRepository: airbyte/source-test\n  dockerImageTag: 1.0.0\n"
    )
    result = run_cli(
        "registry",
        "rc",
        "promote",
        "--name",
        "source-test",
        "--repo-path",
        str(tmp_path),
        cwd=tmp_path,
    )
    assert result.returncode == 1
    assert "failure" in result.stdout
    assert "not a release candidate" in result.stdout


@pytest.mark.unit
def test_registry_rc_rollback_dry_run(tmp_path: Path) -> None:
    """Test RC rollback with dry-run."""
    connector_dir = tmp_path / "airbyte-integrations" / "connectors" / "source-test"
    connector_dir.mkdir(parents=True)
    (connector_dir / "metadata.yaml").write_text(
        "data:\n  dockerRepository: airbyte/source-test\n  dockerImageTag: 1.0.0-rc.1\n"
    )
    result = run_cli(
        "registry",
        "rc",
        "rollback",
        "--name",
        "source-test",
        "--repo-path",
        str(tmp_path),
        "--dry-run",
        cwd=tmp_path,
    )
    assert result.returncode == 0
    assert "dry-run" in result.stdout
    assert "source-test" in result.stdout


@pytest.mark.unit
def test_registry_rc_rollback_non_rc_version(
    tmp_path: Path,
) -> None:
    """Test RC rollback fails for non-RC version."""
    connector_dir = tmp_path / "airbyte-integrations" / "connectors" / "source-test"
    connector_dir.mkdir(parents=True)
    (connector_dir / "metadata.yaml").write_text(
        "data:\n  dockerRepository: airbyte/source-test\n  dockerImageTag: 1.0.0\n"
    )
    result = run_cli(
        "registry",
        "rc",
        "rollback",
        "--name",
        "source-test",
        "--repo-path",
        str(tmp_path),
        cwd=tmp_path,
    )
    assert result.returncode == 1
    assert "failure" in result.stdout
    assert "not a release candidate" in result.stdout


@pytest.mark.unit
def test_registry_rc_promote_connector_not_found(tmp_path: Path) -> None:
    """Test error when connector directory doesn't exist."""
    connectors_dir = tmp_path / "airbyte-integrations" / "connectors"
    connectors_dir.mkdir(parents=True)
    result = run_cli(
        "registry",
        "rc",
        "promote",
        "--name",
        "source-nonexistent",
        "--repo-path",
        str(tmp_path),
        cwd=tmp_path,
    )
    assert result.returncode != 0
    assert "not found" in result.stdout.lower() or "not found" in result.stderr.lower()


@pytest.mark.unit
def test_dockerhub_help() -> None:
    """Test dockerhub subcommand help output."""
    result = run_cli("dockerhub", "--help")
    assert result.returncode == 0
    assert "inspect-image" in result.stdout


@pytest.mark.unit
def test_dockerhub_inspect_image_help() -> None:
    """Test dockerhub inspect-image help output."""
    result = run_cli("dockerhub", "inspect-image", "--help")
    assert result.returncode == 0
    assert "image" in result.stdout.lower()
    assert "tag" in result.stdout.lower()


@pytest.mark.unit
def test_registry_artifacts_generate_help() -> None:
    """Test registry connector-version artifacts generate help output."""
    result = run_cli("registry", "connector-version", "artifacts", "generate", "--help")
    assert result.returncode == 0
    assert "generate" in result.stdout.lower()


@pytest.mark.unit
def test_registry_artifacts_publish_help() -> None:
    """Test registry connector-version artifacts publish help output."""
    result = run_cli("registry", "connector-version", "artifacts", "publish", "--help")
    assert result.returncode == 0
    assert "publish" in result.stdout.lower()


@pytest.mark.unit
def test_registry_connector_version_yank_help() -> None:
    """Test registry connector-version yank help output."""
    result = run_cli("registry", "connector-version", "yank", "--help")
    assert result.returncode == 0
    assert "yank" in result.stdout.lower()


@pytest.mark.unit
def test_registry_connector_version_unyank_help() -> None:
    """Test registry connector-version unyank help output."""
    result = run_cli("registry", "connector-version", "unyank", "--help")
    assert result.returncode == 0
    assert "unyank" in result.stdout.lower()


@pytest.mark.unit
def test_registry_store_mirror_help() -> None:
    """Test registry store mirror help output."""
    result = run_cli("registry", "store", "mirror", "--help")
    assert result.returncode == 0
    assert "mirror" in result.stdout.lower()


@pytest.mark.unit
def test_registry_store_compile_help() -> None:
    """Test registry store compile help output."""
    result = run_cli("registry", "store", "compile", "--help")
    assert result.returncode == 0
    assert "compile" in result.stdout.lower()


@pytest.mark.unit
def test_registry_connector_list_help() -> None:
    """Test registry connector list help output."""
    result = run_cli("registry", "connector", "list", "--help")
    assert result.returncode == 0
    assert "list" in result.stdout.lower()


@pytest.mark.unit
def test_registry_connector_version_list_help() -> None:
    """Test registry connector-version list help output."""
    result = run_cli("registry", "connector-version", "list", "--help")
    assert result.returncode == 0
    assert "list" in result.stdout.lower()


@pytest.mark.unit
def test_registry_connector_version_metadata_get_help() -> None:
    """Test registry connector-version metadata get help output."""
    result = run_cli("registry", "connector-version", "metadata", "get", "--help")
    assert result.returncode == 0
    assert "metadata" in result.stdout.lower() or "get" in result.stdout.lower()
