"""Tests for fix-symlinks command."""

import os

import pytest
from click.testing import CliRunner

from debrepomanager.cli import cli
from debrepomanager.config import Config


@pytest.fixture
def runner():
    """Create Click test runner."""
    return CliRunner()


@pytest.fixture
def mock_config(tmp_path):
    """Create mock config with temporary paths."""
    config = Config()
    config._config["aptly"]["publish_base"] = str(tmp_path / "public")
    config._config["repositories"]["dual_format"]["enabled"] = True
    config._config["repositories"]["dual_format"]["auto_symlink"] = True
    return config


def test_fix_symlinks_no_repos(runner, mock_config, tmp_path, mocker):
    """Test fix-symlinks with no published repositories."""
    publish_base = tmp_path / "public"
    publish_base.mkdir(parents=True)

    mocker.patch("debrepomanager.cli.Config", return_value=mock_config)
    mocker.patch("debrepomanager.cli.AptlyManager")

    result = runner.invoke(cli, ["fix-symlinks"])

    assert result.exit_code == 0
    assert "No published repositories found" in result.output


def test_fix_symlinks_check_only(runner, mock_config, tmp_path, mocker):
    """Test fix-symlinks with --check-only flag."""
    publish_base = tmp_path / "public"
    publish_base.mkdir(parents=True)

    # Create test repository structure (aptly format when publish_prefix="")
    codename = "trixie"
    component = "main"
    repo_path = publish_base / codename / "dists" / component
    repo_path.mkdir(parents=True)
    (repo_path / "Release").touch()

    mocker.patch("debrepomanager.cli.Config", return_value=mock_config)
    mocker.patch("debrepomanager.cli.AptlyManager")

    result = runner.invoke(cli, ["fix-symlinks", "--check-only"])

    assert result.exit_code == 1  # Should fail because symlink missing
    assert "symlink missing" in result.output
    assert "Need fixing: 1" in result.output
    assert "Run without --check-only to fix" in result.output


def test_fix_symlinks_creates_symlinks(runner, mock_config, tmp_path, mocker):
    """Test fix-symlinks creates missing symlinks."""
    publish_base = tmp_path / "public"
    publish_base.mkdir(parents=True)

    # Create test repository structure (aptly format when publish_prefix="")
    codename = "trixie"
    component = "main"
    repo_path = publish_base / codename / "dists" / component
    repo_path.mkdir(parents=True)
    (repo_path / "Release").touch()

    mocker.patch("debrepomanager.cli.Config", return_value=mock_config)
    mocker.patch("debrepomanager.cli.AptlyManager")

    result = runner.invoke(cli, ["fix-symlinks"])

    assert result.exit_code == 0
    assert "symlink created" in result.output
    assert "Created: 1" in result.output

    # Check symlink was actually created
    symlink_path = publish_base / "dists" / codename / component
    assert symlink_path.is_symlink()
    assert symlink_path.resolve() == repo_path.resolve()


def test_fix_symlinks_validates_existing(runner, mock_config, tmp_path, mocker):
    """Test fix-symlinks validates existing symlinks."""
    publish_base = tmp_path / "public"
    publish_base.mkdir(parents=True)

    # Create test repository structure (aptly format when publish_prefix="")
    codename = "trixie"
    component = "main"
    repo_path = publish_base / codename / "dists" / component
    repo_path.mkdir(parents=True)
    (repo_path / "Release").touch()

    # Create correct symlink
    symlink_path = publish_base / "dists" / codename / component
    symlink_path.parent.mkdir(parents=True, exist_ok=True)
    rel_path = os.path.relpath(repo_path, symlink_path.parent)
    os.symlink(rel_path, symlink_path)

    mocker.patch("debrepomanager.cli.Config", return_value=mock_config)
    mocker.patch("debrepomanager.cli.AptlyManager")

    result = runner.invoke(cli, ["fix-symlinks"])

    assert result.exit_code == 0
    assert "symlink OK" in result.output
    assert "OK: 1" in result.output


def test_fix_symlinks_fixes_wrong_target(runner, mock_config, tmp_path, mocker):
    """Test fix-symlinks fixes symlinks with wrong target."""
    publish_base = tmp_path / "public"
    publish_base.mkdir(parents=True)

    # Create test repository structure (aptly format when publish_prefix="")
    codename = "trixie"
    component = "main"
    repo_path = publish_base / codename / "dists" / component
    repo_path.mkdir(parents=True)
    (repo_path / "Release").touch()

    # Create wrong symlink (point to existing but wrong directory)
    wrong_target = publish_base / "wrong"
    wrong_target.mkdir()
    symlink_path = publish_base / "dists" / codename / component
    symlink_path.parent.mkdir(parents=True, exist_ok=True)
    rel_wrong = os.path.relpath(wrong_target, symlink_path.parent)
    os.symlink(rel_wrong, symlink_path)

    mocker.patch("debrepomanager.cli.Config", return_value=mock_config)
    mocker.patch("debrepomanager.cli.AptlyManager")

    result = runner.invoke(cli, ["fix-symlinks"])

    assert result.exit_code == 0
    assert "symlink updated" in result.output
    assert "Updated: 1" in result.output

    # Check symlink was fixed
    assert symlink_path.is_symlink()
    # Check target is correct (compare relative paths)
    current_target = os.readlink(symlink_path)
    expected_target = os.path.relpath(repo_path, symlink_path.parent)
    assert current_target == expected_target


