"""
Configuration Utilities

Handles loading and merging configuration with proper hierarchy:
1. Connector global defaults (from connector/defaults.yml -> default_sync)
2. Connector resource template (from connector/defaults.yml -> resources[base_resource])
3. Source config defaults (from sources/<source>.yml -> default_sync)
4. Source resource config (from sources/<source>.yml -> resources[name])
"""

import re
from typing import Any, Dict, List, Optional

import yaml


def load_yaml(file_path: str) -> Dict[str, Any]:
    """Load YAML file safely."""
    try:
        with open(file_path, "r") as f:
            return yaml.safe_load(f) or {}
    except FileNotFoundError:
        return {}


def deep_merge(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
    """
    Deep merge two dictionaries. Override takes precedence.

    Args:
        base: Base dictionary
        override: Override dictionary (values take precedence)

    Returns:
        Merged dictionary
    """
    result = base.copy()

    for key, value in override.items():
        if key in result and isinstance(result[key], dict) and isinstance(value, dict):
            result[key] = deep_merge(result[key], value)
        else:
            result[key] = value

    return result


def load_connector_defaults(connector_name: str, base_path: str) -> Dict[str, Any]:
    """
    Load connector defaults from defaults.yml.

    Args:
        connector_name: Name of the connector (e.g., 'notion')
        base_path: Base path to lakehouse_files folder

    Returns:
        Dictionary with 'default_sync' and 'resources' keys
    """
    defaults_file = f"{base_path}/connectors/{connector_name}/defaults.yml"
    data = load_yaml(defaults_file)

    return {
        "default_sync": data.get("default_sync", {}),
        "resources": data.get("resources", []),
    }


def find_base_resource(
    base_resource_name: str,
    connector_defaults: Dict[str, Any],
) -> Optional[Dict[str, Any]]:
    """
    Find a base resource template from connector defaults.

    Args:
        base_resource_name: Name of the base resource (e.g., 'database_query')
        connector_defaults: Connector defaults dict from load_connector_defaults()

    Returns:
        Resource template dict or None if not found
    """
    resources = connector_defaults.get("resources", [])
    for resource in resources:
        if resource.get("name") == base_resource_name:
            return resource.copy()
    return None


def substitute_params(value: Any, params: Dict[str, str]) -> Any:
    """
    Substitute {param_name} placeholders in strings with actual values.

    Args:
        value: Value to process (string, dict, list, or other)
        params: Dictionary of parameter names to values

    Returns:
        Value with placeholders substituted
    """
    if isinstance(value, str):
        # Replace {param_name} patterns
        for param_name, param_value in params.items():
            value = value.replace(f"{{{param_name}}}", str(param_value))
        return value
    elif isinstance(value, dict):
        return {k: substitute_params(v, params) for k, v in value.items()}
    elif isinstance(value, list):
        return [substitute_params(item, params) for item in value]
    else:
        return value


def build_resource_config(
    source_resource: Dict[str, Any],
    connector_defaults: Dict[str, Any],
    source_default_sync: Dict[str, Any],
    table_prefix: str = "",
) -> Dict[str, Any]:
    """
    Build a complete resource configuration by merging hierarchy.

    Hierarchy (lowest to highest priority):
    1. Connector default_sync
    2. Base resource template (from defaults.yml)
    3. Source default_sync
    4. Source resource config

    Args:
        source_resource: Resource config from source YAML
        connector_defaults: Connector defaults from load_connector_defaults()
        source_default_sync: Source-level default_sync
        table_prefix: Prefix to add to table names

    Returns:
        Complete resource configuration ready for dlt
    """
    result = {}

    # Get base resource template if specified
    # If no explicit base_resource, try to find by resource name in defaults
    base_resource_name = source_resource.get("base_resource")
    if not base_resource_name:
        # Try to find a matching resource by name in defaults
        base_resource_name = source_resource.get("name")

    if base_resource_name:
        base_resource = find_base_resource(base_resource_name, connector_defaults)
        if base_resource:
            result = base_resource.copy()

    # Get params for substitution
    params = source_resource.get("params", {})

    # Substitute params in base resource
    if params and result:
        result = substitute_params(result, params)

    # Determine final resource name
    resource_name = source_resource.get("name")
    table_name = source_resource.get("table_name", resource_name)
    final_table_name = f"{table_prefix}{table_name}" if table_prefix else table_name

    result["name"] = final_table_name

    # 1. Apply connector default_sync
    connector_default_sync = connector_defaults.get("default_sync", {})
    sync_fields = ["write_disposition", "sync_mode", "primary_key"]
    for field in sync_fields:
        if field in connector_default_sync:
            result[field] = connector_default_sync[field]

    # 2. Apply base resource sync (if has sync section)
    if base_resource_name:
        base_resource = find_base_resource(base_resource_name, connector_defaults)
        if base_resource and "sync" in base_resource:
            for field, value in base_resource["sync"].items():
                result[field] = value

    # 3. Apply source default_sync
    for field in sync_fields:
        if field in source_default_sync:
            result[field] = source_default_sync[field]

    # 4. Apply source resource sync
    source_sync = source_resource.get("sync", {})
    for field, value in source_sync.items():
        result[field] = value

    # Build endpoint with overrides
    endpoint = result.get("endpoint", {}).copy() if "endpoint" in result else {}

    # Apply source resource endpoint overrides
    source_endpoint = source_resource.get("endpoint", {})
    endpoint = deep_merge(endpoint, source_endpoint)

    # Substitute params in endpoint path
    if params and "path" in endpoint:
        endpoint["path"] = substitute_params(endpoint["path"], params)

    result["endpoint"] = endpoint

    # Apply paginator (source resource overrides base)
    if "paginator" in source_resource:
        result["endpoint"]["paginator"] = source_resource["paginator"]
    elif "paginator" in result.get("endpoint", {}):
        pass  # Keep base paginator
    elif base_resource_name:
        base = find_base_resource(base_resource_name, connector_defaults)
        if base and "paginator" in base:
            result["endpoint"]["paginator"] = base["paginator"]

    # Build incremental config
    incremental = source_resource.get("incremental")
    if incremental:
        result["incremental"] = _build_incremental_config(incremental)

    return result


def _build_incremental_config(incremental: Dict[str, Any]) -> Dict[str, Any]:
    """
    Build dlt incremental configuration from YAML format.

    YAML format:
        field: last_edited_time
        param_name: last_edited_time
        format: iso8601
        initial_value: "1970-01-01T00:00:00Z"

    DLT format:
        cursor_path: last_edited_time
        initial_value: "1970-01-01T00:00:00Z"

    Args:
        incremental: Incremental config from YAML

    Returns:
        DLT-compatible incremental config
    """
    dlt_incremental = {}

    # Map field -> cursor_path
    if "field" in incremental:
        dlt_incremental["cursor_path"] = incremental["field"]

    # Pass through initial_value
    if "initial_value" in incremental:
        dlt_incremental["initial_value"] = incremental["initial_value"]

    # Store param_name and format for filter building
    if "param_name" in incremental:
        dlt_incremental["param_name"] = incremental["param_name"]

    if "format" in incremental:
        dlt_incremental["format"] = incremental["format"]

    return dlt_incremental


def get_enabled_resources(source_config: Dict[str, Any]) -> List[str]:
    """
    Get list of enabled resource names from source config.

    Args:
        source_config: Source configuration dictionary

    Returns:
        List of enabled resource names
    """
    resources = source_config.get("resources", [])
    enabled = []

    for resource in resources:
        # Check if explicitly disabled
        if resource.get("enabled", True):
            name = resource.get("name")
            if name:
                enabled.append(name)

    return enabled


def resolve_resource_config(
    resource_name: str,
    connector_defaults: Dict[str, Any],
    source_config: Dict[str, Any],
    resource_override: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    """
    Resolve final resource configuration with proper hierarchy.

    Hierarchy (lowest to highest priority):
    1. Connector global defaults (default_sync)
    2. Connector resource sync (resources[name].sync)
    3. Source config defaults (default_sync)
    4. Source resource sync (resources[name].sync)
    5. Direct resource override (passed at runtime)

    Args:
        resource_name: Name of the resource
        connector_defaults: Output from load_connector_defaults()
        source_config: Source configuration dictionary
        resource_override: Optional runtime override

    Returns:
        Final merged resource configuration
    """
    # 1. Start with connector global defaults from default_sync
    connector_sync = connector_defaults.get("default_sync", {})
    result = connector_sync.copy()

    # 2. Apply connector resource-specific sync (from resources[].sync)
    connector_resources = connector_defaults.get("resources", [])
    for res in connector_resources:
        if res.get("name") == resource_name:
            res_sync = res.get("sync", {})
            if res_sync:
                result = deep_merge(result, res_sync)
            break

    # 3. Apply source config global defaults from default_sync
    source_sync = source_config.get("default_sync", {})
    if source_sync:
        result = deep_merge(result, source_sync)

    # 4. Apply source resource-specific sync (from resources[].sync)
    source_resources = source_config.get("resources", [])
    if isinstance(source_resources, list):
        for res in source_resources:
            if res.get("name") == resource_name:
                res_sync = res.get("sync", {})
                if res_sync:
                    result = deep_merge(result, res_sync)
                break

    # 5. Apply runtime override
    if resource_override:
        result = deep_merge(result, resource_override)

    return result


def get_resource_field(
    field_name: str,
    resource_name: str,
    connector_defaults: Dict[str, Any],
    source_config: Dict[str, Any],
    default_value: Any = None,
) -> Any:
    """
    Get a specific field value with proper hierarchy resolution.

    Args:
        field_name: Name of the field (e.g., 'write_disposition', 'primary_key')
        resource_name: Name of the resource
        connector_defaults: Output from load_connector_defaults()
        source_config: Source configuration dictionary
        default_value: Default if not found anywhere

    Returns:
        Resolved field value
    """
    config = resolve_resource_config(resource_name, connector_defaults, source_config)
    return config.get(field_name, default_value)
