"""
Envchain-based token storage for schwab-py.

Requires the `envchain` optional dependency:
    pip install schwab-py-utils[envchain]
"""

import json
import logging
from typing import Any, Callable

logger = logging.getLogger(__name__)

TokenReadFunc = Callable[[], dict[str, Any] | None]
TokenWriteFunc = Callable[[dict[str, Any]], None]


def create_envchain_token_handlers(
    envchain_namespace: str = "schwab-py",
    envchain_key: str = "token",
    envchain_cmd: str = "/opt/homebrew/bin/envchain",
) -> tuple[TokenReadFunc, TokenWriteFunc]:
    """
    Create token read/write functions that use envchain for secure storage.

    The returned functions are suitable for use with schwab-py's
    `client_from_access_functions`.

    Args:
        envchain_namespace: The namespace in envchain for token storage
        envchain_key: The key within the namespace
        envchain_cmd: Path to the envchain command

    Returns:
        Tuple of (token_read_func, token_write_func)

    Raises:
        ImportError: If envchain-python is not installed
    """
    try:
        from envchain_python import (  # pyright: ignore[reportMissingImports]
            EnvchainError,
            deserialize_string_to_object,
            get_var,
            serialize_object_to_string,
            set_var,
        )
    except ImportError:
        raise ImportError(
            "envchain-python is required for envchain token storage. "
            "Install with: pip install schwab-py-utils[envchain]"
        )

    if not isinstance(envchain_namespace, str) or not envchain_namespace.strip():
        raise ValueError("envchain_namespace must be a non-empty string.")
    if not isinstance(envchain_key, str) or not envchain_key.strip():
        raise ValueError("envchain_key must be a non-empty string.")

    envchain_kwargs = {"envchain_cmd": envchain_cmd}

    def read_func() -> dict[str, Any] | None:
        """Read token from envchain."""
        logger.debug(
            f"Reading token from envchain: namespace='{envchain_namespace}', key='{envchain_key}'"
        )
        try:
            token_str = get_var(envchain_namespace, envchain_key, **envchain_kwargs)

            if token_str is None or not token_str:
                logger.info(
                    f"No token found in envchain for namespace='{envchain_namespace}'"
                )
                return None

            token_obj = deserialize_string_to_object(token_str)
            if not isinstance(token_obj, dict):
                logger.warning(f"Deserialized token is not a dict: {type(token_obj)}")
                return None

            logger.debug("Successfully read token from envchain.")
            return token_obj

        except EnvchainError as e:
            logger.warning(f"EnvchainError reading token: {e}")
            return None
        except json.JSONDecodeError as e:
            logger.error(f"Failed to deserialize token: {e}")
            return None

    def write_func(
        metadata_wrapped_token: dict[str, Any], *args: Any, **kwargs: Any
    ) -> None:
        """Write token to envchain."""
        logger.debug(
            f"Writing token to envchain: namespace='{envchain_namespace}', key='{envchain_key}'"
        )
        if not isinstance(metadata_wrapped_token, dict):
            raise TypeError(f"Expected dict, got {type(metadata_wrapped_token)}")

        try:
            serialized = serialize_object_to_string(metadata_wrapped_token)
            set_var(
                envchain_namespace,
                envchain_key,
                serialized,
                require_passphrase=True,
                **envchain_kwargs,
            )
            logger.debug("Successfully wrote token to envchain.")
        except EnvchainError as e:
            logger.error(f"EnvchainError writing token: {e}")
            raise

    return read_func, write_func
