"""Semantic gating with industry conflict detection."""

import logging
from dataclasses import dataclass
from difflib import SequenceMatcher
from math import isfinite
from typing import Optional

from .industry_classifier import infer_industry, industries_conflict

logger = logging.getLogger(__name__)

# Gating thresholds
SIM_THRESHOLD = 0.80  # Tightened from 0.70
SOFT_THRESHOLD = 0.75  # Below this, never boost (unless same company name)
SOFT_THRESHOLD_SAME_COMPANY = 0.65  # More lenient for same company
NAME_SIMILARITY_THRESHOLD = 0.75  # For company name matching
MAX_BOOST = 0.15  # Temporarily reduced cap (was 0.30)


@dataclass
class SemanticGateResult:
    """Result of semantic gating check."""

    similarity: float
    target_industry: Optional[str]
    candidate_industry: Optional[str]
    gated_ok: bool
    reason: str
    name_similarity: Optional[float] = None


def _compute_name_similarity(name1: str, name2: str) -> float:
    """Compute string similarity between two company names."""

    # Normalize: lowercase, remove common suffixes
    def normalize(name: str) -> str:
        name = name.lower().strip()
        # Remove common business suffixes
        for suffix in [
            " inc",
            " llc",
            " ltd",
            " corporation",
            " corp",
            " company",
            " co",
            " gmbh",
        ]:
            if name.endswith(suffix):
                name = name[: -len(suffix)].strip()
        return name

    n1 = normalize(name1)
    n2 = normalize(name2)

    return SequenceMatcher(None, n1, n2).ratio()


def semantic_gate(
    target_summary: str,
    candidate_summary: str,
    similarity: float,
    target_name: Optional[str] = None,
    candidate_name: Optional[str] = None,
) -> SemanticGateResult:
    """
    Apply semantic gating with industry conflict detection.

    Args:
        target_summary: Target business description
        candidate_summary: Candidate business description
        similarity: Cosine similarity score (0.0-1.0)
        target_name: Target company name (optional, for name matching)
        candidate_name: Candidate company name (optional, for name matching)

    Returns:
        SemanticGateResult with gating decision
    """
    # Infer industries from summaries
    target_industry = infer_industry(target_summary)
    candidate_industry = infer_industry(candidate_summary)

    # Check if company names match (if provided)
    name_similarity = None
    same_company = False
    if target_name and candidate_name:
        name_similarity = _compute_name_similarity(target_name, candidate_name)
        same_company = name_similarity >= NAME_SIMILARITY_THRESHOLD
        logger.debug(
            f"Company name similarity: {name_similarity:.3f} (same_company={same_company})"
        )

    name_sim_str = f"{name_similarity:.3f}" if name_similarity is not None else "N/A"
    logger.debug(
        f"Semantic gate: sim={similarity:.3f}, "
        f"target_industry={target_industry}, "
        f"candidate_industry={candidate_industry}, "
        f"name_sim={name_sim_str}"
    )

    # 1) Reject unknowns and low similarity outright
    # BUT: Be more lenient if company names match
    threshold = SOFT_THRESHOLD_SAME_COMPANY if same_company else SOFT_THRESHOLD
    if not (isfinite(similarity) and similarity >= threshold):
        return SemanticGateResult(
            similarity=similarity,
            target_industry=target_industry,
            candidate_industry=candidate_industry,
            gated_ok=False,
            reason="similarity_low",
            name_similarity=name_similarity,
        )

    # 2) Hard conflict block
    if industries_conflict(target_industry, candidate_industry):
        logger.info(
            f"BLOCKED: Industry conflict - {target_industry} vs {candidate_industry} "
            f"(sim={similarity:.3f})"
        )
        return SemanticGateResult(
            similarity=similarity,
            target_industry=target_industry,
            candidate_industry=candidate_industry,
            gated_ok=False,
            reason="industry_conflict",
            name_similarity=name_similarity,
        )

    # 3) Require same industry OR high similarity (>= SIM_THRESHOLD)
    # BUT: If same company name, use the lower threshold here too
    same_industry = (
        target_industry and candidate_industry and target_industry == candidate_industry
    )
    industry_threshold = SOFT_THRESHOLD_SAME_COMPANY if same_company else SIM_THRESHOLD

    if not same_industry and similarity < industry_threshold:
        logger.debug(
            f"Industry mismatch: {target_industry} vs {candidate_industry}, "
            f"sim={similarity:.3f} < {industry_threshold}"
        )
        return SemanticGateResult(
            similarity=similarity,
            target_industry=target_industry,
            candidate_industry=candidate_industry,
            gated_ok=False,
            reason="industry_mismatch",
            name_similarity=name_similarity,
        )

    # Passed all gates
    return SemanticGateResult(
        similarity=similarity,
        target_industry=target_industry,
        candidate_industry=candidate_industry,
        gated_ok=True,
        reason="ok",
        name_similarity=name_similarity,
    )


def compute_enrichment_boost(similarity: float, gate: SemanticGateResult) -> float:
    """
    Compute enrichment confidence boost based on gating result.

    Args:
        similarity: Cosine similarity score
        gate: Semantic gating result

    Returns:
        Confidence boost value
    """
    if not gate.gated_ok:
        return 0.0

    # Linearly scale above SOFT_THRESHOLD, capped at MAX_BOOST
    # 0.00 at 0.75, 0.25 at 1.0 -> scaled to MAX_BOOST
    raw = max(0.0, similarity - SOFT_THRESHOLD)
    scaled = min(MAX_BOOST, raw * (MAX_BOOST / (1.0 - SOFT_THRESHOLD)))

    return round(scaled, 3)


def compose_total_boost(
    semantic_boost: float,
    live_https_bonus: float = 0.0,
    parked_penalty: float = 0.0,
    other_bonuses: float = 0.0,
) -> float:
    """
    Compose total enrichment boost from all signals.

    Args:
        semantic_boost: Boost from semantic matching
        live_https_bonus: Bonus for live HTTPS website
        parked_penalty: Penalty for parked domains
        other_bonuses: Any other bonus signals

    Returns:
        Total composed boost, clamped to safe range
    """
    raw = semantic_boost + live_https_bonus + parked_penalty + other_bonuses

    # Clamp stricter while we validate
    # Max boost reduced from 0.30 to 0.20
    return max(-0.15, min(raw, 0.20))
