"""Enrichment orchestrator for promotion candidates."""

import logging
import threading
import time
from collections import Counter, defaultdict
from dataclasses import asdict, dataclass, field
from typing import Dict, Optional

from .business_description import BusinessDescriptionOperator
from .compliance import ComplianceManager
from .cross_reference import CrossReferenceOperator
from .parked_detection import ParkedDomainDetector
from .semantic_gate import compute_enrichment_boost, compose_total_boost, semantic_gate
from .website_content import WebsiteContentOperator
from .website_liveness import WebsiteLivenessOperator

logger = logging.getLogger(__name__)


@dataclass
class EnrichmentResult:
    """Complete enrichment result for a candidate."""

    # Overall assessment
    enrichment_complete: bool
    confidence_boost: float  # Amount to add to base score (0.0-0.3)

    # Individual results
    website_live: bool
    website_status_code: Optional[int] = None
    website_error: Optional[str] = None
    website_https: bool = False
    website_tls_valid: Optional[bool] = None
    website_tls_error: Optional[str] = None
    website_response_time_ms: Optional[float] = None
    website_final_url: Optional[str] = None
    website_final_host: Optional[str] = None
    website_final_etld1: Optional[str] = None
    website_compliance_reason: Optional[str] = None

    content_extracted: bool = False
    website_title: Optional[str] = None
    website_description: Optional[str] = None
    content_error: Optional[str] = None
    content_language: Optional[str] = None
    content_is_english: Optional[bool] = None
    content_source_url: Optional[str] = None
    is_parked_domain: Optional[bool] = None
    parked_indicator: Optional[str] = None
    timings_ms: Dict[str, float] = field(default_factory=dict)

    target_business_description: Optional[str] = None
    target_industry: Optional[str] = None

    candidate_business_description: Optional[str] = None
    candidate_industry: Optional[str] = None

    descriptions_match: bool = False
    description_match_score: float = 0.0
    mismatch_reason: Optional[str] = None


