"""Enrich company data from FoundryGraph profiles."""

from __future__ import annotations

import json
import logging
import os
from functools import lru_cache
from typing import Any, Dict, List, Optional, TYPE_CHECKING

from ..core.psl_root import registrable_root

__transform_id__ = "enrich_from_foundrygraph"
__version__ = "1.0.0"
__updated__ = "2025-12-30"

log = logging.getLogger(__name__)

# Lazy import google.cloud.bigquery to avoid GCP credential discovery at startup
if TYPE_CHECKING:
    pass

_bq_client = None


def _get_bq_client():
    global _bq_client
    if _bq_client is None:
        from google.cloud import bigquery

        project_id = os.getenv("FM_FOUNDRYGRAPH_PROJECT_ID") or os.getenv(
            "GOOGLE_CLOUD_PROJECT"
        )
        _bq_client = bigquery.Client(project=project_id) if project_id else bigquery.Client()
    return _bq_client


def _table_ref(dataset: str, table: str) -> str:
    project_id = os.getenv("FM_FOUNDRYGRAPH_PROJECT_ID") or os.getenv(
        "GOOGLE_CLOUD_PROJECT"
    )
    if project_id:
        return f"{project_id}.{dataset}.{table}"
    return f"{dataset}.{table}"


@lru_cache(maxsize=1000)
def normalize_domain(url_or_domain: str) -> str:
    """Extract and normalize domain from URL or domain string."""
    if not url_or_domain:
        return ""

    s = url_or_domain.strip().lower()
    if "://" not in s:
        s = "http://" + s

    try:
        from urllib.parse import urlparse

        parsed = urlparse(s)
        domain = parsed.hostname or parsed.path.split("/")[0]
        if domain and domain.startswith("www."):
            domain = domain[4:]
        root = registrable_root(domain)
        return root or domain or ""
    except Exception:
        return ""


def _looks_like_domain(value: str) -> bool:
    if not value:
        return False
    lowered = value.lower()
    if "://" in lowered or "/" in lowered:
        return True
    if "@" in lowered:
        return True
    return "." in lowered


def _safe_json_list(value: Any) -> List[Any]:
    if not value or (isinstance(value, str) and not value.strip()):
        return []
    if isinstance(value, list):
        return value
    if isinstance(value, str):
        try:
            return json.loads(value)
        except (json.JSONDecodeError, TypeError):
            return []
    return []


def _safe_json_dict(value: Any) -> Optional[Dict[str, Any]]:
    if not value or (isinstance(value, str) and not value.strip()):
        return None
    if isinstance(value, dict):
        return value
    if isinstance(value, str):
        try:
            parsed = json.loads(value)
        except (json.JSONDecodeError, TypeError):
            return None
        return parsed if isinstance(parsed, dict) else None
    return None


def _extract_labels(items: List[Any]) -> List[str]:
    labels: List[str] = []
    for item in items:
        if isinstance(item, dict):
            label = item.get("label")
            if label:
                labels.append(str(label))
        elif isinstance(item, str):
            labels.append(item)
    return labels


def _coerce_number(value: Any) -> Optional[float]:
    if value is None:
        return None
    if isinstance(value, (int, float)):
        return float(value)
    if isinstance(value, str):
        cleaned = value.replace(",", "").replace("$", "").strip()
        if not cleaned:
            return None
        try:
            return float(cleaned)
        except ValueError:
            return None
    return None


def _coerce_int(value: Any) -> Optional[int]:
    number = _coerce_number(value)
    if number is None:
        return None
    return int(number)


def _blank_result(domain: str = "") -> Dict[str, Any]:
    return {
        "canonical_name": "",
        "domain": domain or "",
        "wikidata_id": "",
        "industries": "",
        "headquarters": "",
        "country": "",
        "revenue_amount": "",
        "revenue_currency": "",
        "revenue_year": "",
        "employee_count": "",
        "stock_ticker": "",
        "stock_exchange": "",
        "parent_company": "",
        "parent_domain": "",
        "twitter_handle": "",
        "youtube_channel": "",
    }


