from __future__ import annotations

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

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

logger = logging.getLogger(__name__)

_bq_client = None


def _project_id() -> str:
    value = os.getenv("FM_FOUNDRYGRAPH_PROJECT_ID") or os.getenv("GOOGLE_CLOUD_PROJECT") or ""
    return value.strip()


def _table_ref() -> str:
    project_id = _project_id()
    if project_id:
        return f"{project_id}.foundrygraph_gold.companies_gold"
    return "foundrygraph_gold.companies_gold"


def _get_bq_client():
    global _bq_client
    project_id = _project_id()
    if _bq_client is None or (project_id and not getattr(_bq_client, "project", None)):
        from google.cloud import bigquery
        _bq_client = bigquery.Client(project=project_id) if project_id else bigquery.Client()
    return _bq_client


def _normalize_domain(domain: str) -> str:
    normalized = (domain or "").strip().lower()
    if normalized.startswith("www."):
        normalized = normalized[4:]
    return normalized


def check_bq_health() -> Dict[str, Any]:
    """
    Health check for BigQuery connectivity.
    Tests connection and verifies table exists with a simple query.
    Returns status dict with connected, project, table, error, and sample_count.
    """
    result = {
        "connected": False,
        "project": _project_id() or "<not set>",
        "table": _table_ref(),
        "error": None,
        "sample_count": 0,
    }

    try:
        client = _get_bq_client()
    except Exception as exc:
        result["error"] = f"Client init failed: {exc}"
        logger.warning("FoundryGraph BQ health check: client init failed - %s", exc)
        return result

    # Test with a known domain that should exist
    test_query = f"""
        SELECT COUNT(*) as cnt
        FROM `{_table_ref()}`
        WHERE domain = 'cisco.com'
    """

    try:
        from google.cloud import bigquery

        project_id = _project_id() or None
        rows = list(client.query(test_query, project=project_id).result())
        count = rows[0].cnt if rows else 0
        result["connected"] = True
        result["sample_count"] = count
        if count == 0:
            result["error"] = "Query succeeded but cisco.com not found - table may be empty"
            logger.warning("FoundryGraph BQ health: connected but cisco.com not in table")
        else:
            logger.info("FoundryGraph BQ health: OK (cisco.com found, count=%d)", count)
    except Exception as exc:
        result["error"] = str(exc)[:200]
        logger.warning("FoundryGraph BQ health check FAILED: %s", exc)

    return result


def _escape_regex(term: str) -> str:
    return re.escape(term)


def lookup_company_by_domain(domain: str) -> Optional[Dict[str, Any]]:
    normalized = _normalize_domain(domain)
    if not normalized:
        return None

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

    query = f"""
        SELECT fg_id, wikidata_id, label, domain, website, quality_score
        FROM `{_table_ref()}`
        WHERE domain = @domain
        LIMIT 1
    """

    try:
        from google.cloud import bigquery

        job_config = bigquery.QueryJobConfig(
            query_parameters=[
                bigquery.ScalarQueryParameter("domain", "STRING", normalized)
            ]
        )
        project_id = _project_id() or None
        rows = list(client.query(query, job_config=job_config, project=project_id).result())
    except Exception as exc:
        # Retry without fg_id if schema not yet updated
        try:
            query = f"""
                SELECT wikidata_id, label, domain, website, quality_score
                FROM `{_table_ref()}`
                WHERE domain = @domain
                LIMIT 1
            """
            rows = list(client.query(query, job_config=job_config, project=project_id).result())
        except Exception:
            logger.warning(
                "FoundryGraph BQ FAILED for %s (project=%s, table=%s): %s",
                normalized,
                _project_id() or "<empty>",
                _table_ref(),
                exc,
            )
            return None

    if not rows:
        return None

    row = rows[0]
    return {
        "fg_id": getattr(row, "fg_id", None),
        "wikidata_id": getattr(row, "wikidata_id", None),
        "label": getattr(row, "label", None),
        "domain": getattr(row, "domain", None),
        "website": getattr(row, "website", None),
        "quality_score": getattr(row, "quality_score", None),
        "source": "foundrygraph_bq",
    }


