"""Configuration loading and path resolution."""

from __future__ import annotations

import json
import os
import sys
import tomllib
from pathlib import Path
from typing import Any

import yaml

from omni.config.models import ConfigInput, OmniInstanceConfig, OmniSDKConfig, ResolvedConfig

OMNI_SDK_CONFIG_ENV = "OMNI_SDK_CONFIG"


def _default_config_dir() -> Path:
    if sys.platform.startswith("win"):
        local_app_data = os.environ.get("LOCALAPPDATA")
        if local_app_data:
            return Path(local_app_data) / "omnisdk"
        return Path.home() / "AppData" / "Local" / "omnisdk"

    return Path.home() / ".config" / "omnisdk"


def default_config_candidates() -> list[Path]:
    base = _default_config_dir()
    return [
        base / "config.yml",
        base / "config.yaml",
        base / "config.toml",
        base / "config.json",
    ]


def parse_config_file(path: Path) -> dict[str, Any]:
    raw = path.read_text(encoding="utf-8")
    suffix = path.suffix.lower()
    if suffix in {".yml", ".yaml"}:
        data = yaml.safe_load(raw) or {}
    elif suffix == ".toml":
        data = tomllib.loads(raw)
    elif suffix == ".json":
        data = json.loads(raw)
    else:
        raise ValueError(f"unsupported config format: {path}")

    if not isinstance(data, dict):
        raise ValueError("config root must be a mapping")

    return data


def ensure_default_config_exists(path: Path | None = None) -> Path:
    target = path or default_config_candidates()[0]
    target.parent.mkdir(parents=True, exist_ok=True)
    if target.exists():
        return target

    default = OmniSDKConfig(
        default_instance="default",
        instances={
            "default": OmniInstanceConfig(url="https://omni.example.invalid"),
        },
    )
    target.write_text(yaml.safe_dump(default.model_dump(mode="json"), sort_keys=False), encoding="utf-8")
    return target


def load_config(
    config: ConfigInput | None = None,
    *,
    config_path: str | Path | None = None,
) -> ResolvedConfig:
    if config is not None:
        if isinstance(config, OmniSDKConfig):
            return ResolvedConfig(source="runtime-model", data=config)
        return ResolvedConfig(source="runtime-dict", data=OmniSDKConfig.model_validate(config))

    if config_path is not None:
        path = Path(config_path).expanduser().resolve()
        payload = parse_config_file(path)
        return ResolvedConfig(source="explicit-path", path=path, data=OmniSDKConfig.model_validate(payload))

    env_path = os.getenv(OMNI_SDK_CONFIG_ENV)
    if env_path:
        path = Path(env_path).expanduser().resolve()
        payload = parse_config_file(path)
        return ResolvedConfig(
            source=f"env:{OMNI_SDK_CONFIG_ENV}",
            path=path,
            data=OmniSDKConfig.model_validate(payload),
        )

    for candidate in default_config_candidates():
        if candidate.exists():
            path = candidate.expanduser().resolve()
            payload = parse_config_file(path)
            return ResolvedConfig(source="default-path", path=path, data=OmniSDKConfig.model_validate(payload))

    created = ensure_default_config_exists()
    payload = parse_config_file(created)
    return ResolvedConfig(source="default-created", path=created, data=OmniSDKConfig.model_validate(payload))


def save_config(config: OmniSDKConfig, *, path: Path | None = None) -> Path:
    target = path or default_config_candidates()[0]
    target = target.expanduser()
    target.parent.mkdir(parents=True, exist_ok=True)
    suffix = target.suffix.lower()

    dumped = config.model_dump(mode="json")
    if suffix in {".yml", ".yaml"}:
        content = yaml.safe_dump(dumped, sort_keys=False)
    elif suffix == ".toml":
        raise ValueError("saving TOML is not implemented; use YAML or JSON path")
    elif suffix == ".json":
        content = json.dumps(dumped, indent=2)
    else:
        raise ValueError(f"unsupported config extension for saving: {target}")

    target.write_text(content, encoding="utf-8")
    return target.resolve()
