import os, json
from typing import Dict, Callable
from dotenv import load_dotenv
from enum import Enum
from functools import wraps

class CloudProvider(Enum):
    LOCAL = "local"
    AWS = "aws"
    AZURE = "azure"
    GCP = "gcp"

class ConfigLoader:
    """Load configuration from various cloud providers or local environment"""

    def __init__(self, cloud_provider: str, config_location: str):
        self.cloud_provider = CloudProvider(cloud_provider.lower())
        self.config_location = config_location

    def _load_from_local(self) -> Dict[str,str]:
        """Load configuration from local .env file"""

        try:
            from dotenv import load_dotenv, dotenv_values

            #Try to load from .env file
            env_file = os.path.join(os.getcwd(),".env")
            print("env_file",env_file)
            if os.path.exists(env_file):
                load_dotenv(env_file, override=True)
                config = dotenv_values(env_file)
                print(f"✓ Loaded configuration from local .env file")
                return dict(config)
            else:
                print(f"⚠ No .env file found at {env_file}")
                return {}
        except ImportError:
            print("⚠ python-dotenv not installed. Install with: pip install python-dotenv")
            return {}

    def _load_from_aws(self) -> Dict[str,str]:
        """Load configuration from AWS Parameter Store"""
        try:
            import boto3
            from botocore.exceptions import ClientError

            ssm_client = boto3.client("ssm")
            config = {}

            # If no children found, try fetching a single parameter at the path
            try:
                path = self.config_location.rstrip("/")  # normalize
                resp = ssm_client.get_parameter(Name=path, WithDecryption=True)
                raw = resp["Parameter"]["Value"]
                # Try JSON parse
                try:
                    data = json.loads(raw)
                    # stringify values to env
                    for k, v in data.items():
                        config[str(k)] = str(v)
                    print(f"✓ Loaded {len(config)} keys from single JSON parameter: {path}")
                    return config
                except json.JSONDecodeError:
                    print("\n⚠ Parameter value is not valid JSON")

            except ssm_client.exceptions.ParameterNotFound:
                print(f"⚠ No parameters found under path and no parameter at: {path}")
                return {}

        except ImportError:
            print("⚠ boto3 not installed. Install with: pip install boto3")
            return {}

    def _load_from_azure(self) -> Dict[str, str]:
        """Load configuration from Azure App Configuration"""
        try:
            from azure.appconfiguration import AzureAppConfigurationClient
            from azure.identity import DefaultAzureCredential

            config = {}

            try:
                # config_location should be the connection string or endpoint
                if self.config_location.startswith('Endpoint='):
                    # Using connection string
                    client = AzureAppConfigurationClient.from_connection_string(
                        self.config_location
                    )
                else:
                    # Using endpoint with managed identity
                    credential = DefaultAzureCredential()
                    client = AzureAppConfigurationClient(
                        base_url=self.config_location,
                        credential=credential
                    )

                # List all configuration settings
                config_settings = client.list_configuration_settings()

                for setting in config_settings:
                    config[setting.key] = setting.value

                print(f"✓ Loaded {len(config)} settings from Azure App Configuration")
                return config

            except Exception as e:
                print(f"✗ Error loading from Azure App Configuration: {e}")
                return {}

        except ImportError:
            print("⚠ azure-appconfiguration not installed. Install with: pip install azure-appconfiguration azure-identity")
            return {}


    def _load_from_gcp(self) -> Dict[str, str]:
        """Load configuration from GCP Secret Manager"""
        try:
            from google.cloud import secretmanager

            client = secretmanager.SecretManagerServiceClient()
            config = {}

            try:
                # config_location should be in format: projects/PROJECT_ID
                parent = self.config_location

                # List all secrets
                for secret in client.list_secrets(request={"parent": parent}):
                    secret_name = secret.name
                    # Get the latest version
                    version_name = f"{secret_name}/versions/latest"

                    try:
                        response = client.access_secret_version(request={"name": version_name})
                        key = secret_name.split('/')[-1]
                        config[key] = response.payload.data.decode('UTF-8')
                    except Exception as e:
                        print(f"⚠ Could not access secret {secret_name}: {e}")

                print(f"✓ Loaded {len(config)} secrets from GCP Secret Manager")
                return config

            except Exception as e:
                print(f"✗ Error loading from GCP Secret Manager: {e}")
                return {}

        except ImportError:
            print("⚠ google-cloud-secret-manager not installed. Install with: pip install google-cloud-secret-manager")
            return {}


    def load(self) -> Dict[str, str]:
        """Load configuration based on cloud provider"""
        if self.cloud_provider == CloudProvider.LOCAL:
            return self._load_from_local()
        elif self.cloud_provider == CloudProvider.AWS:
            return self._load_from_aws()
        elif self.cloud_provider == CloudProvider.AZURE:
            return self._load_from_azure()
        elif self.cloud_provider == CloudProvider.GCP:
            return self._load_from_gcp()
        else:
            raise ValueError(f"Unsupported cloud provider: {self.cloud_provider}")

def load_cloud_config() -> Dict[str, str]:
    """
    Main function to load cloud configuration.
    Reads CONFIG_LOCATION and CLOUD_PROVIDER from environment variables.

    Returns:
        Dict[str, str]: Configuration key-value pairs
    """
    load_dotenv(override=True)
    cloud_provider = os.getenv('CLOUD_PROVIDER')
    config_location = os.getenv('CONFIG_LOCATION')
    print("cloud_provider",cloud_provider,
          "\nconfig_location",config_location)
    if not cloud_provider or not config_location:
        raise ValueError(
            "CLOUD_PROVIDER or CONFIG_LOCATION environment variables must be set."
        )

    print(f"Loading configuration from {cloud_provider.upper()}...")
    if config_location:
        print(f"Config location: {config_location}")

    loader = ConfigLoader(cloud_provider, config_location)
    config = loader.load()

    # Set environment variables
    for key, value in config.items():
        os.environ[key] = value

    return config

# Decorator defined in the same file
def with_cloud_config():
    """
    Ensures cloud config is loaded before the wrapped function executes.
    - force_reload=True will reload on every call.
    """
    def decorator(func: Callable):
        @wraps(func)
        def wrapper(event, context):
            load_cloud_config()
            return func(event, context)
        return wrapper
    return decorator

# Example usage for local testing
if __name__ == "__main__":
    # Option A: Only decorator usage (recommended for handlers)
    @with_cloud_config()  # loads once and caches
    def demo_handler(event=None, context=None):
        print("AZURE_STORAGE_ACCOUNT_NAME:", os.getenv("AZURE_STORAGE_ACCOUNT_NAME"))
        return {"ok": True}

    demo_handler({}, {})

    # Option B: Only explicit load (useful for debugging)
    # config = load_cloud_config()
    # print(f"\n{'=' * 50}")
    # print(f"Loaded {len(config)} configuration variables")
    # print(f"{'=' * 50}")
    # for key, value in config.items():
    #     masked_value = value[:4] + '*' * (len(value) - 4) if len(value) > 4 else '****'
    #     print(f"{key}: {masked_value}")