from __future__ import annotations

import os
from dataclasses import dataclass
from pathlib import Path
from urllib.parse import urlparse

from src.errors import UserError

DEFAULT_PIPEDRIVE_BASE_URL = "https://api.pipedrive.com/v1"

PIPEDRIVE_API_TOKEN_ENV_VARS = ("PIPE_DRIVE_API_TOKEN", "PIPEDRIVE_API_TOKEN")
PIPEDRIVE_BASE_URL_ENV_VAR = "PIPEDRIVE_BASE_URL"
PIPEDRIVE_MAPPING_PATH_ENV_VAR = "PIPEDRIVE_FIELD_MAPPING_PATH"
PIPEDRIVE_ALLOW_INSECURE_HTTP_ENV_VAR = "PIPEDRIVE_ALLOW_INSECURE_HTTP"


@dataclass(frozen=True)
class PipedriveConfig:
    api_token: str
    base_url: str
    mapping_path: Path


def load_pipedrive_config(mapping_path_override: Path | None = None) -> PipedriveConfig:
    api_token = _load_api_token()
    base_url = _load_base_url()
    mapping_path = _load_mapping_path(mapping_path_override)
    return PipedriveConfig(api_token=api_token, base_url=base_url, mapping_path=mapping_path)


def _load_api_token() -> str:
    for name in PIPEDRIVE_API_TOKEN_ENV_VARS:
        value = os.environ.get(name, "").strip()
        if value:
            return value
    options = ", ".join(PIPEDRIVE_API_TOKEN_ENV_VARS)
    raise UserError(f"Missing Pipedrive API token. Set one of: {options}")


def _load_base_url() -> str:
    raw = os.environ.get(PIPEDRIVE_BASE_URL_ENV_VAR, "").strip()
    if not raw:
        return DEFAULT_PIPEDRIVE_BASE_URL

    parsed = urlparse(raw)
    if parsed.scheme not in {"https", "http"} or not parsed.netloc:
        raise UserError(f"Invalid {PIPEDRIVE_BASE_URL_ENV_VAR}: {raw!r}")
    if not parsed.path.rstrip("/").endswith("/v1"):
        raise UserError(f"{PIPEDRIVE_BASE_URL_ENV_VAR} must end with /v1 (got: {raw!r})")
    if parsed.scheme == "http" and os.environ.get(PIPEDRIVE_ALLOW_INSECURE_HTTP_ENV_VAR) != "1":
        raise UserError(
            "Refusing insecure http base URL for Pipedrive. "
            f"Set {PIPEDRIVE_ALLOW_INSECURE_HTTP_ENV_VAR}=1 to override."
        )
    return raw.rstrip("/")


def _load_mapping_path(override: Path | None) -> Path:
    if override is not None:
        path = override
    else:
        raw = os.environ.get(PIPEDRIVE_MAPPING_PATH_ENV_VAR, "").strip()
        if not raw:
            raise UserError(
                f"Missing mapping path. Set {PIPEDRIVE_MAPPING_PATH_ENV_VAR} or pass --mapping."
            )
        path = Path(raw)

    if not path.exists():
        raise UserError(f"Mapping file does not exist: {path}")
    if not path.is_file():
        raise UserError(f"Mapping path is not a file: {path}")
    return path