def _resolve_domain(value: str, input_type: str) -> Optional[str]:
    effective_type = (input_type or "domain").strip().lower()
    if effective_type not in ("domain", "company_name"):
        effective_type = "domain" if _looks_like_domain(value) else "company_name"

    if effective_type == "domain":
        return normalize_domain(value)

    try:
        from .company_domain_resolver import resolve_company_domain
    except Exception as exc:
        log.debug("Company resolver unavailable: %s", exc)
        return None

    try:
        resolution = resolve_company_domain(value, mode="balanced", use_llm=False)
    except Exception as exc:
        log.debug("Company resolver failed for %s: %s", value, exc)
        return None

    data = resolution.get("value") if isinstance(resolution, dict) else None
    data = data if isinstance(data, dict) else {}
    confidence = (data.get("confidence") or "").lower()
    if confidence == "low":
        return None
    return normalize_domain(data.get("domain") or "")


def _extract_exchange(qualifier: Any) -> str:
    if not qualifier:
        return ""
    if isinstance(qualifier, str):
        try:
            qualifier = json.loads(qualifier)
        except Exception:
            return ""
    if not isinstance(qualifier, dict):
        return ""
    for key in ("exchange", "stock_exchange", "market", "primary_exchange"):
        value = qualifier.get(key)
        if value:
            return str(value)
    if len(qualifier) == 1:
        return str(next(iter(qualifier.values())))
    return ""


def _query_external_ids_bq(wikidata_id: str) -> Dict[str, str]:
    result: Dict[str, str] = {}
    if not wikidata_id:
        return result

    try:
        client = _get_bq_client()
    except Exception as exc:
        log.debug("FoundryGraph BigQuery client unavailable: %s", exc)
        return result

    table_ref = _table_ref("foundrygraph_silver", "external_ids")
    try:
        from google.cloud import bigquery

        query = f"""
            SELECT id_type, id_value, qualifier
            FROM `{table_ref}`
            WHERE wikidata_id = @wikidata_id
              AND id_type IN ('ticker', 'stock_exchange', 'twitter_handle', 'youtube_channel_id')
        """
        job_config = bigquery.QueryJobConfig(
            query_parameters=[
                bigquery.ScalarQueryParameter("wikidata_id", "STRING", wikidata_id)
            ]
        )
        rows = list(client.query(query, job_config=job_config).result())
    except Exception as exc:
        log.debug("External ID lookup failed: %s", exc)
        return result

    for row in rows:
        id_type = getattr(row, "id_type", None)
        id_value = getattr(row, "id_value", None)
        qualifier = getattr(row, "qualifier", None)
        if id_type == "ticker":
            if id_value and not result.get("stock_ticker"):
                result["stock_ticker"] = id_value
            exchange = _extract_exchange(qualifier)
            if exchange and not result.get("stock_exchange"):
                result["stock_exchange"] = exchange
        elif id_type == "stock_exchange" and id_value and not result.get("stock_exchange"):
            result["stock_exchange"] = id_value
        elif id_type == "twitter_handle" and id_value:
            handle = id_value
            if handle and not handle.startswith("@"):
                handle = "@" + handle
            result["twitter_handle"] = handle
        elif id_type == "youtube_channel_id" and id_value:
            result["youtube_channel"] = id_value

    return result


def _query_profile_bq(domain: str) -> Optional[Dict[str, Any]]:
    if not domain:
        return None
    try:
        client = _get_bq_client()
    except Exception as exc:
        log.debug("FoundryGraph BigQuery client unavailable: %s", exc)
        return None

    companies = _table_ref("foundrygraph_gold", "companies_gold")
    try:
        from google.cloud import bigquery

        query = f"""
            SELECT c.wikidata_id, c.label, c.domain, c.industries, c.countries,
                   c.headquarters, c.revenue, c.employees,
                   p.label AS parent_label, p.domain AS parent_domain
            FROM `{companies}` c
            LEFT JOIN `{companies}` p
              ON c.parent_org = p.wikidata_id
            WHERE c.domain = @domain
            LIMIT 1
        """
        job_config = bigquery.QueryJobConfig(
            query_parameters=[
                bigquery.ScalarQueryParameter("domain", "STRING", domain)
            ]
        )
        rows = list(client.query(query, job_config=job_config).result())
    except Exception as exc:
        log.debug("FoundryGraph BigQuery profile lookup failed: %s", exc)
        try:
            query = f"""
                SELECT wikidata_id, label, domain, industries, countries,
                       headquarters, revenue, employees
                FROM `{companies}`
                WHERE domain = @domain
                LIMIT 1
            """
            job_config = bigquery.QueryJobConfig(
                query_parameters=[
                    bigquery.ScalarQueryParameter("domain", "STRING", domain)
                ]
            )
            rows = list(client.query(query, job_config=job_config).result())
        except Exception as retry_exc:
            log.debug("FoundryGraph BigQuery profile fallback failed: %s", retry_exc)
            return None

    if not rows:
        return None
    row = rows[0]
    return {
        "wikidata_id": getattr(row, "wikidata_id", None),
        "label": getattr(row, "label", None),
        "domain": getattr(row, "domain", None),
        "industries": getattr(row, "industries", None),
        "countries": getattr(row, "countries", None),
        "headquarters": getattr(row, "headquarters", None),
        "revenue": getattr(row, "revenue", None),
        "employees": getattr(row, "employees", None),
        "parent_label": getattr(row, "parent_label", None),
        "parent_domain": getattr(row, "parent_domain", None),
    }


