from __future__ import annotations

from dataclasses import dataclass, field
import logging
from pathlib import Path
import tomllib
from urllib.parse import urlparse

logger = logging.getLogger(__name__)
DEFAULT_SETTINGS_PATH = Path("settings.toml")


@dataclass(frozen=True, slots=True)
class InfluxSettings:
    """InfluxDB connection settings.

    Attributes
    ----------
    url : str
        Base URL for the InfluxDB server.
    token : str
        Authentication token.
    org : str
        InfluxDB organization name.
    bucket : str
        Target bucket name.
    default_tags : dict[str, str]
        Tags automatically attached to each written point.
    """

    url: str
    token: str
    org: str
    bucket: str
    default_tags: dict[str, str] = field(default_factory=dict)


def _required_string(data: dict[str, object], key: str) -> str:
    """Return a required non-empty string setting.

    Parameters
    ----------
    data : dict[str, object]
        Parsed TOML dictionary.
    key : str
        Key to read.

    Returns
    -------
    str
        Stripped string value.

    Raises
    ------
    ValueError
        If the key is missing or not a non-empty string.
    """
    value = data.get(key)
    if not isinstance(value, str) or not value.strip():
        logger.error("Invalid settings value for '%s': expected non-empty string.", key)
        raise ValueError(f"'{key}' must be a non-empty string.")
    return value.strip()


def _validate_url(url: str) -> None:
    """Validate that a URL is HTTP or HTTPS.

    Parameters
    ----------
    url : str
        URL value to validate.

    Raises
    ------
    ValueError
        If URL is not a valid HTTP or HTTPS URL.
    """
    parsed = urlparse(url)
    if parsed.scheme not in {"http", "https"} or not parsed.netloc:
        logger.error("Invalid InfluxDB URL format provided.")
        raise ValueError("'url' must be a valid http/https URL.")


def load_settings(path: str | Path | None = None) -> InfluxSettings:
    """Load and validate InfluxDB settings from TOML.

    Parameters
    ----------
    path : str | Path | None, default=None
        Path to a TOML settings file. If ``None``, uses ``settings.toml`` in
        the current working directory.

    Returns
    -------
    InfluxSettings
        Validated settings object.

    Raises
    ------
    FileNotFoundError
        If the settings file does not exist.
    ValueError
        If required keys are missing or contain invalid values.
    """

    settings_path = Path(path) if path is not None else DEFAULT_SETTINGS_PATH
    logger.debug("Loading InfluxDB settings from '%s'.", settings_path)

    if not settings_path.exists():
        logger.error("Settings file not found at '%s'.", settings_path)
        raise FileNotFoundError(
            f"Settings file not found: {settings_path}. "
            "Keep a repo-level settings.toml and fill url/org/bucket/token."
        )

    with settings_path.open("rb") as file:
        data = tomllib.load(file)
    logger.debug("Parsed settings TOML successfully.")

    url = _required_string(data, "url")
    _validate_url(url)

    token = _required_string(data, "token")
    org = _required_string(data, "org")
    bucket = _required_string(data, "bucket")

    defaults = data.get("defaults", {})
    if not isinstance(defaults, dict):
        logger.error("Invalid settings: 'defaults' is not a TOML table.")
        raise ValueError("'defaults' must be a table.")

    raw_tags = defaults.get("tags", {})
    if not isinstance(raw_tags, dict):
        logger.error("Invalid settings: 'defaults.tags' is not a TOML table.")
        raise ValueError("'defaults.tags' must be a table.")

    default_tags: dict[str, str] = {}
    for key, value in raw_tags.items():
        if not isinstance(key, str) or not key.strip():
            logger.error("Invalid default tag key in settings.")
            raise ValueError("Each default tag key must be a non-empty string.")
        if not isinstance(value, str) or not value.strip():
            logger.error("Invalid default tag value for key '%s'.", key)
            raise ValueError(f"Default tag '{key}' must be a non-empty string value.")
        default_tags[key.strip()] = value.strip()

    settings = InfluxSettings(
        url=url,
        token=token,
        org=org,
        bucket=bucket,
        default_tags=default_tags,
    )
    logger.info(
        "Loaded InfluxDB settings for org='%s', bucket='%s'.",
        settings.org,
        settings.bucket,
    )
    logger.debug("Default tags configured: %d", len(settings.default_tags))
    return settings
