"""CRD/resource catalog resolver for CLI and SDK helpers."""

from __future__ import annotations

import json
from dataclasses import dataclass
from typing import Any

from omni.generated.crd_catalog import CRD_CATALOG
from omni.models.crd_overrides import CRD_OVERRIDES


@dataclass(slots=True)
class CRDInfo:
    resource_type: str
    const_name: str
    description: str
    default_namespace: str | None
    print_columns: list[str]
    aliases: list[str]
    source_file: str | None


def _normalize(token: str) -> str:
    return token.strip().lower().replace("_", "").replace("-", "").replace(".", "")


def _to_info(resource_type: str, payload: dict[str, Any]) -> CRDInfo:
    return CRDInfo(
        resource_type=resource_type,
        const_name=str(payload.get("const_name", "")),
        description=str(payload.get("description", f"Omni resource type `{resource_type}`.")),
        default_namespace=(str(payload["default_namespace"]) if payload.get("default_namespace") else None),
        print_columns=[str(item) for item in payload.get("print_columns", []) if isinstance(item, str)],
        aliases=[str(item) for item in payload.get("aliases", []) if isinstance(item, str)],
        source_file=(str(payload["source_file"]) if payload.get("source_file") else None),
    )


def _merge_catalog() -> dict[str, dict[str, Any]]:
    merged: dict[str, dict[str, Any]] = {}
    for key, value in CRD_CATALOG.items():
        if not isinstance(value, dict):
            continue
        merged[key] = dict(value)
    for resource_type, override in CRD_OVERRIDES.items():
        item = dict(merged.get(resource_type, {}))
        item.update({k: v for k, v in override.items() if k != "aliases"})
        item_aliases = item.get("aliases", [])
        override_aliases = override.get("aliases", [])
        aliases: set[str] = set()
        if isinstance(item_aliases, list):
            aliases.update(str(alias) for alias in item_aliases)
        if isinstance(override_aliases, list):
            aliases.update(str(alias) for alias in override_aliases)
        item["aliases"] = sorted(aliases)
        item["type"] = resource_type
        merged[resource_type] = item
    return merged


class CRDCatalogResolver:
    def __init__(self) -> None:
        self._catalog = _merge_catalog()
        self._type_index = {
            resource_type: _to_info(resource_type, payload)
            for resource_type, payload in self._catalog.items()
        }
        self._alias_index: dict[str, str] = {}
        for resource_type, info in self._type_index.items():
            self._alias_index[_normalize(resource_type)] = resource_type
            for alias in info.aliases:
                self._alias_index[_normalize(alias)] = resource_type

    def list(self) -> list[CRDInfo]:
        return sorted(self._type_index.values(), key=lambda item: item.resource_type)

    def resolve(self, query: str, *, client: Any | None = None) -> CRDInfo | None:
        stripped = query.strip()
        if not stripped:
            return None

        if stripped in self._type_index:
            return self._type_index[stripped]

        normalized = _normalize(stripped)
        resource_type = self._alias_index.get(normalized)
        if resource_type:
            return self._type_index[resource_type]

        for candidate in self._type_index.values():
            if normalized in _normalize(candidate.resource_type):
                return candidate
            if any(normalized in _normalize(alias) for alias in candidate.aliases):
                return candidate

        if client is not None:
            runtime = self._resolve_from_runtime(client, stripped)
            if runtime:
                return runtime

        return None

    def _resolve_from_runtime(self, client: Any, query: str) -> CRDInfo | None:
        payload = client.resources.list(
            {
                "namespace": "meta",
                "type": "ResourceDefinitions.meta.cosi.dev",
            }
        )
        items = payload.get("items")
        if not isinstance(items, list):
            return None

        normalized_query = _normalize(query)
        for raw in items:
            item: dict[str, Any] | None
            if isinstance(raw, str):
                try:
                    decoded = json.loads(raw)
                except json.JSONDecodeError:
                    continue
                item = decoded if isinstance(decoded, dict) else None
            elif isinstance(raw, dict):
                item = raw
            else:
                item = None

            if not item:
                continue
            metadata = item.get("metadata")
            if not isinstance(metadata, dict):
                continue
            resource_type = str(metadata.get("id", ""))
            if not resource_type:
                continue
            if normalized_query not in _normalize(resource_type):
                continue
            return CRDInfo(
                resource_type=resource_type,
                const_name="",
                description=f"Runtime-discovered resource definition for `{resource_type}`.",
                default_namespace="meta",
                print_columns=[],
                aliases=[],
                source_file=None,
            )

        return None

    def explain(self, query: str, *, client: Any | None = None) -> str:
        info = self.resolve(query, client=client)
        if info is None:
            return f"Unknown resource type '{query}'. Try 'omnipy crd list' for available types."

        aliases = ", ".join(info.aliases[:8]) if info.aliases else "none"
        default_ns = info.default_namespace or "default"
        columns = ", ".join(info.print_columns) if info.print_columns else "none"
        return (
            f"{info.resource_type}\n"
            f"Description: {info.description}\n"
            f"Default namespace: {default_ns}\n"
            f"Aliases: {aliases}\n"
            f"Print columns: {columns}"
        )


def resolver() -> CRDCatalogResolver:
    return CRDCatalogResolver()