def test_fix_symlinks_specific_codename(runner, mock_config, tmp_path, mocker):
    """Test fix-symlinks with specific codename."""
    publish_base = tmp_path / "public"
    publish_base.mkdir(parents=True)

    # Create two codenames (aptly format when publish_prefix="")
    for codename in ["trixie", "bookworm"]:
        repo_path = publish_base / codename / "dists" / "main"
        repo_path.mkdir(parents=True)
        (repo_path / "Release").touch()

    mocker.patch("debrepomanager.cli.Config", return_value=mock_config)
    mocker.patch("debrepomanager.cli.AptlyManager")

    result = runner.invoke(cli, ["fix-symlinks", "--codename", "trixie"])

    assert result.exit_code == 0
    assert "trixie/main" in result.output
    assert "bookworm" not in result.output
    assert "Total repositories: 1" in result.output


def test_fix_symlinks_specific_component(runner, mock_config, tmp_path, mocker):
    """Test fix-symlinks with specific codename and component."""
    publish_base = tmp_path / "public"
    publish_base.mkdir(parents=True)

    # Create multiple components (aptly format when publish_prefix="")
    codename = "trixie"
    for component in ["main", "contrib"]:
        repo_path = publish_base / codename / "dists" / component
        repo_path.mkdir(parents=True)
        (repo_path / "Release").touch()

    mocker.patch("debrepomanager.cli.Config", return_value=mock_config)
    mocker.patch("debrepomanager.cli.AptlyManager")

    result = runner.invoke(
        cli, ["fix-symlinks", "--codename", "trixie", "--component", "main"]
    )

    assert result.exit_code == 0
    assert "trixie/main" in result.output
    assert "contrib" not in result.output
    assert "Total repositories: 1" in result.output


def test_fix_symlinks_dual_format_disabled(runner, mock_config, tmp_path, mocker):
    """Test fix-symlinks when dual format is disabled."""
    mock_config._config["repositories"]["dual_format"]["enabled"] = False

    mocker.patch("debrepomanager.cli.Config", return_value=mock_config)
    mocker.patch("debrepomanager.cli.AptlyManager")

    result = runner.invoke(cli, ["fix-symlinks"])

    assert result.exit_code == 1
    assert "Dual format support is disabled" in result.output
    assert "To enable, add to config.yaml" in result.output


def test_fix_symlinks_publish_base_missing(runner, mock_config, tmp_path, mocker):
    """Test fix-symlinks when publish base doesn't exist."""
    mock_config._config["aptly"]["publish_base"] = "/nonexistent/path"

    mocker.patch("debrepomanager.cli.Config", return_value=mock_config)
    mocker.patch("debrepomanager.cli.AptlyManager")

    result = runner.invoke(cli, ["fix-symlinks"])

    assert result.exit_code == 1
    assert "Publish base doesn't exist" in result.output


def test_fix_symlinks_skips_dists_directory(runner, mock_config, tmp_path, mocker):
    """Test fix-symlinks skips the dists directory itself."""
    publish_base = tmp_path / "public"
    publish_base.mkdir(parents=True)

    # Create dists directory (which should be skipped)
    (publish_base / "dists").mkdir()

    # Create real repo (aptly format when publish_prefix="")
    repo_path = publish_base / "trixie" / "dists" / "main"
    repo_path.mkdir(parents=True)
    (repo_path / "Release").touch()

    mocker.patch("debrepomanager.cli.Config", return_value=mock_config)
    mocker.patch("debrepomanager.cli.AptlyManager")

    result = runner.invoke(cli, ["fix-symlinks"])

    assert result.exit_code == 0
    assert "Total repositories: 1" in result.output  # Only trixie/main


def test_fix_symlinks_handles_non_symlink_path(runner, mock_config, tmp_path, mocker):
    """Test fix-symlinks when old path exists but is not a symlink."""
    publish_base = tmp_path / "public"
    publish_base.mkdir(parents=True)

    # Create test repository structure (aptly format when publish_prefix="")
    codename = "trixie"
    component = "main"
    repo_path = publish_base / codename / "dists" / component
    repo_path.mkdir(parents=True)
    (repo_path / "Release").touch()

    # Create regular directory at symlink location
    wrong_path = publish_base / "dists" / codename / component
    wrong_path.mkdir(parents=True)

    mocker.patch("debrepomanager.cli.Config", return_value=mock_config)
    mocker.patch("debrepomanager.cli.AptlyManager")

    result = runner.invoke(cli, ["fix-symlinks"])

    assert result.exit_code == 1
    assert "path exists (not symlink)" in result.output
    assert "Errors: 1" in result.output
