import pytest
import os
from typing import List

from tensorus.config import Settings, settings as global_settings
from tensorus.metadata import get_configured_storage_instance, InMemoryStorage, PostgresMetadataStorage, ConfigurationError
from tensorus.metadata.storage_abc import MetadataStorage

# --- Test Settings Loading ---

def test_settings_default_values():
    # Test without any env vars set (relies on defaults in Settings)
    # This test is tricky because settings are loaded globally at import time.
    # We need to ensure a clean environment or test specific fields not typically overridden by CI.
    # For now, let's assume a default state or test fields that are less likely to be in CI env.
    s = Settings()  # Creates a new instance, re-evaluating defaults if env vars are not set for these specific fields
    assert s.STORAGE_BACKEND == "in_memory" # Default
    assert s.API_KEY_HEADER_NAME == "X-API-KEY" # Default
    assert s.VALID_API_KEYS == [] # Default (before manual parsing from env)
    assert s.POSTGRES_PORT == 5432 # Default

def test_settings_from_env(monkeypatch):
    monkeypatch.setenv("TENSORUS_STORAGE_BACKEND", "postgres_test")
    monkeypatch.setenv("TENSORUS_API_KEY_HEADER_NAME", "X-CUSTOM-API-KEY")
    monkeypatch.setenv("TENSORUS_VALID_API_KEYS", '["key_env1", "key_env2"]')
    monkeypatch.setenv("TENSORUS_POSTGRES_HOST", "testhost")
    monkeypatch.setenv("TENSORUS_POSTGRES_PORT", "1234")

    # Create a new Settings instance to load these monkeypatched env vars
    s = Settings()

    assert s.STORAGE_BACKEND == "postgres_test"
    assert s.API_KEY_HEADER_NAME == "X-CUSTOM-API-KEY"
    assert s.VALID_API_KEYS == ["key_env1", "key_env2"]
    assert s.POSTGRES_HOST == "testhost"
    assert s.POSTGRES_PORT == 1234

def test_valid_api_keys_parsing_empty_env(monkeypatch):
    # Ensure if TENSORUS_VALID_API_KEYS is not set, it defaults to empty list
    monkeypatch.delenv("TENSORUS_VALID_API_KEYS", raising=False)

    # global_settings is already loaded. We need to simulate its re-initialization or check its state.
    # The manual parsing block in config.py affects the global `settings` instance.
    # To test this properly, we might need to re-run that logic or use a new settings instance.

    # Create a new instance to test its initialization behavior
    s = Settings()

    assert s.VALID_API_KEYS == []

def test_valid_api_keys_json_list_in_env(monkeypatch):
    monkeypatch.setenv("TENSORUS_VALID_API_KEYS", '["json_key1", "json_key2"]')
    s = Settings()
    assert s.VALID_API_KEYS == ["json_key1", "json_key2"]


# --- Test Dynamic Storage Instantiation ---

@pytest.fixture(autouse=True)
def reset_global_settings_after_test(monkeypatch):
    """ Ensures each test involving settings gets a fresh state relative to env vars,
        and attempts to reset global settings to avoid test interference.
        This is complex due to Python's import-time evaluation.
    """
    original_env = os.environ.copy()
    # Store original values of global_settings that might be changed by manual parsing in config.py
    original_valid_keys = global_settings.VALID_API_KEYS

    yield

    os.environ.clear()
    os.environ.update(original_env)
    # Attempt to restore global_settings to a semblance of its original state
    # This is hard because `settings = Settings()` in config.py runs only once.
    # A better way would be a settings fixture that provides a fresh Settings instance.
    global_settings.VALID_API_KEYS = original_valid_keys
    # Update the global settings list to reflect the restored environment
    raw_keys = os.getenv("TENSORUS_VALID_API_KEYS")
    if raw_keys:
        global_settings.VALID_API_KEYS = [key.strip() for key in raw_keys.split(',')]
    else:
        global_settings.VALID_API_KEYS = []


def test_get_in_memory_storage_by_default(monkeypatch):
    monkeypatch.delenv("TENSORUS_STORAGE_BACKEND", raising=False)
    # Temporarily modify global_settings for this test's scope
    monkeypatch.setattr(global_settings, 'STORAGE_BACKEND', "in_memory")

    storage = get_configured_storage_instance()
    assert isinstance(storage, InMemoryStorage)

def test_get_in_memory_storage_explicitly(monkeypatch):
    monkeypatch.setenv("TENSORUS_STORAGE_BACKEND", "in_memory")
    # Update global_settings to reflect the monkeypatched env var for this test
    monkeypatch.setattr(global_settings, 'STORAGE_BACKEND', "in_memory")

    storage = get_configured_storage_instance()
    assert isinstance(storage, InMemoryStorage)

