"""Loader utilities for Wikidata company profile enrichments."""

from __future__ import annotations

import json
import logging
import os
import threading
from pathlib import Path
from typing import Any, Dict, List, Optional

try:
    import duckdb  # type: ignore
except ImportError:  # pragma: no cover - duckdb optional in some environments
    duckdb = None

_PROFILE_CACHE: Optional[Dict[str, Dict]] = None
_DOMAIN_INDEX: Optional[Dict[str, List[str]]] = None
_FAMILY_LINKS: Optional[Dict[str, List[Dict]]] = None
_LOCK = threading.RLock()


def _database_path() -> str:
    """
    Resolve the DuckDB path for FoundryGraph lookups.

    If the configured path does not exist but the FoundryGraph bucket/project
    env vars are present, opportunistically download the latest artifact from
    GCS so the API can serve lookups without a baked-in DB file.
    """

    configured = os.getenv("WIKIDATA_INDEX_PATH", "config/foundrygraph.duckdb")
    path = Path(configured)

    if path.exists():
        return str(path)

    bucket = os.getenv("FM_FOUNDRYGRAPH_BUCKET")
    project = os.getenv("FM_FOUNDRYGRAPH_PROJECT_ID")
    if not bucket or not project:
        return str(path)

    try:
        # Reuse the serving helper to pull the latest artifact
        from google.cloud import storage
        from fmatch.saas.services import foundrygraph_serving

        storage_client = storage.Client(project=project)
        version_info = foundrygraph_serving._get_latest_version(storage_client)  # type: ignore[attr-defined]
        if not version_info or "path" not in version_info:
            return str(path)

        local_path = foundrygraph_serving._download_artifact(  # type: ignore[attr-defined]
            storage_client, version_info
        )
        os.environ["WIKIDATA_INDEX_PATH"] = str(local_path)
        return str(local_path)

    except Exception as exc:  # pragma: no cover - defensive guard for prod
        logging.warning("Failed to download FoundryGraph artifact: %s", exc)
        return str(path)


def _clone_item(item: Any) -> Any:
    """Clone an item safely, handling dicts, strings, and other types."""
    if isinstance(item, dict):
        return dict(item)
    return item  # Strings, ints, etc. are immutable


def _clone_profile(profile: Dict) -> Dict:
    copy = dict(profile)
    # Handle lists that may contain dicts or plain strings (Wikidata IDs)
    industries = profile.get("industries", [])
    copy["industries"] = [_clone_item(item) for item in industries] if isinstance(industries, list) else []

    countries = profile.get("countries", [])
    copy["countries"] = [_clone_item(item) for item in countries] if isinstance(countries, list) else []

    # Headquarters can be a list of dicts, a list of strings, or a single string
    headquarters = profile.get("headquarters", [])
    if isinstance(headquarters, list):
        copy["headquarters"] = [_clone_item(item) for item in headquarters]
    elif isinstance(headquarters, str):
        copy["headquarters"] = [headquarters] if headquarters else []
    else:
        copy["headquarters"] = []

    revenue = profile.get("revenue")
    employees = profile.get("employees")
    copy["revenue"] = dict(revenue) if isinstance(revenue, dict) else None
    copy["employees"] = dict(employees) if isinstance(employees, dict) else None
    copy["domains"] = list(profile.get("domains", []))
    return copy


