from __future__ import annotations

import os
from dataclasses import dataclass, fields
from pathlib import Path
from typing import Any

from dotenv import dotenv_values


@dataclass(slots=True)
class LoadedCredentials:
    api_key: str | None
    api_secret: str | None
    source: str
    env_file: Path | None = None


@dataclass(slots=True)
class EasyBinanceConfig:
    env_file: Path | None = None
    api_key: str | None = None
    api_secret: str | None = None
    testnet: bool = False
    rest_timeout: int = 1000
    ws_timeout: int = 5000
    reconnect_delay: int = 5000
    pool_size: int = 2
    retries: int = 3
    backoff: int = 1000
    keep_alive: bool = True
    rest_compression: bool = True
    ws_compression: int = 0
    proxy: dict[str, Any] | None = None
    time_unit: str | None = None
    return_rate_limits: bool = True
    rest_url: str | None = None
    ws_api_url: str | None = None
    ws_stream_url: str | None = None

    def with_overrides(self, **kwargs: Any) -> "EasyBinanceConfig":
        data = {field.name: getattr(self, field.name) for field in fields(self)}
        data.update({key: value for key, value in kwargs.items() if value is not None})
        if data["env_file"] is not None and not isinstance(data["env_file"], Path):
            data["env_file"] = Path(data["env_file"])
        return EasyBinanceConfig(**data)


def load_credentials(
    env_file: str | Path | None = None,
    api_key: str | None = None,
    api_secret: str | None = None,
    cwd: Path | None = None,
) -> LoadedCredentials:
    """Load credentials with local .env precedence over process environment variables."""

    if api_key or api_secret:
        return LoadedCredentials(
            api_key=api_key,
            api_secret=api_secret,
            source="explicit",
            env_file=Path(env_file) if env_file else None,
        )

    base_dir = cwd or Path.cwd()
    chosen_env_file = Path(env_file) if env_file else base_dir / ".env"

    values: dict[str, str] = {}
    source = "environment"
    existing_file: Path | None = None

    if chosen_env_file.exists():
        values = {
            key: value
            for key, value in dotenv_values(chosen_env_file).items()
            if value is not None
        }
        source = "dotenv"
        existing_file = chosen_env_file

    return LoadedCredentials(
        api_key=values.get("BINANCE_API_KEY") or os.getenv("BINANCE_API_KEY"),
        api_secret=values.get("BINANCE_API_SECRET") or os.getenv("BINANCE_API_SECRET"),
        source=source,
        env_file=existing_file,
    )