def _query_domain_fallback_bq(domain: str) -> Optional[Dict[str, Any]]:
    if not domain:
        return None
    try:
        client = _get_bq_client()
    except Exception as exc:
        log.debug("FoundryGraph BigQuery client unavailable: %s", exc)
        return None

    table_ref = _table_ref("foundrygraph_gold", "domain_lookup")
    try:
        from google.cloud import bigquery

        query = f"""
            SELECT wikidata_id, label
            FROM `{table_ref}`
            WHERE domain = @domain
            LIMIT 1
        """
        job_config = bigquery.QueryJobConfig(
            query_parameters=[
                bigquery.ScalarQueryParameter("domain", "STRING", domain)
            ]
        )
        rows = list(client.query(query, job_config=job_config).result())
    except Exception as exc:
        log.debug("FoundryGraph BigQuery domain lookup failed: %s", exc)
        return None

    if not rows:
        return None
    row = rows[0]
    return {
        "wikidata_id": getattr(row, "wikidata_id", None),
        "label": getattr(row, "label", None),
    }


def enrich_from_foundrygraph(
    value: str,
    input_type: str = "domain",
) -> Dict[str, Any]:
    """
    Enrich a company with FoundryGraph data.

    Args:
        value: Domain or company name (based on input_type)
        input_type: "domain" or "company_name"

    Returns:
        Dict with canonical_name, domain, wikidata_id, industries, headquarters,
        country, revenue, employees, stock info, parent, and social handles.
    """
    raw_value = (value or "").strip()
    if not raw_value:
        return _blank_result()

    domain = _resolve_domain(raw_value, input_type) or ""
    if not domain:
        return _blank_result()

    result = _blank_result(domain)

    profile = _query_profile_bq(domain)
    if profile:
        result["canonical_name"] = profile.get("label") or ""
        result["wikidata_id"] = profile.get("wikidata_id") or ""
        profile_domain = profile.get("domain") or ""
        if profile_domain:
            result["domain"] = profile_domain

        industry_labels = _extract_labels(_safe_json_list(profile.get("industries")))
        if industry_labels:
            result["industries"] = ", ".join(industry_labels)

        hq_labels = _extract_labels(_safe_json_list(profile.get("headquarters")))
        if hq_labels:
            result["headquarters"] = hq_labels[0]

        country_labels = _extract_labels(_safe_json_list(profile.get("countries")))
        if country_labels:
            result["country"] = country_labels[0]

        revenue_payload = _safe_json_dict(profile.get("revenue")) or {}
        amount = _coerce_number(revenue_payload.get("amount") or revenue_payload.get("value"))
        if amount is not None:
            result["revenue_amount"] = amount
        currency = str(
            revenue_payload.get("currency")
            or revenue_payload.get("currency_code")
            or ""
        ).strip()
        if currency:
            result["revenue_currency"] = currency
        year = str(
            revenue_payload.get("year")
            or revenue_payload.get("as_of")
            or ""
        ).strip()
        if year:
            result["revenue_year"] = year

        employees_payload = _safe_json_dict(profile.get("employees")) or {}
        employee_count = _coerce_int(
            employees_payload.get("count") or employees_payload.get("value")
        )
        if employee_count is not None:
            result["employee_count"] = employee_count

        parent_label = profile.get("parent_label") or ""
        parent_domain = profile.get("parent_domain") or ""
        if parent_label:
            result["parent_company"] = parent_label
        if parent_domain:
            result["parent_domain"] = parent_domain.lower()
    else:
        fallback = _query_domain_fallback_bq(domain)
        if fallback:
            result["wikidata_id"] = fallback.get("wikidata_id") or ""
            result["canonical_name"] = fallback.get("label") or ""

    wikidata_id = result.get("wikidata_id") or ""
    if wikidata_id:
        result.update(_query_external_ids_bq(wikidata_id))

    return result
