import asyncio
import logging
import smtplib
from datetime import datetime, timezone
from email.message import EmailMessage
from typing import Any

from app.config import settings
from app.repositories.conversation_repository import get_conversation_by_session_id
from app.repositories.message_repository import get_messages_by_conversation_id
from app.repositories.session_repository import get_session_by_id

logger = logging.getLogger(__name__)


def _format_time_str(dt_val: Any, time_only: bool = False) -> str:
    if not dt_val:
        return "N/A"
    if isinstance(dt_val, str):
        try:
            dt_val = datetime.fromisoformat(dt_val.replace("Z", "+00:00"))
        except Exception:
            return dt_val
    if isinstance(dt_val, datetime):
        if dt_val.tzinfo is None:
            dt_val = dt_val.replace(tzinfo=timezone.utc)
        if time_only:
            return dt_val.strftime("%H:%M:%S UTC")
        return dt_val.strftime("%Y-%m-%d %H:%M:%S UTC")
    return str(dt_val)


def build_transcript_email_content(
    session: dict,
    conversation: dict | None,
    messages: list[dict],
    agent_info: dict | None = None,
    ended_by: str = "visitor",
    ended_at: datetime | None = None
) -> tuple[str, str]:
    """
    Builds the formatted plain text and HTML email bodies for a chat session transcript.
    """
    session_id = session.get("sessionId", "Unknown")
    site_id = session.get("siteId", "Unknown")
    ip_address = session.get("ipAddress") or "Unknown"
    country = session.get("country") or "Unknown"
    
    agent_name = "None"
    agent_email = "None"
    if agent_info:
        agent_name = agent_info.get("name") or agent_info.get("agentId") or "Assigned Agent"
        agent_email = agent_info.get("email") or "N/A"
    elif session.get("assignedAgentId"):
        agent_name = session.get("assignedAgentId")
        agent_email = "N/A"

    started_at_str = _format_time_str(session.get("createdAt") or session.get("connectedAt"))
    ended_at_str = _format_time_str(ended_at or datetime.now(timezone.utc))

    # Build Plain Text
    lines = [
        "Chat Session Details",
        "--------------------",
        f"- Session ID: {session_id}",
        f"- Site ID: {site_id}",
        f"- Visitor IP: {ip_address}",
        f"- Country: {country}",
        f"- Assigned Agent: {agent_name}",
        f"- Agent Email: {agent_email}",
        f"- Chat Started At: {started_at_str}",
        f"- Chat Ended At: {ended_at_str}",
        f"- Ended By: {ended_by}",
        "",
        "Conversation Transcript",
        "-----------------------"
    ]

    if not messages:
        lines.append("(No messages in this conversation)")
    else:
        for m in messages:
            msg_time = _format_time_str(m.get("createdAt"), time_only=True)
            role = m.get("role", "unknown").lower()
            
            if role in ("user", "visitor"):
                sender_label = "Visitor"
            elif role in ("agent", "assistant"):
                sender_label = f"Agent ({agent_name})" if agent_name != "None" else "Agent"
            elif role == "system":
                sender_label = "System"
            else:
                sender_label = role.capitalize()
                
            content = m.get("content", "")
            lines.append(f"[{msg_time}] {sender_label}:\n{content}\n")

    text_body = "\n".join(lines)

    # Build HTML Version
    html_lines = [
        "<html><body style='font-family: Arial, sans-serif; line-height: 1.5; color: #333;'>",
        "<h2>Chat Session Details</h2>",
        "<table style='border-collapse: collapse; margin-bottom: 20px;'>",
        f"<tr><td style='padding: 4px 12px 4px 0; font-weight: bold;'>Session ID:</td><td>{session_id}</td></tr>",
        f"<tr><td style='padding: 4px 12px 4px 0; font-weight: bold;'>Site ID:</td><td>{site_id}</td></tr>",
        f"<tr><td style='padding: 4px 12px 4px 0; font-weight: bold;'>Visitor IP:</td><td>{ip_address}</td></tr>",
        f"<tr><td style='padding: 4px 12px 4px 0; font-weight: bold;'>Country:</td><td>{country}</td></tr>",
        f"<tr><td style='padding: 4px 12px 4px 0; font-weight: bold;'>Assigned Agent:</td><td>{agent_name}</td></tr>",
        f"<tr><td style='padding: 4px 12px 4px 0; font-weight: bold;'>Agent Email:</td><td>{agent_email}</td></tr>",
        f"<tr><td style='padding: 4px 12px 4px 0; font-weight: bold;'>Chat Started At:</td><td>{started_at_str}</td></tr>",
        f"<tr><td style='padding: 4px 12px 4px 0; font-weight: bold;'>Chat Ended At:</td><td>{ended_at_str}</td></tr>",
        f"<tr><td style='padding: 4px 12px 4px 0; font-weight: bold;'>Ended By:</td><td>{ended_by}</td></tr>",
        "</table>",
        "<h2>Conversation Transcript</h2>",
        "<div style='background-color: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px;'>"
    ]

    if not messages:
        html_lines.append("<p style='color: #64748b;'><em>(No messages in this conversation)</em></p>")
    else:
        for m in messages:
            msg_time = _format_time_str(m.get("createdAt"), time_only=True)
            role = m.get("role", "unknown").lower()
            if role in ("user", "visitor"):
                sender_label = "Visitor"
                bg_color = "#e0f2fe"
                border_color = "#38bdf8"
            elif role in ("agent", "assistant"):
                sender_label = f"Agent ({agent_name})" if agent_name != "None" else "Agent"
                bg_color = "#f1f5f9"
                border_color = "#94a3b8"
            else:
                sender_label = "System"
                bg_color = "#fef3c7"
                border_color = "#f59e0b"

            content = m.get("content", "").replace("\n", "<br>")
            html_lines.append(
                f"<div style='margin-bottom: 12px; padding: 8px 12px; background: {bg_color}; border-left: 3px solid {border_color}; border-radius: 4px;'>"
                f"<div style='font-size: 11px; color: #64748b; margin-bottom: 4px;'>[{msg_time}] <strong>{sender_label}</strong></div>"
                f"<div style='font-size: 13px;'>{content}</div>"
                f"</div>"
            )

    html_lines.append("</div></body></html>")
    html_body = "".join(html_lines)

    return text_body, html_body