def test_get_postgres_storage_with_dsn(monkeypatch):
    dsn = "postgresql://testuser:testpass@testhost:5432/testdb"
    monkeypatch.setenv("TENSORUS_STORAGE_BACKEND", "postgres")
    monkeypatch.setenv("TENSORUS_POSTGRES_DSN", dsn)
    # Update global_settings
    monkeypatch.setattr(global_settings, 'STORAGE_BACKEND', "postgres")
    monkeypatch.setattr(global_settings, 'POSTGRES_DSN', dsn)
    monkeypatch.setattr(global_settings, 'POSTGRES_HOST', None) # Ensure DSN is chosen

    # Mock psycopg2.pool.SimpleConnectionPool to avoid actual connection
    class MockPool:
        def __init__(self, minconn, maxconn, dsn):
            assert dsn == "postgresql://testuser:testpass@testhost:5432/testdb"
        def getconn(self): pass
        def putconn(self, conn): pass
        def closeall(self): pass

    monkeypatch.setattr("psycopg2.pool.SimpleConnectionPool", MockPool)

    storage = get_configured_storage_instance()
    assert isinstance(storage, PostgresMetadataStorage)
    assert storage.dsn == dsn

def test_get_postgres_storage_with_params(monkeypatch):
    monkeypatch.setenv("TENSORUS_STORAGE_BACKEND", "postgres")
    monkeypatch.setenv("TENSORUS_POSTGRES_HOST", "localhost")
    monkeypatch.setenv("TENSORUS_POSTGRES_USER", "tensorus_user")
    monkeypatch.setenv("TENSORUS_POSTGRES_DB", "tensorus_db")
    monkeypatch.setenv("TENSORUS_POSTGRES_PASSWORD", "securepassword") # Optional but good to test
    monkeypatch.setenv("TENSORUS_POSTGRES_PORT", "5433")

    # Update global_settings
    monkeypatch.setattr(global_settings, 'STORAGE_BACKEND', "postgres")
    monkeypatch.setattr(global_settings, 'POSTGRES_HOST', "localhost")
    monkeypatch.setattr(global_settings, 'POSTGRES_USER', "tensorus_user")
    monkeypatch.setattr(global_settings, 'POSTGRES_DB', "tensorus_db")
    monkeypatch.setattr(global_settings, 'POSTGRES_PASSWORD', "securepassword")
    monkeypatch.setattr(global_settings, 'POSTGRES_PORT', 5433)
    monkeypatch.setattr(global_settings, 'POSTGRES_DSN', None) # Ensure params are chosen


    class MockPoolKwargs:
        def __init__(self, minconn, maxconn, host, port, user, password, database):
            assert host == "localhost"
            assert port == 5433
            assert user == "tensorus_user"
            assert password == "securepassword"
            assert database == "tensorus_db"
        def getconn(self): pass
        def putconn(self, conn): pass
        def closeall(self): pass

    monkeypatch.setattr("psycopg2.pool.SimpleConnectionPool", MockPoolKwargs)

    storage = get_configured_storage_instance()
    assert isinstance(storage, PostgresMetadataStorage)

def test_postgres_storage_missing_config_dsn_and_params(monkeypatch):
    monkeypatch.setenv("TENSORUS_STORAGE_BACKEND", "postgres")
    # Ensure all relevant PG env vars are unset or None in global_settings
    monkeypatch.delenv("TENSORUS_POSTGRES_DSN", raising=False)
    monkeypatch.delenv("TENSORUS_POSTGRES_HOST", raising=False)
    monkeypatch.delenv("TENSORUS_POSTGRES_USER", raising=False)
    monkeypatch.delenv("TENSORUS_POSTGRES_DB", raising=False)

    monkeypatch.setattr(global_settings, 'STORAGE_BACKEND', "postgres")
    monkeypatch.setattr(global_settings, 'POSTGRES_DSN', None)
    monkeypatch.setattr(global_settings, 'POSTGRES_HOST', None)
    monkeypatch.setattr(global_settings, 'POSTGRES_USER', None)
    monkeypatch.setattr(global_settings, 'POSTGRES_DB', None)

    with pytest.raises(ConfigurationError, match="PostgreSQL backend selected, but required connection details"):
        get_configured_storage_instance()

def test_postgres_storage_missing_some_params(monkeypatch):
    monkeypatch.setenv("TENSORUS_STORAGE_BACKEND", "postgres")
    monkeypatch.setenv("TENSORUS_POSTGRES_HOST", "localhost") # User and DB missing

    monkeypatch.setattr(global_settings, 'STORAGE_BACKEND', "postgres")
    monkeypatch.setattr(global_settings, 'POSTGRES_HOST', "localhost")
    monkeypatch.setattr(global_settings, 'POSTGRES_USER', None)
    monkeypatch.setattr(global_settings, 'POSTGRES_DB', None)
    monkeypatch.setattr(global_settings, 'POSTGRES_DSN', None)

    with pytest.raises(ConfigurationError, match="PostgreSQL backend selected, but required connection details"):
        get_configured_storage_instance()

def test_unsupported_storage_backend(monkeypatch):
    monkeypatch.setenv("TENSORUS_STORAGE_BACKEND", "mongodb")
    monkeypatch.setattr(global_settings, 'STORAGE_BACKEND', "mongodb")

    with pytest.raises(ConfigurationError, match="Unsupported storage backend: mongodb"):
        get_configured_storage_instance()

# Note: psycopg2 is not actually imported or used if mocked correctly.
# These tests verify the configuration logic that *would* pass params to psycopg2.
