from __future__ import annotations

import hashlib
import os
import logging
from dataclasses import dataclass
from typing import Any

from .atlas_model import Entity, Identifier
from .atlas_store import load_entity_by_subject, save_entity_minimal
from .wikidata import WikidataSearch


ATLAS = "http://world.eu.org/atlas_ontology#"
DEFAULT_ENDPOINT = os.getenv("ATLAS_VIRTUOSO_MCP_SSE_URL", "http://192.168.0.249:8501/mcp/sse")
DEFAULT_UPDATE_ENDPOINT = os.getenv("ATLAS_VIRTUOSO_MCP_SSE_URL", DEFAULT_ENDPOINT)
DEBUG_LOGS = os.getenv("ATLAS_DEBUG_LOGS", "false").lower() in {"1", "true", "yes", "on"}

logger = logging.getLogger(__name__)


def _hash_id(subject: str) -> str:
    return hashlib.sha1(subject.strip().lower().encode("utf-8")).hexdigest()[:16]


def _entity_iri(atlas_id: str) -> str:
    return f"atlas_data:entity_{atlas_id}"


async def _wikidata_lookup(subject: str) -> dict[str, Any] | None:
    search = WikidataSearch({"search": subject, "limit": 1})
    result = await search.quick_resolve(subject, limit=1)
    items = result.get("results", [])
    return items[0] if items else None


def _infer_atlas_type(label: str | None, description: str | None) -> str:
    text = f"{label or ''} {description or ''}".lower()
    if any(k in text for k in ["president", "person", "singer", "composer", "human", "actor", "writer"]):
        return "atlas:Person"
    if any(k in text for k in ["city", "town", "village", "country", "state", "location", "place"]):
        return "atlas:Location"
    if any(k in text for k in ["company", "organization", "organisation", "institution", "foundation", "band"]):
        return "atlas:Organization"
    return "atlas:Other"


def _entity_from_wikidata(subject: str, wd: dict[str, Any]) -> Entity:
    atlas_id = _hash_id(subject)
    label = wd.get("label") or subject
    description = wd.get("description")
    qid = wd.get("id")
    entity_type = _infer_atlas_type(label, description)

    ent = Entity(
        id=atlas_id,
        label=label,
        description=description,
        type=entity_type,
        aliases=[subject] if subject.lower() != label.lower() else [],
        identifiers=[Identifier(scheme="wikidata-qid", value=qid)] if qid else [],
        needs_curation=True,
    )
    return ent


def _flatten_exception_details(exc: BaseException) -> list[str]:
    parts = [f"{type(exc).__name__}: {exc}"]
    nested = getattr(exc, "exceptions", None)
    if nested:
        for sub in nested:
            parts.extend(_flatten_exception_details(sub))
    return parts


async def _persist_entity(entity: Entity) -> None:
    await save_entity_minimal(entity, DEFAULT_UPDATE_ENDPOINT)


async def _load_entity(subject: str) -> dict[str, Any] | None:
    return await load_entity_by_subject(subject, DEFAULT_ENDPOINT)


@dataclass
class ResolveService:
    load_entity_fn: Any = _load_entity
    wikidata_lookup_fn: Any = _wikidata_lookup
    persist_entity_fn: Any = _persist_entity

    async def resolve(self, *, subject: str, context: dict[str, Any] | None = None,
                      constraints: dict[str, Any] | None = None,
                      hints: dict[str, Any] | None = None,
                      debug: dict[str, Any] | None = None) -> dict[str, Any]:
        try:
            subject = (subject or "").strip()
            if not subject:
                return {"status": "not_found"}

            if DEBUG_LOGS:
                logger.info("resolve start subject=%s", subject)

            stored = await self.load_entity_fn(subject)
            if stored:
                if DEBUG_LOGS:
                    logger.info("store hit subject=%s atlas_id=%s", subject, stored.get("atlas_id"))
                return {
                    "status": "resolved",
                    "atlas_id": stored.get("atlas_id"),
                    "label": stored.get("label"),
                    "type": stored.get("type"),
                    "wikidata_id": stored.get("wikidata_id"),
                    "alias": stored.get("alias") or subject,
                }

            wd = await self.wikidata_lookup_fn(subject)
            if not wd:
                if DEBUG_LOGS:
                    logger.info("wikidata miss subject=%s", subject)
                return {"status": "not_found"}

            entity = _entity_from_wikidata(subject, wd)
            if DEBUG_LOGS:
                logger.info(
                    "wikidata hit subject=%s qid=%s atlas_id=%s type=%s",
                    subject,
                    wd.get("id"),
                    entity.id,
                    entity.type,
                )
            await self.persist_entity_fn(entity)

            return {
                "status": "resolved",
                "atlas_id": entity.id,
                "label": entity.label,
                "type": entity.type,
                "wikidata_id": wd.get("id"),
                "alias": subject,
            }
        except Exception as exc:
            detail = " | ".join(_flatten_exception_details(exc))
            return {
                "status": "error",
                "error": {"code": "RESOLVE_FAILED", "message": detail},
            }
