"""Helper utilities for the interview agent."""

import json
import logging
import os
from datetime import datetime

import httpx
from s3_client import get_s3_bucket, get_s3_client

logger = logging.getLogger(__name__)

BACKEND_API_URL = os.getenv("BACKEND_API_URL", "http://localhost:8000")
INTERVIEW_SERVICE_TOKEN = os.getenv("INTERVIEW_SERVICE_TOKEN", "")

# Timeout constants (in seconds)
TRANSCRIPT_POST_TIMEOUT = 30
BACKEND_PATCH_TIMEOUT = 10


def serialize_object(obj, depth=0, max_depth=10, _seen=None):
    """Recursively convert objects to JSON-serializable dictionaries."""
    if _seen is None:
        _seen = set()
    if depth > max_depth:
        return str(obj)
    if obj is None or isinstance(obj, (str, int, float, bool)):
        return obj
    if isinstance(obj, datetime):
        return obj.isoformat()

    obj_id = id(obj)
    if obj_id in _seen:
        return f"<circular reference to {type(obj).__name__}>"

    if isinstance(obj, (list, tuple)):
        _seen.add(obj_id)
        result = [serialize_object(item, depth + 1, max_depth, _seen) for item in obj]
        _seen.remove(obj_id)
        return result

    if isinstance(obj, dict):
        _seen.add(obj_id)
        result = {str(key): serialize_object(value, depth + 1, max_depth, _seen) for key, value in obj.items()}
        _seen.remove(obj_id)
        return result

    if hasattr(obj, "__dataclass_fields__"):
        _seen.add(obj_id)
        result = {
            field: serialize_object(getattr(obj, field, None), depth + 1, max_depth, _seen)
            for field in obj.__dataclass_fields__
        }
        _seen.remove(obj_id)
        return result

    if hasattr(obj, "__dict__"):
        _seen.add(obj_id)
        obj_dict = {k: v for k, v in vars(obj).items() if not k.startswith("_") and not callable(v)}
        result = {key: serialize_object(value, depth + 1, max_depth, _seen) for key, value in obj_dict.items()}
        _seen.remove(obj_id)
        return result

    return str(obj)


def post_transcripts_to_backend(interview_id, entries: list[dict]):
    """POST transcript entries to the backend API (synchronous to survive process shutdown)."""
    if not INTERVIEW_SERVICE_TOKEN:
        logger.warning("INTERVIEW_SERVICE_TOKEN not set — skipping transcript POST")
        return

    url = f"{BACKEND_API_URL}/interviews/{interview_id}/transcripts"
    try:
        resp = httpx.post(
            url,
            json={"entries": entries},
            headers={"X-Service-Token": INTERVIEW_SERVICE_TOKEN},
            timeout=TRANSCRIPT_POST_TIMEOUT,
        )
        resp.raise_for_status()
        logger.info(f"Posted {len(entries)} transcript entries to backend")
    except httpx.HTTPStatusError as e:
        logger.error(f"Backend rejected transcript POST: {e.response.status_code}")
    except httpx.RequestError as e:
        logger.error(f"Failed to POST transcripts (network error): {e}")


def notify_backend_completed(interview_id, s3_key: str | None = None):
    """PATCH the backend to mark the interview session as completed."""
    body = {"status": "completed"}
    if s3_key:
        body["session_report_s3_key"] = s3_key

    url = f"{BACKEND_API_URL}/interviews/{interview_id}/session"
    logger.info(f"[CLEANUP] Notifying backend: PATCH {url} with {body}")
    try:
        resp = httpx.patch(url, json=body, timeout=BACKEND_PATCH_TIMEOUT)
        logger.info(f"[CLEANUP] Backend PATCH response: {resp.status_code}")
    except httpx.HTTPStatusError as e:
        logger.warning(f"[CLEANUP] Backend rejected completion PATCH: {e.response.status_code}")
    except httpx.RequestError as e:
        logger.warning(f"[CLEANUP] Failed to notify backend of completion: {e}")


def extract_transcript_entries(livekit_report: dict) -> list[dict]:
    """Extract transcript entries from a serialized LiveKit session report."""
    transcript_entries = []
    events = livekit_report.get("events", [])
    for event in events:
        if event.get("type") != "conversation_item_added":
            continue
        item = event.get("item", {})
        if item.get("type") != "message":
            continue
        role = item.get("role", "")
        content_parts = item.get("content", [])
        content = " ".join(str(c) for c in content_parts if c)
        if not content.strip():
            continue
        speaker = "interviewer" if role == "assistant" else "candidate"
        created_at = item.get("created_at", 0)
        timestamp_ms = int(created_at * 1000) if created_at else 0
        transcript_entries.append(
            {
                "speaker": speaker,
                "timestamp_ms": timestamp_ms,
                "text": content,
            }
        )
    return transcript_entries


def save_session_report(ctx, metadata: dict, egress_id: str = None) -> str | None:
    """Generate session report, POST transcripts, and upload report to S3.

    Returns the S3 key on success, or None on failure.
    """
    interview_id = metadata.get("interview_id", "unknown")
    candidate_name = metadata.get("candidate", {}).get("name", "Unknown")

    try:
        logger.info(f"[CLEANUP] Generating session report for interview_id={interview_id}")
        livekit_report_obj = ctx.make_session_report()
        logger.info("[CLEANUP] Session report object created, serializing...")
        livekit_report = serialize_object(livekit_report_obj)
        logger.info(f"[CLEANUP] Session report serialized, events count: " f"{len(livekit_report.get('events', []))}")
    except AttributeError as e:
        logger.error(f"[CLEANUP] Failed to create session report (missing attribute): {e}")
        return None
    except Exception as e:
        logger.error(f"[CLEANUP] Failed to serialize session report: {e}", exc_info=True)
        return None

    transcript_entries = extract_transcript_entries(livekit_report)
    logger.info(f"[CLEANUP] Extracted {len(transcript_entries)} transcript entries")

    if transcript_entries:
        logger.info(f"[CLEANUP] Posting {len(transcript_entries)} transcripts to backend...")
        post_transcripts_to_backend(interview_id, transcript_entries)
        logger.info("[CLEANUP] Transcripts posted")
    else:
        logger.warning("[CLEANUP] No transcript entries found to post")

    # Upload report to S3
    s3_client = get_s3_client()
    s3_bucket = get_s3_bucket()
    if not s3_client or not s3_bucket:
        logger.warning("S3 not configured — skipping session report upload")
        return None

    s3_key = f"interview_session_reports/interview_{interview_id}.json"
    report_data = {
        "interview_id": interview_id,
        "session_id": ctx.job.id,
        "room_id": ctx.room.name,
        "interview_metadata": metadata,
        "livekit_session_report": livekit_report,
        "summary": {
            "candidate_name": candidate_name,
            "completed_at": datetime.now().isoformat(),
        },
    }

    try:
        report_json = json.dumps(report_data, indent=2, default=str)
        logger.info(f"[CLEANUP] Uploading session report to S3: {s3_key}")
        s3_client.put_object(
            Bucket=s3_bucket,
            Key=s3_key,
            Body=report_json.encode("utf-8"),
            ContentType="application/json",
        )
        logger.info(f"[CLEANUP] Session report uploaded to S3: {s3_key}")
        return s3_key
    except s3_client.exceptions.ClientError as e:
        logger.error(f"[CLEANUP] S3 upload failed: {e}")
        return None
    except Exception as e:
        logger.error(f"[CLEANUP] Failed to upload session report: {e}", exc_info=True)
        return None
