"""Docker-based E2E tests for installer/install.sh.

Each test runs install.sh in a fresh Ubuntu container with a local mock HTTP
server serving fake release artifacts (tarball + SHA256SUMS). This validates
the full download-verify-extract-install pipeline with real tools.

Requires Docker. Skipped automatically if Docker is not available.
"""

from __future__ import annotations

import pytest

from tests.installer.e2e.conftest import ContainerResult, skip_no_docker

pytestmark = [pytest.mark.e2e, skip_no_docker]

INSTALL_CMD = "bash /workspace/install.sh"


# ---------------------------------------------------------------------------
# Happy path — full install
# ---------------------------------------------------------------------------


class TestFullInstall:
    """Full install lifecycle tests."""

    def test_full_install_as_user_with_sudo(self, run_in_container) -> None:
        """testuser (has sudo) installs to /usr/local/bin via sudo."""
        result: ContainerResult = run_in_container(
            f"ILUM_VERSION=0.1.0 {INSTALL_CMD} && ilum --version",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0, f"Install failed:\n{result.output}"
        assert "Checksum OK" in result.output
        assert "Installed ilum" in result.output
        assert "0.1.0" in result.output

    def test_full_install_without_sudo(self, run_in_container) -> None:
        """Without sudo, falls back to ~/.local/bin."""
        # Remove sudo so the script falls back to user-local install.
        # Container is ephemeral so cleanup is unnecessary.
        result: ContainerResult = run_in_container(
            f"sudo mv /usr/bin/sudo /usr/bin/sudo.hidden && ILUM_VERSION=0.1.0 {INSTALL_CMD}",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0, f"Install failed:\n{result.output}"
        assert "Installed ilum" in result.output
        assert ".local/bin" in result.output

    def test_full_install_with_version_flag(self, run_in_container) -> None:
        """--version 0.1.0 skips GitHub API call and downloads that version."""
        result: ContainerResult = run_in_container(
            f"{INSTALL_CMD} --version 0.1.0",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0, f"Install failed:\n{result.output}"
        assert "Using version from --version flag" in result.output
        assert "Checksum OK" in result.output

    def test_full_install_with_env_var(self, run_in_container) -> None:
        """ILUM_VERSION=0.1.0 env var pins the version."""
        result: ContainerResult = run_in_container(
            f"ILUM_VERSION=0.1.0 {INSTALL_CMD}",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0, f"Install failed:\n{result.output}"
        assert "Using pinned version" in result.output
        assert "Checksum OK" in result.output


# ---------------------------------------------------------------------------
# Shell integration
# ---------------------------------------------------------------------------


class TestShellIntegration:
    """PATH setup for different shells."""

    def test_bashrc_updated_after_install(self, run_in_container) -> None:
        """After install, ~/.bashrc contains PATH entry for install dir."""
        # Use a custom INSTALL_DIR not in PATH so setup_path triggers
        result: ContainerResult = run_in_container(
            "mkdir -p /tmp/ilum-bin && "
            f"ILUM_VERSION=0.1.0 ILUM_INSTALL_DIR=/tmp/ilum-bin {INSTALL_CMD} && "
            "cat ~/.bashrc",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0, f"Install failed:\n{result.output}"
        assert "/tmp/ilum-bin" in result.output

    def test_zshrc_updated_for_zsh_user(self, run_in_container) -> None:
        """User with SHELL=/bin/zsh gets ~/.zshrc updated."""
        # Use a custom INSTALL_DIR not in PATH so setup_path triggers
        result: ContainerResult = run_in_container(
            "mkdir -p /tmp/ilum-bin && "
            f"ILUM_VERSION=0.1.0 ILUM_INSTALL_DIR=/tmp/ilum-bin {INSTALL_CMD} && "
            "cat ~/.zshrc",
            env={"SHELL": "/bin/zsh"},
        )
        assert result.exit_code == 0, f"Install failed:\n{result.output}"
        assert "/tmp/ilum-bin" in result.output

    def test_fish_config_updated(self, run_in_container) -> None:
        """User with SHELL=/usr/bin/fish gets fish config updated."""
        result: ContainerResult = run_in_container(
            "mkdir -p /tmp/ilum-bin && "
            f"ILUM_VERSION=0.1.0 ILUM_INSTALL_DIR=/tmp/ilum-bin {INSTALL_CMD} && "
            "cat ~/.config/fish/config.fish",
            env={"SHELL": "/usr/bin/fish"},
        )
        assert result.exit_code == 0, f"Install failed:\n{result.output}"
        assert "set -gx PATH" in result.output

    def test_no_path_flag_skips_rc_update(self, run_in_container) -> None:
        """--no-path prevents rc file modification."""
        result: ContainerResult = run_in_container(
            "mkdir -p /tmp/ilum-bin && "
            f"ILUM_VERSION=0.1.0 ILUM_INSTALL_DIR=/tmp/ilum-bin "
            f"{INSTALL_CMD} --no-path && "
            "test ! -f ~/.bashrc || "
            "! grep -q 'ilum-bin' ~/.bashrc",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0, f"Failed:\n{result.output}"


# ---------------------------------------------------------------------------
# Checksum & security
# ---------------------------------------------------------------------------


class TestChecksumSecurity:
    """Checksum verification tests."""

    def test_checksum_verification_passes(self, run_in_container) -> None:
        """Valid tarball + matching SHA256SUMS: 'Checksum OK' in output."""
        result: ContainerResult = run_in_container(
            f"ILUM_VERSION=0.1.0 {INSTALL_CMD}",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0
        assert "Checksum OK" in result.output

    def test_bad_checksum_aborts(self, run_in_container_bad_checksum) -> None:
        """Tampered SHA256SUMS causes install to abort."""
        result: ContainerResult = run_in_container_bad_checksum(
            f"ILUM_VERSION=0.1.0 {INSTALL_CMD}",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code != 0
        assert "Checksum verification failed" in result.output or "FAILED" in result.output

    def test_missing_tarball_aborts(self, run_in_container_no_tarball) -> None:
        """SHA256SUMS present but tarball missing causes error."""
        result: ContainerResult = run_in_container_no_tarball(
            f"ILUM_VERSION=0.1.0 {INSTALL_CMD}",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code != 0


# ---------------------------------------------------------------------------
# Pipe mode — curl | bash simulation
# ---------------------------------------------------------------------------


class TestPipeMode:
    """Simulates piped execution (stdin is not a terminal)."""

    def test_pipe_mode_no_ansi(self, run_in_container) -> None:
        """When run via pipe, no ANSI escape codes in output."""
        result: ContainerResult = run_in_container(
            "echo '' | ILUM_VERSION=0.1.0 bash /workspace/install.sh",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0
        # ANSI escape codes start with \033[
        assert "\033[" not in result.stdout

    def test_pipe_mode_succeeds(self, run_in_container) -> None:
        """Piped execution completes successfully."""
        result: ContainerResult = run_in_container(
            "echo '' | ILUM_VERSION=0.1.0 bash /workspace/install.sh",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0
        assert "Installed ilum" in result.output


# ---------------------------------------------------------------------------
# Argument handling
# ---------------------------------------------------------------------------


class TestArguments:
    """CLI argument parsing tests."""

    def test_help_flag_in_container(self, run_in_container) -> None:
        """--help prints usage and exits 0."""
        result: ContainerResult = run_in_container(f"{INSTALL_CMD} --help")
        assert result.exit_code == 0
        assert "Usage" in result.output

    def test_unknown_flag_error(self, run_in_container) -> None:
        """Unknown flag causes error exit."""
        result: ContainerResult = run_in_container(f"{INSTALL_CMD} --bogus")
        assert result.exit_code != 0
        assert "Unknown argument" in result.output


# ---------------------------------------------------------------------------
# Fallback behavior
# ---------------------------------------------------------------------------


class TestFallbacks:
    """Download tool fallback tests."""

    def test_wget_fallback_when_no_curl(self, run_in_container) -> None:
        """With curl removed from PATH, wget is used as fallback."""
        # Rename curl so `command -v curl` fails, forcing wget fallback
        result: ContainerResult = run_in_container(
            "sudo mv /usr/bin/curl /usr/bin/curl.bak && "
            f"ILUM_VERSION=0.1.0 {INSTALL_CMD} ; "
            "code=$? ; "
            "sudo mv /usr/bin/curl.bak /usr/bin/curl ; "
            "exit $code",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0, f"wget fallback failed:\n{result.output}"
        assert "Installed ilum" in result.output

    def test_no_curl_no_wget_dies(self, run_in_container) -> None:
        """With both curl and wget removed, script dies."""
        result: ContainerResult = run_in_container(
            "sudo mv /usr/bin/curl /usr/bin/curl.bak && "
            "sudo mv /usr/bin/wget /usr/bin/wget.bak && "
            f"ILUM_VERSION=0.1.0 {INSTALL_CMD} ; "
            "code=$? ; "
            "sudo mv /usr/bin/curl.bak /usr/bin/curl ; "
            "sudo mv /usr/bin/wget.bak /usr/bin/wget ; "
            "exit $code",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code != 0
        assert "Neither curl nor wget" in result.output


# ---------------------------------------------------------------------------
# Idempotency & re-install
# ---------------------------------------------------------------------------


class TestIdempotency:
    """Re-installation and overwrite tests."""

    def test_reinstall_over_existing(self, run_in_container) -> None:
        """Running install twice succeeds; no duplicate PATH entries."""
        # Hide sudo to trigger fallback path + PATH setup in .bashrc
        result: ContainerResult = run_in_container(
            "sudo mv /usr/bin/sudo /usr/bin/sudo.hidden && "
            f"ILUM_VERSION=0.1.0 {INSTALL_CMD} && "
            f"ILUM_VERSION=0.1.0 {INSTALL_CMD} && "
            "cat ~/.bashrc",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0, f"Reinstall failed:\n{result.output}"
        # Count .local/bin PATH entries — idempotency check should prevent dupes
        path_count = result.stdout.count("Added by ilum CLI installer")
        assert path_count <= 1, f"Duplicate PATH entries ({path_count})"

    def test_install_dir_already_has_ilum(self, run_in_container) -> None:
        """Pre-existing ilum binary gets overwritten."""
        result: ContainerResult = run_in_container(
            # Place a fake binary first
            "sudo bash -c 'echo old > /usr/local/bin/ilum && chmod +x /usr/local/bin/ilum' && "
            f"ILUM_VERSION=0.1.0 {INSTALL_CMD} && "
            "ilum --version",
            env={"SHELL": "/bin/bash"},
        )
        assert result.exit_code == 0, f"Overwrite failed:\n{result.output}"
        assert "0.1.0" in result.output