class EnrichmentOrchestrator:
    """Orchestrate enrichment operations for promotion candidates."""

    def __init__(
        self,
        enable_liveness: bool = True,
        enable_content: bool = True,
        enable_description: bool = True,
        enable_cross_ref: bool = True,
        enable_parked_detection: bool = True,
        use_compliance: bool = True,
        compliance_manager: Optional[ComplianceManager] = None,
    ):
        self.enable_liveness = enable_liveness
        self.enable_content = enable_content
        self.enable_description = enable_description
        self.enable_cross_ref = enable_cross_ref
        self.enable_parked_detection = enable_parked_detection
        self.use_compliance = use_compliance

        self.compliance = (
            compliance_manager
            if compliance_manager is not None
            else (ComplianceManager() if use_compliance else None)
        )

        # Initialize operators
        self.liveness_op = (
            WebsiteLivenessOperator(compliance=self.compliance)
            if enable_liveness
            else None
        )
        self.content_op = (
            WebsiteContentOperator(compliance=self.compliance)
            if enable_content
            else None
        )
        self.description_op = (
            BusinessDescriptionOperator() if enable_description else None
        )
        self.cross_ref_op = CrossReferenceOperator() if enable_cross_ref else None
        self.parked_detector = (
            ParkedDomainDetector() if enable_parked_detection else None
        )
        self._metrics_lock = threading.Lock()
        self.metrics = {
            "timing_ms": defaultdict(float),
            "results": Counter(),
            "errors": Counter(),
        }

    def enrich(
        self,
        target_company_name: str,
        candidate_domain: Optional[str],
        candidate_company_name: Optional[str] = None,
    ) -> EnrichmentResult:
        """
        Enrich a promotion candidate with additional data.

        Args:
            target_company_name: Company name from Fortune 1000
            candidate_domain: Domain from candidate (may be None)
            candidate_company_name: Name from Wikidata/source (defaults to target if None)

        Returns:
            EnrichmentResult with gathered signals
        """
        if not candidate_company_name:
            candidate_company_name = target_company_name

        timings: Dict[str, float] = {}
        total_start = time.perf_counter()

        result = EnrichmentResult(
            enrichment_complete=False,
            confidence_boost=0.0,
            website_live=False,
        )

        # Step 1: Check website liveness
        liveness_result = None
        if self.enable_liveness and candidate_domain:
            logger.info(f"Checking liveness for {candidate_domain}")
            segment_start = time.perf_counter()
            liveness_result = self.liveness_op.check(candidate_domain)
            timings["liveness"] = (time.perf_counter() - segment_start) * 1000
            result.website_live = liveness_result.is_live
            result.website_status_code = liveness_result.status_code
            result.website_error = liveness_result.error
            result.website_https = liveness_result.has_ssl
            result.website_tls_valid = liveness_result.tls_valid
            result.website_tls_error = liveness_result.tls_error
            result.website_response_time_ms = liveness_result.response_time_ms
            result.website_final_url = liveness_result.final_url
            result.website_final_host = liveness_result.final_host
            result.website_final_etld1 = liveness_result.final_etld1
            result.website_compliance_reason = liveness_result.compliance_reason

            if not liveness_result.is_live:
                logger.warning(
                    f"Website {candidate_domain} not live: {liveness_result.error}"
                )
                # Still try to continue with other enrichment
        else:
            logger.info("Liveness check disabled or no domain")

        # Step 2: Extract website content (if live)
        content_result = None
        if self.enable_content and liveness_result and liveness_result.is_live:
            logger.info(f"Extracting content from {liveness_result.final_url}")
            segment_start = time.perf_counter()
            content_result = self.content_op.extract(liveness_result.final_url)
            timings["content"] = (time.perf_counter() - segment_start) * 1000
            result.content_extracted = content_result.success
            result.website_title = content_result.title
            result.website_description = content_result.description
            result.content_error = content_result.error
            result.content_language = content_result.language
            result.content_is_english = content_result.is_english
            result.content_source_url = content_result.source_url

            if not content_result.success:
                logger.warning(f"Content extraction failed: {content_result.error}")
            elif self.enable_parked_detection and self.parked_detector:
                park_result = self.parked_detector.detect(
                    title=content_result.title,
                    description=content_result.description,
                    body_snippet=content_result.body_snippet,
                    extra_text=[content_result.h1_text]
                    if content_result.h1_text
                    else None,
                )
                result.is_parked_domain = park_result.is_parked
                result.parked_indicator = park_result.indicator
        else:
            logger.info("Content extraction skipped (disabled or website not live)")

        # Step 3: Extract business descriptions
        if self.enable_description:
            # Target description (from name only)
            logger.info(f"Extracting target description for {target_company_name}")
            segment_start = time.perf_counter()
            target_desc_result = self.description_op.extract_from_name(
                target_company_name
            )
            timings["description_target"] = (time.perf_counter() - segment_start) * 1000
            if target_desc_result.success:
                result.target_business_description = target_desc_result.description
                result.target_industry = target_desc_result.industry

            # Candidate description (from website if available)
            if content_result and content_result.success:
                logger.info("Extracting candidate description from website content")
                segment_start = time.perf_counter()
                candidate_desc_result = self.description_op.extract_from_website(
                    candidate_company_name,
                    title=content_result.title or "",
                    meta=content_result.description or "",
                    snippet=content_result.body_snippet or "",
                )
            else:
                logger.info("Extracting candidate description from name only")
                segment_start = time.perf_counter()
                candidate_desc_result = self.description_op.extract_from_name(
                    candidate_company_name
                )
            timings["description_candidate"] = (
                time.perf_counter() - segment_start
            ) * 1000

            if candidate_desc_result.success:
                result.candidate_business_description = (
                    candidate_desc_result.description
                )
                result.candidate_industry = candidate_desc_result.industry
        else:
            logger.info("Description extraction disabled")

        # Step 4: Cross-reference validation
        if (
            self.enable_cross_ref
            and result.target_business_description
            and result.candidate_business_description
        ):
            logger.info("Cross-referencing target and candidate descriptions")
            segment_start = time.perf_counter()
            cross_ref_result = self.cross_ref_op.validate(
                target_company_name,
                result.target_business_description,
                candidate_company_name,
                result.candidate_business_description,
            )
            timings["cross_reference"] = (time.perf_counter() - segment_start) * 1000
            result.descriptions_match = cross_ref_result.is_match
            result.description_match_score = cross_ref_result.match_score
            result.mismatch_reason = cross_ref_result.mismatch_reason
        else:
            logger.info("Cross-reference skipped (disabled or missing descriptions)")

        # Calculate confidence boost
        result.confidence_boost = self._calculate_confidence_boost(result)
        result.enrichment_complete = True
        timings["total"] = (time.perf_counter() - total_start) * 1000
        result.timings_ms = {k: round(v, 2) for k, v in timings.items()}

        self._update_metrics(result)

        return result

    def _calculate_confidence_boost(self, result: EnrichmentResult) -> float:
        """
        Calculate confidence boost based on enrichment results.

        Uses semantic gating with industry conflict detection.
        Returns value between -0.15 and 0.20 to add to base score.
        """
        # Semantic boost (using gating logic)
        semantic_boost = 0.0
        if (
            result.target_business_description
            and result.candidate_business_description
            and result.description_match_score > 0.0
        ):
            gate_result = semantic_gate(
                result.target_business_description,
                result.candidate_business_description,
                result.description_match_score,
            )
            semantic_boost = compute_enrichment_boost(
                result.description_match_score, gate_result
            )

        # Live HTTPS bonus
        live_https_bonus = 0.0
        if result.website_live:
            live_https_bonus += 0.05
        if result.website_https and (result.website_tls_valid is not False):
            live_https_bonus += 0.02

        # Parked penalty
        parked_penalty = -0.10 if result.is_parked_domain else 0.0

        # Other bonuses
        other_bonuses = 0.0
        if result.content_extracted:
            other_bonuses += 0.03
        if result.website_response_time_ms and result.website_response_time_ms < 1000:
            other_bonuses += 0.02
        if result.content_is_english is False:
            other_bonuses -= 0.02
        if not result.website_live and result.website_error:
            other_bonuses -= 0.10

        # Compose total boost with clamping
        return compose_total_boost(
            semantic_boost=semantic_boost,
            live_https_bonus=live_https_bonus,
            parked_penalty=parked_penalty,
            other_bonuses=other_bonuses,
        )

    def to_dict(self, result: EnrichmentResult) -> Dict:
        """Convert result to dict for storage in engine_payload."""
        return asdict(result)

    def get_metrics_snapshot(self) -> Dict[str, Dict[str, float]]:
        """Return a snapshot of aggregated metrics."""
        with self._metrics_lock:
            return {
                "timing_ms": dict(self.metrics["timing_ms"]),
                "results": dict(self.metrics["results"]),
                "errors": dict(self.metrics["errors"]),
            }

    def reset_metrics(self) -> None:
        """Clear aggregated metrics after emitting telemetry."""
        with self._metrics_lock:
            self.metrics = {
                "timing_ms": defaultdict(float),
                "results": Counter(),
                "errors": Counter(),
            }

    def _update_metrics(self, result: EnrichmentResult) -> None:
        """Aggregate metrics for observability."""
        with self._metrics_lock:
            timing_bucket = self.metrics["timing_ms"]
            for key, value in result.timings_ms.items():
                timing_bucket[key] += value

            results_bucket: Counter = self.metrics["results"]
            results_bucket["runs"] += 1
            if result.website_live:
                results_bucket["website_live"] += 1
            if result.website_https:
                results_bucket["website_https"] += 1
            if result.content_extracted:
                results_bucket["content_extracted"] += 1
            if result.descriptions_match:
                results_bucket["descriptions_match"] += 1
            if result.is_parked_domain:
                results_bucket["parked"] += 1
            if result.content_is_english is False:
                results_bucket["non_english"] += 1

            errors_bucket: Counter = self.metrics["errors"]
            if result.website_error:
                errors_bucket[result.website_error] += 1
            if result.content_error:
                errors_bucket[result.content_error] += 1
            if not result.descriptions_match and result.mismatch_reason:
                errors_bucket[result.mismatch_reason] += 1
