"""
Tests for Dev/Staging/Prod environment configuration.

Tests the environment files, Docker Compose configurations, and helper scripts
to ensure proper environment separation and functionality.

Usage:
    cd backend && pytest test_environment_config.py -v
"""

import os
import subprocess
from pathlib import Path
from typing import Dict, List
import pytest


# Project root directory
PROJECT_ROOT = Path(__file__).parent.parent


class TestEnvironmentFiles:
    """Test environment configuration files exist and have correct content."""

    def test_env_dev_exists(self):
        """Test that .env.dev file exists."""
        env_file = PROJECT_ROOT / ".env.dev"
        assert env_file.exists(), f".env.dev should exist at {env_file}"

    def test_env_staging_exists(self):
        """Test that .env.staging file exists."""
        env_file = PROJECT_ROOT / ".env.staging"
        assert env_file.exists(), f".env.staging should exist at {env_file}"

    def test_env_example_exists(self):
        """Test that .env.example file exists."""
        env_file = PROJECT_ROOT / ".env.example"
        assert env_file.exists(), f".env.example should exist at {env_file}"

    def test_env_dev_content(self):
        """Test that .env.dev has correct environment settings."""
        env_file = PROJECT_ROOT / ".env.dev"
        if not env_file.exists():
            pytest.skip(".env.dev not found")

        content = env_file.read_text()

        # Required settings for development
        assert "ENVIRONMENT=development" in content, "ENVIRONMENT should be development"
        assert "COMPOSE_PROJECT_NAME=breakpilot-dev" in content, "Project name should be breakpilot-dev"
        assert "POSTGRES_DB=breakpilot_dev" in content, "Database should be breakpilot_dev"
        assert "DEBUG=true" in content, "DEBUG should be true in development"

    def test_env_staging_content(self):
        """Test that .env.staging has correct environment settings."""
        env_file = PROJECT_ROOT / ".env.staging"
        if not env_file.exists():
            pytest.skip(".env.staging not found")

        content = env_file.read_text()

        # Required settings for staging
        assert "ENVIRONMENT=staging" in content, "ENVIRONMENT should be staging"
        assert "COMPOSE_PROJECT_NAME=breakpilot-staging" in content, "Project name should be breakpilot-staging"
        assert "POSTGRES_DB=breakpilot_staging" in content, "Database should be breakpilot_staging"
        assert "DEBUG=false" in content, "DEBUG should be false in staging"

    def test_env_files_have_different_databases(self):
        """Test that dev and staging use different database names."""
        env_dev = PROJECT_ROOT / ".env.dev"
        env_staging = PROJECT_ROOT / ".env.staging"

        if not (env_dev.exists() and env_staging.exists()):
            pytest.skip("Environment files not found")

        dev_content = env_dev.read_text()
        staging_content = env_staging.read_text()

        # Extract database names
        dev_db = None
        staging_db = None
        for line in dev_content.split("\n"):
            if line.startswith("POSTGRES_DB="):
                dev_db = line.split("=")[1].strip()
        for line in staging_content.split("\n"):
            if line.startswith("POSTGRES_DB="):
                staging_db = line.split("=")[1].strip()

        assert dev_db is not None, "POSTGRES_DB not found in .env.dev"
        assert staging_db is not None, "POSTGRES_DB not found in .env.staging"
        assert dev_db != staging_db, f"Dev and staging should use different databases, both use {dev_db}"


