from __future__ import annotations

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

from ..common.tree_tools import normalize_navexa_tree_document


def _read_json(path: Path) -> Dict[str, Any]:
    with path.open("r", encoding="utf-8") as f:
        loaded = json.load(f)
    if not isinstance(loaded, dict):
        raise ValueError(f"Expected JSON object in {path}")
    return loaded


def _resolve_path(source: Any) -> Optional[Path]:
    if isinstance(source, (str, Path)):
        return Path(source)
    if hasattr(source, "out_dir"):
        return Path(getattr(source, "out_dir"))
    return None


def _load_document_like(source: Any, file_name: str) -> Dict[str, Any]:
    if isinstance(source, dict):
        return copy.deepcopy(source)
    if hasattr(source, "tree_navexa"):
        return copy.deepcopy(getattr(source, "tree_navexa"))

    path = _resolve_path(source)
    if path is None:
        raise ValueError("Unsupported source type. Use dict, file path, directory path, or IndexResult.")

    if path.is_dir():
        target = path / file_name
        if not target.exists():
            raise FileNotFoundError(f"Expected file does not exist: {target}")
        return _read_json(target)

    if path.is_file():
        return _read_json(path)

    raise FileNotFoundError(f"Source path does not exist: {path}")


def _validate_navexa_tree_document(document: Dict[str, Any]) -> None:
    required_keys = {"doc_id", "doc_name", "structure", "pipeline"}
    missing = sorted(required_keys - set(document.keys()))
    if missing:
        raise ValueError(f"Invalid Navexa tree document; missing required keys: {missing}")
    if not isinstance(document.get("structure"), list):
        raise ValueError("Invalid Navexa tree document; 'structure' must be a list.")


def fetch_document_tree(source: Any, file_name: str = "tree_navexa.json") -> Dict[str, Any]:
    document = _load_document_like(source, file_name=file_name)
    normalized = normalize_navexa_tree_document(document)
    _validate_navexa_tree_document(normalized)
    return normalized


def fetch_validation_report(source: Any) -> Optional[Dict[str, Any]]:
    if isinstance(source, dict) and "metrics" in source:
        return copy.deepcopy(source)
    if hasattr(source, "validation_report"):
        return copy.deepcopy(getattr(source, "validation_report"))

    path = _resolve_path(source)
    if path is None:
        return None

    if path.is_file():
        loaded = _read_json(path)
        return loaded if "metrics" in loaded else None

    if path.is_dir():
        target = path / "validation_report.json"
        if not target.exists():
            return None
        return _read_json(target)

    return None


def fetch_compat_tree(source: Any) -> Optional[Dict[str, Any]]:
    if isinstance(source, dict) and "structure" in source and "pipeline" not in source:
        return copy.deepcopy(source)
    if hasattr(source, "tree_legacy_compat"):
        return copy.deepcopy(getattr(source, "tree_legacy_compat"))

    path = _resolve_path(source)
    if path is None:
        return None

    if path.is_file():
        loaded = _read_json(path)
        return loaded if "structure" in loaded else None

    if path.is_dir():
        target = path / "tree_legacy_compat.json"
        if not target.exists():
            return None
        return _read_json(target)

    return None
