from flask import Blueprint, jsonify, request

from backend.config import get_config
from backend.utils.logger_utils import log_http_request
from backend.utils.logging_utils import get_logger
from backend.utils.utils import (
    get_store,
)

logger = get_logger(__name__)

convs_blueprint = Blueprint("convs_blueprint", __name__, url_prefix="/conversations")

# --- Global Conversations Endpoints (all user-scoped) ---


@convs_blueprint.route("", methods=["GET"])
@log_http_request
def get_conversations():
    store = get_store()
    conversations = store.get_conversations()
    conv_list = []
    for cid, conv in conversations.items():
        if conv.get("messages"):
            conv_list.append(
                {
                    "id": cid,
                    "title": conv.get("title", "Untitled"),
                    "messages": conv.get("messages", []),
                    "lastUpdated": conv.get("lastUpdated", ""),
                    "agentCount": len(conv.get("agentIds", [])),
                    "agentIds": conv.get("agentIds", []),
                }
            )
    conv_list.sort(key=lambda c: c["lastUpdated"], reverse=True)
    return jsonify({"conversations": conv_list})


@convs_blueprint.route("/metadata", methods=["GET"])
@log_http_request
def get_conversation_metadata():
    """
    Return only the metadata for each conversation:
      - id
      - title
      - lastUpdated
      - agentCount
      - agentIds
    (no messages)
    """
    store = get_store()
    metas = store.get_conversations_metadata()
    # sort by lastUpdated desc
    metas.sort(key=lambda c: c["lastUpdated"], reverse=True)
    # strip userId if we don’t want to expose it
    for m in metas:
        m.pop("userId", None)
    return jsonify({"conversations": metas})


@convs_blueprint.route("/<conversation_id>", methods=["GET"])
@log_http_request
def get_conversation(conversation_id):
    store = get_store()
    conv = store.get_conversation(conversation_id)
    if not conv:
        return jsonify({"error": "Conversation not found"}), 404
    try:
        docs = store.get_derived_documents(conversation_id)
        uploaded_attachments = []
        has_attachments = len(docs) > 0  # True if conversation has ANY derived documents (regardless of status)
        for doc in docs:
            meta = doc.get("metadata", {})
            if meta.get("status") in ("uploaded", "ready"):
                attachment_info = {
                    "id": doc.get("id"),
                    "document_name": doc.get("document_name"),
                    "document_path": doc.get("document_path"),
                    "uploadStatus": meta.get("status"),
                }
                if "size" in meta:
                    attachment_info["size"] = meta["size"]
                if "type" in meta:
                    attachment_info["type"] = meta["type"]
                uploaded_attachments.append(attachment_info)
    except Exception:
        uploaded_attachments = []
        has_attachments = False

    # Get extraction mode from latest message with attachments
    extraction_mode = None
    messages = conv.get("messages", [])
    latest_messages = messages[-2:] if len(messages) >= 2 else messages
    for msg in reversed(latest_messages):  # Only check the two latest messages
        if msg.get("extractionMode"):
            extraction_mode = msg.get("extractionMode")
            break
    return jsonify({
        "id": conversation_id,
        "title": conv.get("title", "Untitled"),
        "messages": messages,
        "lastUpdated": conv.get("lastUpdated", ""),
        "agentIds": conv.get("agentIds", []),
        "selectedLLM": conv.get("selectedLLM", ""),
        "modeAgents": conv.get("modeAgents", True),
        "agentCount": len(conv.get("agentIds", [])),
        "conversation_attachments": uploaded_attachments,
        "has_attachments": has_attachments,
        "extractionMode": extraction_mode,
    })


@convs_blueprint.route("/message_events/<message_id>", methods=["GET"])
@log_http_request
def get_message_events(message_id):
    store = get_store()
    if request.args.get("draft") == "1":
        aid = request.args.get("agentId")
        if not aid:
            return jsonify({"error": "agentId required when draft=1"}), 400
        convo = store.get_draft_conversation(aid) or {}
        for m in convo.get("messages", []):
            if m["id"] == message_id:
                return jsonify({"messageId": message_id, "events": m.get("eventLog", [])})
        return jsonify({"messageId": message_id, "events": []})

    events = store.get_message_events(message_id)
    return jsonify({"messageId": message_id, "events": events})


@convs_blueprint.route("/<conversation_id>", methods=["DELETE"])
@log_http_request
def delete_conversation(conversation_id):
    store = get_store()
    permanent_delete = get_config().get("permanent_delete_messages", False)
    success = store.delete_conversation(conversation_id, permanent_delete)
    if not success:
        return jsonify({"error": "Conversation not found"}), 404
    return jsonify({"message": "Conversation deleted", "conversationId": conversation_id})


@convs_blueprint.route("", methods=["DELETE"])
@log_http_request
def delete_all_conversations():
    store = get_store()
    permanent_delete = get_config().get("permanent_delete_messages", False)

    for cid in list(store.get_conversations_ids()):
        store.delete_conversation(cid, permanent_delete)
    return jsonify({"message": "All conversations deleted"})