def _load_data(db_path: str) -> None:
    global _PROFILE_CACHE, _DOMAIN_INDEX, _FAMILY_LINKS

    # Kill switch to disable DuckDB-based wikidata profiles (avoids C-level crashes)
    if os.getenv("FM_DISABLE_WIKIDATA_PROFILES", "").lower() in ("1", "true", "yes"):
        logger = logging.getLogger(__name__)
        logger.info("Wikidata profiles DISABLED via FM_DISABLE_WIKIDATA_PROFILES")
        _PROFILE_CACHE = {}
        _DOMAIN_INDEX = {}
        _FAMILY_LINKS = {}
        return

    if duckdb is None or not Path(db_path).exists():
        _PROFILE_CACHE = {}
        _DOMAIN_INDEX = {}
        _FAMILY_LINKS = {}
        return

    conn = duckdb.connect(db_path, read_only=True)
    try:
        profile_rows = conn.execute(
            """
            SELECT wikidata_id, label, domain, industries, countries, headquarters, revenue, employees
            FROM company_profiles
            """
        ).fetchall()

        profiles: Dict[str, Dict] = {}
        domain_index: Dict[str, List[str]] = {}
        def _safe_json_list(val: Any) -> List:
            """Parse JSON safely, returning [] for None/empty/invalid."""
            if not val or (isinstance(val, str) and not val.strip()):
                return []
            try:
                return json.loads(val)
            except (json.JSONDecodeError, TypeError):
                return []

        def _safe_json_dict(val: Any) -> Optional[Dict]:
            """Parse JSON safely, returning None for None/empty/invalid."""
            if not val or (isinstance(val, str) and not val.strip()):
                return None
            try:
                return json.loads(val)
            except (json.JSONDecodeError, TypeError):
                return None

        for (
            wdid,
            label,
            domain,
            industries,
            countries,
            headquarters,
            revenue,
            employees,
        ) in profile_rows:
            industries_list = _safe_json_list(industries)
            countries_list = _safe_json_list(countries)
            headquarters_list = _safe_json_list(headquarters)
            revenue_dict = _safe_json_dict(revenue)
            employees_dict = _safe_json_dict(employees)
            normalized_domain = (domain or "").lower()
            profiles[wdid] = {
                "wikidata_id": wdid,
                "label": label,
                "domain": normalized_domain,
                "industries": industries_list,
                "countries": countries_list,
                "headquarters": headquarters_list,
                "revenue": revenue_dict,
                "employees": employees_dict,
                "domains": {normalized_domain} if normalized_domain else set(),
            }
            if normalized_domain:
                domain_index.setdefault(normalized_domain, []).append(wdid)

        family_links: Dict[str, List[Dict]] = {}
        try:
            family_rows = conn.execute(
                """
                SELECT wikidata_id, domain, family_id, match_source, has_family
                FROM company_family_links
                """
            ).fetchall()
        except Exception as exc:  # pragma: no cover - defensive against older artifacts
            logging.warning(
                "company_family_links missing in FoundryGraph artifact, skipping families: %s",
                exc,
            )
            family_rows = []

        for wdid, domain, family_id, match_source, has_family in family_rows:
            normalized_domain = (domain or "").lower()
            entry = {
                "wikidata_id": wdid,
                "domain": normalized_domain,
                "family_id": family_id,
                "match_source": match_source,
                "has_family": bool(has_family),
            }
            family_links.setdefault(normalized_domain, []).append(entry)
            domain_index.setdefault(normalized_domain, [])
            if wdid not in domain_index[normalized_domain]:
                domain_index[normalized_domain].append(wdid)
            if wdid in profiles:
                profiles[wdid]["domains"].add(normalized_domain)

        source_rank = {"profile_official_site": 0, "company_domains": 1}
        for domain, links in family_links.items():
            links.sort(
                key=lambda item: source_rank.get(item.get("match_source", ""), 2)
            )
            unique_wids: List[str] = []
            for link in links:
                wdid = link["wikidata_id"]
                if wdid not in unique_wids:
                    unique_wids.append(wdid)
            existing = domain_index.get(domain, [])
            ordered = sorted(
                existing,
                key=lambda wid: unique_wids.index(wid)
                if wid in unique_wids
                else len(unique_wids),
            )
            domain_index[domain] = ordered

        for profile in profiles.values():
            profile["domains"] = (
                sorted(profile["domains"]) if profile["domains"] else []
            )

        _PROFILE_CACHE = profiles
        _DOMAIN_INDEX = domain_index
        _FAMILY_LINKS = {domain: links for domain, links in family_links.items()}
    finally:
        conn.close()


