"""
Opt-in per-call-group configuration helpers.

Safety contract:
- group_config_enabled defaults to 0 on every BID.
- When disabled, all resolvers use the existing BID-level tables/config only.
- When enabled:
  1) map call groupname → named parameter_scope (if configured in UI)
  2) else try exact groupname as scope
  3) else fall back to BID defaults
"""

from __future__ import annotations

import json
import logging
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)

DEFAULT_SCOPE = ""  # BID-level / legacy default set


def normalize_scope_groupname(value: Optional[str]) -> str:
    """Empty string = BID default set. Non-empty = Mcube call groupname or named pack."""
    return str(value or "").strip()


def is_group_config_enabled(pipeline_cfg: Optional[Dict[str, Any]]) -> bool:
    try:
        return int((pipeline_cfg or {}).get("group_config_enabled") or 0) == 1
    except (TypeError, ValueError):
        return False


def is_group_config_enabled_for_bid(db_handler: Any, bid: str) -> bool:
    try:
        db_handler.ensure_business_pipeline_config_table()
        cfg = db_handler.get_pipeline_config(bid) or {}
        return is_group_config_enabled(cfg)
    except Exception as exc:
        logger.debug("group_config_enabled check failed for %s: %s", bid, exc)
        return False


def get_call_groupname(db_handler: Any, bid: str, callid: str) -> str:
    """Read groupname from {bid}_raw_calls; empty if missing."""
    bid = str(bid).strip()
    callid = str(callid).strip()
    if not bid or not callid:
        return ""
    table = f"{bid}_raw_calls"
    try:
        with db_handler.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute(
                f"SELECT groupname FROM `{table}` WHERE callid = %s LIMIT 1",
                (callid,),
            )
            row = cursor.fetchone() or {}
            return normalize_scope_groupname(row.get("groupname"))
    except Exception as exc:
        logger.debug("Could not load groupname for %s/%s: %s", bid, callid, exc)
        return ""


def resolve_parameter_scope_key(
    db_handler: Optional[Any],
    bid: str,
    groupname: Optional[str],
) -> str:
    """
    Resolve which parameter pack key to load for a call group.
    Uses UI scope map when present; otherwise the call groupname itself.
    """
    scope = normalize_scope_groupname(groupname)
    if not scope or db_handler is None:
        return scope
    try:
        mapped = db_handler.get_parameter_scope_for_group(bid, scope)
        if mapped:
            return normalize_scope_groupname(mapped)
    except Exception as exc:
        logger.debug("parameter scope map lookup failed for %s/%s: %s", bid, scope, exc)
    return scope


def resolve_quality_parameters(
    quality_handler: Any,
    bid: str,
    *,
    groupname: Optional[str] = None,
    group_config_enabled: bool = False,
    db_handler: Optional[Any] = None,
) -> List[Dict[str, Any]]:
    """
    Return quality parameters for analysis.

    Flag off → always BID default table (current behavior).
    Flag on → mapped/named scope rows if present, else exact group, else BID default.
    """
    bid = str(bid).strip()
    if not group_config_enabled:
        return quality_handler.get_parameters(bid) or []

    scope = resolve_parameter_scope_key(db_handler, bid, groupname)
    if scope:
        scoped = quality_handler.get_parameters_for_scope(bid, scope) or []
        if scoped:
            return scoped
        # If mapped to a named pack with no rows yet, try exact call groupname
        # only when map did not remap away from the original name.
        original = normalize_scope_groupname(groupname)
        if original and original != scope:
            return quality_handler.get_parameters(bid) or []
        if original:
            exact = quality_handler.get_parameters_for_scope(bid, original) or []
            if exact:
                return exact
    return quality_handler.get_parameters(bid) or []


def resolve_propensity_parameters(
    propensity_handler: Any,
    bid: str,
    *,
    groupname: Optional[str] = None,
    group_config_enabled: bool = False,
    active_only: bool = True,
    db_handler: Optional[Any] = None,
) -> List[Dict[str, Any]]:
    bid = str(bid).strip()
    if not group_config_enabled:
        return propensity_handler.get_parameters(bid, active_only=active_only) or []

    scope = resolve_parameter_scope_key(db_handler, bid, groupname)
    if scope:
        scoped = propensity_handler.get_parameters_for_scope(bid, scope, active_only=active_only) or []
        if scoped:
            return scoped
        original = normalize_scope_groupname(groupname)
        if original and original != scope:
            return propensity_handler.get_parameters(bid, active_only=active_only) or []
        if original:
            exact = propensity_handler.get_parameters_for_scope(
                bid, original, active_only=active_only
            ) or []
            if exact:
                return exact
    return propensity_handler.get_parameters(bid, active_only=active_only) or []


def merge_pipeline_config_for_group(
    db_handler: Any,
    bid: str,
    pipeline_cfg: Optional[Dict[str, Any]],
    groupname: Optional[str],
) -> Dict[str, Any]:
    """
    Return pipeline config, optionally overlaying group summary overrides.
    When flag is off or no group row exists, returns a shallow copy of pipeline_cfg.
    """
    base = dict(pipeline_cfg or {})
    if not is_group_config_enabled(base):
        return base

    scope = normalize_scope_groupname(groupname)
    if not scope:
        return base

    # Prefer mapped pack name for summary overrides when present.
    try:
        mapped = db_handler.get_parameter_scope_for_group(bid, scope)
        lookup = normalize_scope_groupname(mapped) or scope
    except Exception:
        lookup = scope

    try:
        override = db_handler.get_business_group_config(bid, lookup) or {}
        if not override and lookup != scope:
            override = db_handler.get_business_group_config(bid, scope) or {}
    except Exception as exc:
        logger.debug("group config load failed for %s/%s: %s", bid, scope, exc)
        return base

    if not override:
        return base

    mode = override.get("summary_mode")
    if mode is not None and str(mode).strip():
        base["summary_mode"] = str(mode).strip().lower()
    if override.get("summary_instructions") is not None:
        base["summary_instructions"] = override.get("summary_instructions")
    return base


def merge_crm_config_for_group(
    db_handler: Any,
    bid: str,
    integration_config: Optional[Dict[str, Any]],
    groupname: Optional[str],
    *,
    group_config_enabled: bool = False,
) -> Dict[str, Any]:
    """Shallow-merge CRM toggle overrides for a call group when enabled."""
    base = dict(integration_config or {})
    if not group_config_enabled:
        return base

    scope = normalize_scope_groupname(groupname)
    if not scope:
        return base

    try:
        mapped = db_handler.get_parameter_scope_for_group(bid, scope)
        lookup = normalize_scope_groupname(mapped) or scope
    except Exception:
        lookup = scope

    try:
        override = db_handler.get_business_group_config(bid, lookup) or {}
        if not override and lookup != scope:
            override = db_handler.get_business_group_config(bid, scope) or {}
    except Exception:
        return base

    crm = override.get("crm_config")
    if isinstance(crm, str) and crm.strip():
        try:
            crm = json.loads(crm)
        except Exception:
            crm = None
    if isinstance(crm, dict) and crm:
        merged = dict(base)
        merged.update(crm)
        return merged
    return base