class TestDockerComposeFiles:
    """Test Docker Compose configuration files."""

    def test_docker_compose_yml_exists(self):
        """Test that main docker-compose.yml exists."""
        compose_file = PROJECT_ROOT / "docker-compose.yml"
        assert compose_file.exists(), "docker-compose.yml should exist"

    def test_docker_compose_override_exists(self):
        """Test that docker-compose.override.yml exists for dev."""
        compose_file = PROJECT_ROOT / "docker-compose.override.yml"
        assert compose_file.exists(), "docker-compose.override.yml should exist for development"

    def test_docker_compose_staging_exists(self):
        """Test that docker-compose.staging.yml exists."""
        compose_file = PROJECT_ROOT / "docker-compose.staging.yml"
        assert compose_file.exists(), "docker-compose.staging.yml should exist"

    def test_docker_compose_override_valid_yaml(self):
        """Test that docker-compose.override.yml has valid syntax."""
        compose_file = PROJECT_ROOT / "docker-compose.override.yml"
        if not compose_file.exists():
            pytest.skip("docker-compose.override.yml not found")

        result = subprocess.run(
            ["docker", "compose", "-f", "docker-compose.yml", "-f", "docker-compose.override.yml", "config"],
            cwd=PROJECT_ROOT,
            capture_output=True,
            text=True,
        )
        assert result.returncode == 0, f"docker-compose.override.yml syntax error: {result.stderr}"

    def test_docker_compose_staging_valid_yaml(self):
        """Test that docker-compose.staging.yml has valid syntax."""
        compose_file = PROJECT_ROOT / "docker-compose.staging.yml"
        if not compose_file.exists():
            pytest.skip("docker-compose.staging.yml not found")

        result = subprocess.run(
            ["docker", "compose", "-f", "docker-compose.yml", "-f", "docker-compose.staging.yml", "config"],
            cwd=PROJECT_ROOT,
            capture_output=True,
            text=True,
        )
        assert result.returncode == 0, f"docker-compose.staging.yml syntax error: {result.stderr}"


class TestHelperScripts:
    """Test helper scripts for environment management."""

    def test_env_switch_script_exists(self):
        """Test that env-switch.sh script exists."""
        script = PROJECT_ROOT / "scripts" / "env-switch.sh"
        assert script.exists(), "scripts/env-switch.sh should exist"

    def test_env_switch_script_executable(self):
        """Test that env-switch.sh is executable."""
        script = PROJECT_ROOT / "scripts" / "env-switch.sh"
        if not script.exists():
            pytest.skip("env-switch.sh not found")
        assert os.access(script, os.X_OK), "env-switch.sh should be executable"

    def test_start_script_exists(self):
        """Test that start.sh script exists."""
        script = PROJECT_ROOT / "scripts" / "start.sh"
        assert script.exists(), "scripts/start.sh should exist"

    def test_start_script_executable(self):
        """Test that start.sh is executable."""
        script = PROJECT_ROOT / "scripts" / "start.sh"
        if not script.exists():
            pytest.skip("start.sh not found")
        assert os.access(script, os.X_OK), "start.sh should be executable"

    def test_stop_script_exists(self):
        """Test that stop.sh script exists."""
        script = PROJECT_ROOT / "scripts" / "stop.sh"
        assert script.exists(), "scripts/stop.sh should exist"

    def test_stop_script_executable(self):
        """Test that stop.sh is executable."""
        script = PROJECT_ROOT / "scripts" / "stop.sh"
        if not script.exists():
            pytest.skip("stop.sh not found")
        assert os.access(script, os.X_OK), "stop.sh should be executable"

    def test_promote_script_exists(self):
        """Test that promote.sh script exists."""
        script = PROJECT_ROOT / "scripts" / "promote.sh"
        assert script.exists(), "scripts/promote.sh should exist"

    def test_promote_script_executable(self):
        """Test that promote.sh is executable."""
        script = PROJECT_ROOT / "scripts" / "promote.sh"
        if not script.exists():
            pytest.skip("promote.sh not found")
        assert os.access(script, os.X_OK), "promote.sh should be executable"

    def test_status_script_exists(self):
        """Test that status.sh script exists."""
        script = PROJECT_ROOT / "scripts" / "status.sh"
        assert script.exists(), "scripts/status.sh should exist"

    def test_status_script_executable(self):
        """Test that status.sh is executable."""
        script = PROJECT_ROOT / "scripts" / "status.sh"
        if not script.exists():
            pytest.skip("status.sh not found")
        assert os.access(script, os.X_OK), "status.sh should be executable"