def _ensure_loaded() -> None:
    global _PROFILE_CACHE
    if _PROFILE_CACHE is not None:
        return
    with _LOCK:
        if _PROFILE_CACHE is None:
            _load_data(_database_path())


def get_profile_by_wikidata_id(wikidata_id: str) -> Optional[Dict]:
    _ensure_loaded()
    if not _PROFILE_CACHE:
        return None
    profile = _PROFILE_CACHE.get(wikidata_id)
    return _clone_profile(profile) if profile else None


def get_family_links_for_domain(domain: str) -> List[Dict]:
    _ensure_loaded()
    if not _FAMILY_LINKS:
        return []
    normalized = (domain or "").lower()
    return [dict(link) for link in _FAMILY_LINKS.get(normalized, [])]


def get_profile_for_domain(domain: str) -> Optional[Dict]:
    _ensure_loaded()
    if not _DOMAIN_INDEX:
        return None
    normalized = (domain or "").lower()
    candidates = _DOMAIN_INDEX.get(normalized)
    if not candidates:
        return None
    for wdid in candidates:
        profile = _PROFILE_CACHE.get(wdid)
        if not profile:
            continue
        enriched = _clone_profile(profile)
        enriched["family_links"] = get_family_links_for_domain(normalized)
        enriched["matched_domain"] = normalized
        return enriched
    return None


def reload_cache() -> None:
    global _PROFILE_CACHE, _DOMAIN_INDEX, _FAMILY_LINKS
    with _LOCK:
        _PROFILE_CACHE = None
        _DOMAIN_INDEX = None
        _FAMILY_LINKS = None
    _ensure_loaded()


def get_parent_company_by_domain(domain: str) -> Optional[Dict[str, Any]]:
    """Look up parent company for a given domain via Wikidata relationships."""
    try:
        profile = get_profile_for_domain(domain)
        if not profile:
            return None

        db_path = _database_path()
        if duckdb is None or not Path(db_path).exists():
            return None

        conn = duckdb.connect(db_path, read_only=True)
        try:
            parent_row = conn.execute(
                """
                SELECT p.wikidata_id, p.label, p.domain
                FROM company_relationships r
                JOIN company_profiles p ON r.parent_id = p.wikidata_id
                WHERE r.child_id = ? AND r.relation_type = 'parent'
                LIMIT 1
                """,
                [profile["wikidata_id"]],
            ).fetchone()
        finally:
            conn.close()

        if not parent_row:
            return None

        parent_wdid, parent_label, parent_domain = parent_row
        return {
            "parent_name": parent_label,
            "parent_domain": (parent_domain or "").lower(),
            "parent_wikidata_id": parent_wdid,
            "child_wikidata_id": profile["wikidata_id"],
        }

    except Exception:
        return None


def get_all_family_links() -> Dict[str, str]:
    """Return all domain?family_id mappings from Wikidata."""
    try:
        import os

        db_path = os.getenv("WIKIDATA_INDEX_PATH", "config/foundrygraph.duckdb")

        if duckdb is None or not Path(db_path).exists():
            return {}

        conn = duckdb.connect(db_path, read_only=True)
        try:
            rows = conn.execute(
                """
                SELECT DISTINCT domain, family_id
                FROM company_family_links
                WHERE domain IS NOT NULL
                  AND family_id IS NOT NULL
                  AND has_family = true
                """
            ).fetchall()
        finally:
            conn.close()

        result: Dict[str, str] = {}
        for domain, family_id in rows:
            normalized_domain = (domain or "").lower().strip()
            if normalized_domain:
                result[normalized_domain] = family_id

        return result

    except Exception as exc:
        import logging

        logging.debug(f"Failed to load Wikidata family links: {exc}")
        return {}