def _send_smtp_sync(
    recipient: str,
    subject: str,
    text_content: str,
    html_content: str | None = None
) -> bool:
    """Synchronous SMTP sending worker executed in a thread pool."""
    if not settings.smtp_host:
        logger.info(
            f"[EMAIL] No SMTP host configured. Logged transcript email for '{subject}' to '{recipient}'."
        )
        return True

    msg = EmailMessage()
    msg["Subject"] = subject
    msg["From"] = settings.smtp_from_email
    msg["To"] = recipient
    msg.set_content(text_content)

    if html_content:
        msg.add_alternative(html_content, subtype="html")

    try:
        if settings.smtp_use_ssl:
            with smtplib.SMTP_SSL(settings.smtp_host, settings.smtp_port, timeout=10) as server:
                if settings.smtp_user and settings.smtp_password:
                    server.login(settings.smtp_user, settings.smtp_password)
                server.send_message(msg)
        else:
            with smtplib.SMTP(settings.smtp_host, settings.smtp_port, timeout=10) as server:
                if settings.smtp_use_tls:
                    server.starttls()
                if settings.smtp_user and settings.smtp_password:
                    server.login(settings.smtp_user, settings.smtp_password)
                server.send_message(msg)
        logger.info(f"[EMAIL] Successfully sent transcript email to {recipient} ({subject})")
        return True
    except Exception as e:
        logger.error(f"[EMAIL] Failed to send email to {recipient} via SMTP: {e}", exc_info=True)
        return False


async def send_conversation_transcript_email(
    db: Any,
    session_id: str,
    ended_by: str = "visitor"
) -> bool:
    """
    Fetches the complete session conversation history and metadata from MongoDB,
    generates the formatted transcript, and emails it to the configured admin email.
    
    Prevents duplicate emails if already sent for this conversation.
    Gracefully catches failures without interrupting the chat termination flow.
    """
    if db is None:
        return False

    try:
        # 1. Fetch Session from MongoDB
        session = await get_session_by_id(db, session_id)
        if not session:
            logger.warning(f"[EMAIL] Session not found for transcript email: {session_id}")
            return False

        # 2. Fetch Conversation Associated with Session
        conv = await get_conversation_by_session_id(db, session_id)
        conv_id = conv["conversationId"] if conv else None

        # 3. Deduplication Check
        if conv and conv.get("transcriptEmailed"):
            logger.info(f"[EMAIL] Conversation {conv_id} transcript already emailed. Skipping duplicate.")
            return True
        if session.get("transcriptEmailed"):
            logger.info(f"[EMAIL] Session {session_id} transcript already emailed. Skipping duplicate.")
            return True

        # 4. Fetch All Messages Chronologically
        messages = []
        if conv_id:
            messages = await get_messages_by_conversation_id(db, conv_id)

        # 5. Fetch Assigned Agent Info if applicable
        agent_info = None
        assigned_agent_id = session.get("assignedAgentId")
        if assigned_agent_id:
            agent_info = await db["agents"].find_one({"agentId": assigned_agent_id})

        # 6. Build Email Subject & Content
        subject = f"Chat Conversation — {session_id}"
        recipient = settings.admin_notification_email or "alok.chaudhary2001@gmail.com"
        ended_at = datetime.now(timezone.utc)
        
        text_body, html_body = build_transcript_email_content(
            session=session,
            conversation=conv,
            messages=messages,
            agent_info=agent_info,
            ended_by=ended_by,
            ended_at=ended_at
        )

        # 7. Dispatch Email in Thread Pool
        sent_success = await asyncio.to_thread(
            _send_smtp_sync,
            recipient=recipient,
            subject=subject,
            text_content=text_body,
            html_content=html_body
        )

        # 8. Mark transcriptEmailed flag in DB to prevent duplicate sends
        if conv_id:
            await db["conversations"].update_one(
                {"conversationId": conv_id},
                {"$set": {"transcriptEmailed": True, "transcriptEmailedAt": ended_at}}
            )
        await db["sessions"].update_one(
            {"sessionId": session_id},
            {"$set": {"transcriptEmailed": True, "transcriptEmailedAt": ended_at}}
        )

        return sent_success
    except Exception as e:
        logger.error(f"[EMAIL] Unexpected error generating/sending transcript email for {session_id}: {e}", exc_info=True)
        return False
