"""Load and validate Looker spec files."""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any, Dict

from g_gremlin.errors import missing_pyyaml_error
from g_gremlin.looker.errors import LookerSpecError
from g_gremlin.looker.spec_models import (
    LookerDashboardSpecV1,
    LookerFilterSpecV1,
    LookerLookSpecV1,
    LookerTileSpecV1,
    parse_dashboard_spec,
    parse_filter_spec,
    parse_look_spec,
    parse_tile_spec,
)


def _load_yaml(path: Path) -> Dict[str, Any]:
    try:
        import yaml  # type: ignore
    except Exception as exc:  # pragma: no cover - environment dependent
        raise missing_pyyaml_error() from exc
    raw = path.read_text(encoding="utf-8")
    data = yaml.safe_load(raw)
    if data is None:
        return {}
    if not isinstance(data, dict):
        raise LookerSpecError(f"Spec file must contain an object at top level: {path}")
    return data


def _load_json(path: Path) -> Dict[str, Any]:
    raw = path.read_text(encoding="utf-8")
    try:
        data = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise LookerSpecError(f"Invalid JSON spec: {exc}") from exc
    if not isinstance(data, dict):
        raise LookerSpecError(f"Spec file must contain an object at top level: {path}")
    return data


def load_spec_data(path: str | Path) -> Dict[str, Any]:
    """Load raw spec content from JSON or YAML."""
    spec_path = Path(path)
    if not spec_path.exists():
        raise LookerSpecError(f"Spec file not found: {spec_path}")

    suffix = spec_path.suffix.lower()
    if suffix in {".yaml", ".yml"}:
        return _load_yaml(spec_path)
    if suffix == ".json":
        return _load_json(spec_path)

    # Fallback by trying JSON first, then YAML.
    try:
        return _load_json(spec_path)
    except LookerSpecError:
        return _load_yaml(spec_path)


def load_dashboard_spec(path: str | Path) -> LookerDashboardSpecV1:
    """Load and validate ``LookerDashboardSpecV1`` from disk."""
    return parse_dashboard_spec(load_spec_data(path))


def load_look_spec(path: str | Path) -> LookerLookSpecV1:
    """Load and validate ``LookerLookSpecV1`` from disk."""
    return parse_look_spec(load_spec_data(path))


def load_tile_spec(path: str | Path) -> LookerTileSpecV1:
    """Load and validate ``LookerTileSpecV1`` from disk."""
    return parse_tile_spec(load_spec_data(path))


def load_filter_spec(path: str | Path) -> LookerFilterSpecV1:
    """Load and validate ``LookerFilterSpecV1`` from disk."""
    return parse_filter_spec(load_spec_data(path))