@convs_blueprint.route("", methods=["POST"])
@log_http_request
def create_conversation():
    """
    Create or update a conversation depending on if id is provided.
    Request body (JSON):
        {
            "id": <optional>, // use to update if present, otherwise create
            "title": "",
            "agentIds": ["agent-id1", ...]
        }
    """
    store = get_store()
    payload = request.get_json(force=True, silent=True) or {}

    from uuid import uuid4
    conv_id = payload.get("id") or str(uuid4())
    title = payload.get("title") or 'Untitled'
    agent_ids = payload.get("agentIds") or []
    conversation = {
        "id": conv_id,
        "title": title,
        "messages": [],
        "agentIds": agent_ids,
        "lastUpdated": None,
    }
    # If id exists (update), else create.
    store.create_conversation(conv_id, conversation)
    return jsonify({
        "id": conv_id,
        "title": title,
        "agentIds": agent_ids
    }), 201


@convs_blueprint.route("/<conversation_id>", methods=["PUT"])
@log_http_request
def update_conversation_title(conversation_id):
    payload = request.get_json()
    new_title = payload.get("title", "").strip()
    if not new_title:
        return jsonify({"error": "Title cannot be empty"}), 400

    store = get_store()
    conversations = store.get_conversations()
    if conversation_id not in conversations:
        return jsonify({"error": "Conversation not found"}), 404

    conv = conversations[conversation_id]
    conv["title"] = new_title
    store.update_conversation(conversation_id, conv)
    return jsonify({"message": "Conversation title updated", "title": new_title})


@convs_blueprint.route("/draft/<agent_id>", methods=["DELETE"])
@log_http_request
def reset_draft_chat(agent_id):
    store = get_store()
    store.delete_draft_conversation(agent_id)
    return jsonify({"ok": True})


@convs_blueprint.route("/draft/<agent_id>", methods=["GET"])
@log_http_request
def get_draft_conversation(agent_id):
    """
    Return the saved draft conversation (if any) for *this* user /
    *this* agent.  Shape is identical to what the frontend already
    receives on chat_end, e.g.
      { id, title, messages, lastUpdated, agentIds }
    If nothing exists we return `{}` so the UI knows to start fresh.
    """
    store = get_store()
    convo = store.get_draft_conversation(agent_id)
    if not convo:
        return jsonify({"error": "Draft conversation not found"}), 404
    return jsonify(convo)


@convs_blueprint.route("/messages/<message_id>/feedback", methods=["GET"])
@log_http_request
def get_message_feedback(message_id):
    """
    Return the feedback JSON for one message in the caller's own conversation.
    """
    store = get_store()
    msg = store.get_message(message_id)
    if not msg:  # not found or not authorized
        return jsonify({"error": "Message not found"}), 404
    return jsonify({"messageId": message_id, "feedback": msg.get("feedback") or {}}), 200


@convs_blueprint.route("/messages/<message_id>/feedback", methods=["PUT"])
@log_http_request
def upsert_message_feedback(message_id):
    """Create/update feedback on one message (thumbs up/down).

    Body: { "rating": 1|0, "text": "<optional string>" }.
    """
    store = get_store()
    msg = store.get_message(message_id)
    if not msg:  # not found or not authorized
        return jsonify({"error": "Message not found"}), 404

    payload = request.get_json(force=True) or {}
    rating = payload.get("rating")

    if isinstance(rating, bool):
        rating = 1 if rating else 0
    elif isinstance(rating, str):
        rating = 1 if rating == "1" else 0

    if rating not in {0, 1}:
        return jsonify({"error": "Invalid 'rating'. Use 1 (up) or 0 (down)."}), 400

    text = payload.get("text")
    text = text.strip() if isinstance(text, str) else None
    store.update_message_feedback(message_id, rating=rating, text=text)
    # Round-trip the stored row so GET/PUT shapes stay identical
    refreshed = store.get_message(message_id)
    return jsonify({"messageId": message_id, "feedback": refreshed.get("feedback") or {}}), 200


@convs_blueprint.route("/messages/<message_id>/feedback", methods=["DELETE"])
@log_http_request
def clear_message_feedback(message_id):
    """
    Clear feedback JSON for the message (set to {}).
    """
    store = get_store()
    msg = store.get_message(message_id)
    if not msg:
        return jsonify({"error": "Message not found"}), 404
    store.clear_message_feedback(message_id)
    return jsonify({"messageId": message_id, "cleared": True}), 200


@convs_blueprint.route("/<conversation_id>/messages/<message_id>", methods=["PUT"])
@log_http_request
def update_message(conversation_id, message_id):
    payload = request.get_json()
    new_content = payload.get("content", "").strip()

    if not new_content:
        return jsonify({"error": "Content cannot be empty"}), 400

    store = get_store()

    try:
        # Get current conversation
        conversation = store.get_conversation(conversation_id)
        if not conversation:
            return jsonify({"error": "Conversation not found"}), 404

        # Find the message to delete
        messages = conversation.get("messages", [])
        message_index = None

        for i, msg in enumerate(messages):
            if msg.get("id") == message_id:
                if msg.get("role") != "user":
                    return jsonify({"error": "Can only edit user messages"}), 400
                message_index = i
                break

        if message_index is None:
            return jsonify({"error": "Message not found"}), 404

        # Get all message IDs that need to be deleted (from edited message onwards)
        messages_to_delete = [msg["id"] for msg in messages[message_index:] if msg.get("id")]

        # Delete the specific messages using store methods
        for msg_id in messages_to_delete:
            store.delete_message(msg_id)

        return jsonify(
            {
                "message": "Message deleted and subsequent messages removed",
                "messageId": message_id,
                "content": new_content,
                "messagesRemoved": len(messages_to_delete),
            }
        )

    except Exception as e:
        logger.exception("Failed to update message")
        return jsonify({"error": "Failed to update message"}), 500