def lookup_companies_by_domain(domains: List[str]) -> Dict[str, Dict[str, Any]]:
    normalized = sorted({d for d in (_normalize_domain(d) for d in domains or []) if d})
    if not normalized:
        return {}

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

    query = f"""
        SELECT fg_id, wikidata_id, label, domain, website, quality_score
        FROM `{_table_ref()}`
        WHERE domain IN UNNEST(@domains)
    """

    try:
        from google.cloud import bigquery

        job_config = bigquery.QueryJobConfig(
            query_parameters=[
                bigquery.ArrayQueryParameter("domains", "STRING", normalized)
            ]
        )
        project_id = _project_id() or None
        rows = list(client.query(query, job_config=job_config, project=project_id).result())
    except Exception as exc:
        # Retry without fg_id if schema not yet updated
        try:
            query = f"""
                SELECT wikidata_id, label, domain, website, quality_score
                FROM `{_table_ref()}`
                WHERE domain IN UNNEST(@domains)
            """
            rows = list(client.query(query, job_config=job_config, project=project_id).result())
        except Exception:
            logger.warning(
                "FoundryGraph BQ batch FAILED (%d domains, project=%s, table=%s): %s",
                len(normalized),
                _project_id() or "<empty>",
                _table_ref(),
                exc,
            )
            return {}

    results: Dict[str, Dict[str, Any]] = {}
    for row in rows:
        domain = _normalize_domain(getattr(row, "domain", "") or "")
        if not domain:
            continue
        results[domain] = {
            "fg_id": getattr(row, "fg_id", None),
            "wikidata_id": getattr(row, "wikidata_id", None),
            "label": getattr(row, "label", None),
            "domain": getattr(row, "domain", None),
            "website": getattr(row, "website", None),
            "quality_score": getattr(row, "quality_score", None),
            "source": "foundrygraph_bq",
        }

    # Log hit rate so we can distinguish coverage issues from BQ failures
    hit_count = len(results)
    miss_count = len(normalized) - hit_count
    if miss_count > 0:
        logger.info(
            "FoundryGraph BQ batch: %d/%d hits (%d misses)",
            hit_count,
            len(normalized),
            miss_count,
        )
    return results


@lru_cache(maxsize=5000)
def search_companies_by_name_term(term: str, *, limit: int = 50) -> List[Dict[str, Any]]:
    """Return candidate company rows whose labels contain the provided term."""
    normalized = (term or "").strip().lower()
    if not normalized:
        return []

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

    safe_term = _escape_regex(normalized)
    query = f"""
        SELECT fg_id, wikidata_id, label, label_normalized, domain, website, quality_score
        FROM `{_table_ref()}`
        WHERE REGEXP_CONTAINS(label_normalized, @pattern)
        ORDER BY quality_score DESC
        LIMIT {int(limit)}
    """

    try:
        from google.cloud import bigquery

        pattern = rf"(^|\\s){safe_term}(\\s|$)"
        job_config = bigquery.QueryJobConfig(
            query_parameters=[
                bigquery.ScalarQueryParameter("pattern", "STRING", pattern)
            ]
        )
        project_id = _project_id() or None
        rows = list(client.query(query, job_config=job_config, project=project_id).result())
    except Exception as exc:
        # Retry without fg_id if schema not yet updated
        try:
            query = f"""
                SELECT wikidata_id, label, label_normalized, domain, website, quality_score
                FROM `{_table_ref()}`
                WHERE REGEXP_CONTAINS(label_normalized, @pattern)
                ORDER BY quality_score DESC
                LIMIT {int(limit)}
            """
            rows = list(client.query(query, job_config=job_config, project=project_id).result())
        except Exception:
            logger.debug(
                "FoundryGraph BigQuery name search failed for %s (project=%s, table=%s): %s",
                normalized,
                _project_id() or "",
                _table_ref(),
                exc,
            )
            return []

    results: List[Dict[str, Any]] = []
    for row in rows:
        results.append(
            {
                "fg_id": getattr(row, "fg_id", None),
                "wikidata_id": getattr(row, "wikidata_id", None),
                "label": getattr(row, "label", None),
                "label_normalized": getattr(row, "label_normalized", None),
                "domain": getattr(row, "domain", None),
                "website": getattr(row, "website", None),
                "quality_score": getattr(row, "quality_score", None),
                "source": "foundrygraph_bq",
            }
        )
    return results