class TestGitIgnore:
    """Test .gitignore configuration for environment files."""

    def test_gitignore_exists(self):
        """Test that .gitignore exists."""
        gitignore = PROJECT_ROOT / ".gitignore"
        assert gitignore.exists(), ".gitignore should exist"

    def test_gitignore_excludes_env(self):
        """Test that .gitignore excludes .env file."""
        gitignore = PROJECT_ROOT / ".gitignore"
        if not gitignore.exists():
            pytest.skip(".gitignore not found")

        content = gitignore.read_text()
        # Check for .env exclusion pattern
        assert ".env" in content, ".gitignore should exclude .env"

    def test_gitignore_includes_env_dev(self):
        """Test that .gitignore includes .env.dev (not excluded)."""
        gitignore = PROJECT_ROOT / ".gitignore"
        if not gitignore.exists():
            pytest.skip(".gitignore not found")

        content = gitignore.read_text()
        # Check for .env.dev inclusion pattern (negation)
        assert "!.env.dev" in content, ".gitignore should include .env.dev"

    def test_gitignore_includes_env_staging(self):
        """Test that .gitignore includes .env.staging (not excluded)."""
        gitignore = PROJECT_ROOT / ".gitignore"
        if not gitignore.exists():
            pytest.skip(".gitignore not found")

        content = gitignore.read_text()
        # Check for .env.staging inclusion pattern (negation)
        assert "!.env.staging" in content, ".gitignore should include .env.staging"


class TestDocumentation:
    """Test that environment documentation exists."""

    def test_environments_md_exists(self):
        """Test that environments.md documentation exists."""
        doc = PROJECT_ROOT / "docs" / "architecture" / "environments.md"
        assert doc.exists(), "docs/architecture/environments.md should exist"

    def test_environment_setup_guide_exists(self):
        """Test that environment-setup.md guide exists."""
        doc = PROJECT_ROOT / "docs" / "guides" / "environment-setup.md"
        assert doc.exists(), "docs/guides/environment-setup.md should exist"

    def test_test_environment_setup_script_exists(self):
        """Test that test-environment-setup.sh script exists."""
        script = PROJECT_ROOT / "scripts" / "test-environment-setup.sh"
        assert script.exists(), "scripts/test-environment-setup.sh should exist"


class TestEnvironmentIsolation:
    """Test environment isolation settings."""

    @pytest.fixture
    def env_configs(self) -> Dict[str, Dict[str, str]]:
        """Parse environment files into dictionaries."""
        configs = {}
        for env_name in ["dev", "staging"]:
            env_file = PROJECT_ROOT / f".env.{env_name}"
            if not env_file.exists():
                continue
            config = {}
            for line in env_file.read_text().split("\n"):
                line = line.strip()
                if line and not line.startswith("#") and "=" in line:
                    key, value = line.split("=", 1)
                    config[key.strip()] = value.strip()
            configs[env_name] = config
        return configs

    def test_unique_compose_project_names(self, env_configs):
        """Test that each environment has a unique COMPOSE_PROJECT_NAME."""
        if len(env_configs) < 2:
            pytest.skip("Need at least 2 environment files")

        project_names = [
            config.get("COMPOSE_PROJECT_NAME")
            for config in env_configs.values()
            if config.get("COMPOSE_PROJECT_NAME")
        ]
        assert len(project_names) == len(set(project_names)), "COMPOSE_PROJECT_NAME should be unique per environment"

    def test_unique_database_names(self, env_configs):
        """Test that each environment uses a unique database name."""
        if len(env_configs) < 2:
            pytest.skip("Need at least 2 environment files")

        db_names = [
            config.get("POSTGRES_DB")
            for config in env_configs.values()
            if config.get("POSTGRES_DB")
        ]
        assert len(db_names) == len(set(db_names)), "POSTGRES_DB should be unique per environment"

    def test_debug_disabled_in_staging(self, env_configs):
        """Test that DEBUG is disabled in staging environment."""
        if "staging" not in env_configs:
            pytest.skip(".env.staging not found")

        debug_value = env_configs["staging"].get("DEBUG", "").lower()
        assert debug_value == "false", "DEBUG should be false in staging"

    def test_debug_enabled_in_dev(self, env_configs):
        """Test that DEBUG is enabled in dev environment."""
        if "dev" not in env_configs:
            pytest.skip(".env.dev not found")

        debug_value = env_configs["dev"].get("DEBUG", "").lower()
        assert debug_value == "true", "DEBUG should be true in development"


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
