"""
Re-run sentiment scoring for calls that already have transcripts and analytics.

Uses per-BID sentiment config (keywords, hybrid/llm/keyword mode) without
re-running full quality parameter scoring unless --full-analysis is set.

Usage:
    python reanalyze_sentiment.py --bid 94
    python reanalyze_sentiment.py --bid 94 --limit 50 --delay 0.3
    python reanalyze_sentiment.py --bid 94 --dry-run
    python reanalyze_sentiment.py --bid 94 --full-analysis
"""
import argparse
import json
import logging
import time

from config import Config
from db_handler import DatabaseHandler
from analyze_calls_with_parameters import CallAnalyzer
from sentiment_scoring import resolve_call_sentiment
from sentiment_config_handler import SentimentConfigHandler

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)


class ConfigWrapper:
    def __init__(self, config):
        self._config = config

    def get(self, key, default=None):
        return getattr(self._config, key, default)

    def __getattr__(self, key):
        return getattr(self._config, key)


def fetch_calls(db_handler, bid, limit):
    query = f"""
        SELECT r.callid
        FROM `{bid}_raw_calls` r
        JOIN `{bid}_sarvamresponse` s ON r.callid = s.callid
        JOIN `{bid}_callanalytics` a ON r.callid = a.callid
        WHERE r.call_status = 'ANSWER'
          AND s.transcript IS NOT NULL
          AND s.transcript != ''
        ORDER BY r.call_starttime DESC
        LIMIT %s
    """
    with db_handler.get_connection() as conn:
        cursor = conn.cursor()
        cursor.execute(query, (limit,))
        return cursor.fetchall() or []


def normalize_segments(speaker_segments):
    normalized = []
    for seg in speaker_segments or []:
        seg = dict(seg)
        if not seg.get("speaker_id"):
            speaker_display = str(seg.get("speaker") or "").strip().lower()
            speaker_num = None
            for prefix in ("speaker_", "speaker "):
                if speaker_display.startswith(prefix):
                    try:
                        speaker_num = int(speaker_display[len(prefix):])
                    except ValueError:
                        pass
                    break
            seg["speaker_id"] = f"speaker_{speaker_num}" if speaker_num is not None else "unknown"
        normalized.append(seg)

    nums = []
    for seg in normalized:
        try:
            nums.append(int(str(seg["speaker_id"]).replace("speaker_", "")))
        except (ValueError, AttributeError):
            pass
    min_num = min(nums) if nums else 0

    for seg in normalized:
        try:
            n = int(str(seg["speaker_id"]).replace("speaker_", ""))
        except (ValueError, AttributeError):
            n = -1
        seg["role"] = "agent" if n == min_num else "customer"
    return normalized


def update_sentiment_only(db_handler, bid, callid, sentiment):
    query = f"UPDATE `{bid}_callanalytics` SET sentiment = %s WHERE callid = %s"
    with db_handler.get_connection() as conn:
        cursor = conn.cursor()
        cursor.execute(query, (sentiment, callid))


def main():
    parser = argparse.ArgumentParser(description="Re-analyze customer sentiment for existing calls")
    parser.add_argument("--bid", required=True, help="Business ID")
    parser.add_argument("--limit", type=int, default=100, help="Max calls to process")
    parser.add_argument("--delay", type=float, default=0.0, help="Delay between calls (seconds)")
    parser.add_argument("--dry-run", action="store_true", help="List calls without updating")
    parser.add_argument(
        "--full-analysis",
        action="store_true",
        help="Re-run full analyze_call (quality + sentiment) instead of sentiment-only update",
    )
    args = parser.parse_args()

    bid = str(args.bid)
    cfg = ConfigWrapper(Config())
    db_handler = DatabaseHandler(cfg)
    sentiment_handler = SentimentConfigHandler(cfg)
    sentiment_handler.ensure_table()
    sentiment_handler.seed_defaults_if_empty(bid)

    callids = fetch_calls(db_handler, bid, args.limit)
    logger.info("Found %s call(s) to process for BID %s", len(callids), bid)

    if args.dry_run:
        for row in callids:
            callid = row["callid"] if isinstance(row, dict) else row[0]
            logger.info("[dry-run] would process callid=%s", callid)
        return

    analyzer = CallAnalyzer(cfg) if args.full_analysis else None
    updated = 0
    errors = 0

    for row in callids:
        callid = row["callid"] if isinstance(row, dict) else row[0]
        try:
            call_data = db_handler.get_raw_call_details(bid, callid)
            if not call_data:
                logger.warning("Call %s not found, skipping.", callid)
                continue

            transcript = call_data.get("transcripts")
            if not transcript:
                logger.warning("Call %s has no transcript, skipping.", callid)
                continue

            raw_segments = call_data.get("speaker_segments") or []
            if isinstance(raw_segments, str):
                try:
                    raw_segments = json.loads(raw_segments)
                except (json.JSONDecodeError, TypeError):
                    raw_segments = []
            speaker_segments = normalize_segments(raw_segments)
            actual_duration = call_data.get("duration") or call_data.get("duration_seconds")

            if args.full_analysis:
                analyzer.analyze_call(
                    bid, callid, transcript, speaker_segments, actual_duration=actual_duration
                )
                logger.info("Full re-analysis complete for callid=%s", callid)
            else:
                label, details = resolve_call_sentiment(
                    bid,
                    transcript,
                    speaker_segments,
                    llm_sentiment=None,
                    config=cfg,
                    handler=sentiment_handler,
                )
                update_sentiment_only(db_handler, bid, callid, label)
                logger.info("Updated sentiment=%s for callid=%s (%s)", label, callid, details.get("method"))

            updated += 1
            if args.delay > 0:
                time.sleep(args.delay)
        except Exception as exc:
            errors += 1
            logger.error("Failed callid=%s: %s", callid, exc)

    logger.info("Done. updated=%s errors=%s", updated, errors)


if __name__ == "__main__":
    main()