def lookup_company_by_wikidata_id(wikidata_id: str) -> Optional[Dict[str, Any]]:
    normalized = (wikidata_id or "").strip()
    if not normalized:
        return None

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

    query = f"""
        SELECT fg_id, wikidata_id, status, redirect_to_fg_id
        FROM `{_table_ref()}`
        WHERE wikidata_id = @wikidata_id
        LIMIT 1
    """

    try:
        from google.cloud import bigquery

        job_config = bigquery.QueryJobConfig(
            query_parameters=[
                bigquery.ScalarQueryParameter("wikidata_id", "STRING", normalized)
            ]
        )
        project_id = _project_id() or None
        rows = list(client.query(query, job_config=job_config, project=project_id).result())
    except Exception as exc:
        logger.debug(
            "FoundryGraph BigQuery lookup by wikidata failed for %s (project=%s, table=%s): %s",
            normalized,
            _project_id() or "",
            _table_ref(),
            exc,
        )
        return None

    if not rows:
        return None

    row = rows[0]
    return {
        "fg_id": getattr(row, "fg_id", None),
        "wikidata_id": getattr(row, "wikidata_id", None),
        "status": getattr(row, "status", None),
        "redirect_to_fg_id": getattr(row, "redirect_to_fg_id", None),
    }


def lookup_company_by_fg_id(fg_id: str) -> Optional[Dict[str, Any]]:
    normalized = (fg_id or "").strip()
    if not normalized:
        return None

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

    query = f"""
        SELECT fg_id, wikidata_id, label, domain, website, industries, headquarters, countries, revenue, employees,
               quality_score, status, redirect_to_fg_id, updated_at
        FROM `{_table_ref()}`
        WHERE fg_id = @fg_id
        LIMIT 1
    """

    try:
        from google.cloud import bigquery

        job_config = bigquery.QueryJobConfig(
            query_parameters=[
                bigquery.ScalarQueryParameter("fg_id", "STRING", normalized)
            ]
        )
        project_id = _project_id() or None
        rows = list(client.query(query, job_config=job_config, project=project_id).result())
    except Exception as exc:
        logger.debug(
            "FoundryGraph BigQuery lookup by fg_id failed for %s (project=%s, table=%s): %s",
            normalized,
            _project_id() or "",
            _table_ref(),
            exc,
        )
        return None

    if not rows:
        return None

    row = rows[0]
    return {
        "fg_id": getattr(row, "fg_id", None),
        "wikidata_id": getattr(row, "wikidata_id", None),
        "label": getattr(row, "label", None),
        "domain": getattr(row, "domain", None),
        "website": getattr(row, "website", None),
        "industries": getattr(row, "industries", None),
        "headquarters": getattr(row, "headquarters", None),
        "countries": getattr(row, "countries", None),
        "revenue": getattr(row, "revenue", None),
        "employees": getattr(row, "employees", None),
        "quality_score": getattr(row, "quality_score", None),
        "status": getattr(row, "status", None),
        "redirect_to_fg_id": getattr(row, "redirect_to_fg_id", None),
        "updated_at": getattr(row, "updated_at", None),
        "source": "foundrygraph_bq",
    }
