from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List

from core.database import get_db

from modules.auth.dependencies import get_current_user
from modules.auth.models import User

from modules.nlp.service import (
    process_request,
    ask_ai,
    save_chat_message,
    get_chat_history,
    MessageSender,
)

from modules.nlp.schemas import ProcessCommandRequest, ProcessCommandResponse
from modules.calendar.service import (
    create_calendar_event,
    get_calendar_events,
    update_calendar_event,
    delete_calendar_event,
)
from modules.calendar.models import CalendarEvent
from modules.calendar.schemas import CalendarEventCreate, CalendarEventUpdate

from modules.todo import service as todo_service
from modules.todo.models import Todo
from modules.todo.schemas import TodoCreate, TodoUpdate
from pydantic import BaseModel
from datetime import datetime


class ChatMessageResponse(BaseModel):
    id: int
    sender: MessageSender
    text: str
    timestamp: datetime

    class Config:
        from_attributes = True


router = APIRouter(prefix="/nlp", tags=["nlp"])


def format_calendar_events(events: List[CalendarEvent]) -> List[str]:
    if not events:
        return ["You have no events matching that criteria."]
    formatted = ["Here are the events:"]
    for event in events:
        start_str = (
            event.start.strftime("%Y-%m-%d %H:%M") if event.start else "No start time"
        )
        end_str = event.end.strftime("%H:%M") if event.end else ""
        title = event.title or "Untitled Event"
        formatted.append(f"- {title} ({start_str}{' - ' + end_str if end_str else ''})")
    return formatted


def format_todos(todos: List[Todo]) -> List[str]:
    if not todos:
        return ["Your TODO list is empty."]
    formatted = ["Here is your TODO list:"]
    for todo in todos:
        status = "[X]" if todo.complete else "[ ]"
        date_str = f" (Due: {todo.date.strftime('%Y-%m-%d')})" if todo.date else ""
        remind_str = " (Reminder)" if todo.remind else ""
        formatted.append(
            f"- {status} {todo.task}{date_str}{remind_str} (ID: {todo.id})"
        )
    return formatted


@router.post("/process-command", response_model=ProcessCommandResponse)
def process_command(
    request_data: ProcessCommandRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    """
    Process the user command, save messages, execute action, save response, and return user-friendly responses.
    """
    user_input = request_data.user_input

    save_chat_message(
        db, user_id=current_user.id, sender=MessageSender.USER, text=user_input
    )

    command_data = process_request(user_input)
    intent = command_data["intent"]
    params = command_data["params"]
    response_text = command_data["response_text"]

    responses = [response_text]

    save_chat_message(
        db, user_id=current_user.id, sender=MessageSender.AI, text=response_text
    )

    if intent == "error":
        return ProcessCommandResponse(responses=responses)

    if intent == "clarification_needed" or intent == "unknown":
        return ProcessCommandResponse(responses=responses)

    try:
        match intent:
            case "ask_ai":
                ai_answer = ask_ai(**params)
                responses.append(ai_answer)
                save_chat_message(
                    db, user_id=current_user.id, sender=MessageSender.AI, text=ai_answer
                )
                return ProcessCommandResponse(responses=responses)

            case "get_calendar_events":
                events: List[CalendarEvent] = get_calendar_events(
                    db, current_user.id, **params
                )
                formatted_responses = format_calendar_events(events)
                responses.extend(formatted_responses)
                for resp in formatted_responses:
                    save_chat_message(
                        db, user_id=current_user.id, sender=MessageSender.AI, text=resp
                    )
                return ProcessCommandResponse(responses=responses)

            case "add_calendar_event":
                event_data = CalendarEventCreate(**params)
                created_event = create_calendar_event(db, current_user.id, event_data)
                start_str = (
                    created_event.start.strftime("%Y-%m-%d %H:%M")
                    if created_event.start
                    else "No start time"
                )
                title = created_event.title or "Untitled Event"
                add_response = f"Added: {title} starting at {start_str}."
                responses.append(add_response)
                save_chat_message(
                    db,
                    user_id=current_user.id,
                    sender=MessageSender.AI,
                    text=add_response,
                )
                return ProcessCommandResponse(responses=responses)

            case "update_calendar_event":
                event_id = params.pop("event_id", None)
                if event_id is None:
                    error_msg = "Event ID is required for update."
                    save_chat_message(
                        db,
                        user_id=current_user.id,
                        sender=MessageSender.AI,
                        text=error_msg,
                    )
                    raise HTTPException(status_code=400, detail=error_msg)
                event_data = CalendarEventUpdate(**params)
                updated_event = update_calendar_event(
                    db, current_user.id, event_id, event_data=event_data
                )
                title = updated_event.title or "Untitled Event"
                update_response = f"Updated event ID {updated_event.id}: {title}."
                responses.append(update_response)
                save_chat_message(
                    db,
                    user_id=current_user.id,
                    sender=MessageSender.AI,
                    text=update_response,
                )
                return ProcessCommandResponse(responses=responses)

            case "delete_calendar_event":
                event_id = params.get("event_id")
                if event_id is None:
                    error_msg = "Event ID is required for delete."
                    save_chat_message(
                        db,
                        user_id=current_user.id,
                        sender=MessageSender.AI,
                        text=error_msg,
                    )
                    raise HTTPException(status_code=400, detail=error_msg)
                delete_calendar_event(db, current_user.id, event_id)
                delete_response = f"Deleted event ID {event_id}."
                responses.append(delete_response)
                save_chat_message(
                    db,
                    user_id=current_user.id,
                    sender=MessageSender.AI,
                    text=delete_response,
                )
                return ProcessCommandResponse(responses=responses)

            case "get_todos":
                todos: List[Todo] = todo_service.get_todos(
                    db, user=current_user, **params
                )
                formatted_responses = format_todos(todos)
                responses.extend(formatted_responses)
                for resp in formatted_responses:
                    save_chat_message(
                        db, user_id=current_user.id, sender=MessageSender.AI, text=resp
                    )
                return ProcessCommandResponse(responses=responses)

            case "add_todo":
                todo_data = TodoCreate(**params)
                created_todo = todo_service.create_todo(
                    db, todo=todo_data, user=current_user
                )
                add_response = (
                    f"Added TODO: '{created_todo.task}' (ID: {created_todo.id})."
                )
                responses.append(add_response)
                save_chat_message(
                    db,
                    user_id=current_user.id,
                    sender=MessageSender.AI,
                    text=add_response,
                )
                return ProcessCommandResponse(responses=responses)

            case "update_todo":
                todo_id = params.pop("todo_id", None)
                if todo_id is None:
                    error_msg = "TODO ID is required for update."
                    save_chat_message(
                        db,
                        user_id=current_user.id,
                        sender=MessageSender.AI,
                        text=error_msg,
                    )
                    raise HTTPException(status_code=400, detail=error_msg)
                todo_data = TodoUpdate(**params)
                updated_todo = todo_service.update_todo(
                    db, todo_id=todo_id, todo_update=todo_data, user=current_user
                )
                update_response = (
                    f"Updated TODO ID {updated_todo.id}: '{updated_todo.task}'."
                )
                if "complete" in params:
                    status = "complete" if params["complete"] else "incomplete"
                    update_response += f" Marked as {status}."
                responses.append(update_response)
                save_chat_message(
                    db,
                    user_id=current_user.id,
                    sender=MessageSender.AI,
                    text=update_response,
                )
                return ProcessCommandResponse(responses=responses)

            case "delete_todo":
                todo_id = params.get("todo_id")
                if todo_id is None:
                    error_msg = "TODO ID is required for delete."
                    save_chat_message(
                        db,
                        user_id=current_user.id,
                        sender=MessageSender.AI,
                        text=error_msg,
                    )
                    raise HTTPException(status_code=400, detail=error_msg)
                deleted_todo = todo_service.delete_todo(
                    db, todo_id=todo_id, user=current_user
                )
                delete_response = (
                    f"Deleted TODO ID {deleted_todo.id}: '{deleted_todo.task}'."
                )
                responses.append(delete_response)
                save_chat_message(
                    db,
                    user_id=current_user.id,
                    sender=MessageSender.AI,
                    text=delete_response,
                )
                return ProcessCommandResponse(responses=responses)

            case _:
                print(
                    f"Warning: Unhandled intent '{intent}' reached api.py match statement."
                )
                return ProcessCommandResponse(responses=responses)

    except HTTPException as http_exc:
        if http_exc.status_code != 400 or ("event_id" not in http_exc.detail.lower()):
            save_chat_message(
                db,
                user_id=current_user.id,
                sender=MessageSender.AI,
                text=http_exc.detail,
            )
        raise http_exc
    except Exception as e:
        print(f"Error executing intent '{intent}': {e}")
        error_response = (
            "Sorry, I encountered an error while trying to perform that action."
        )
        save_chat_message(
            db, user_id=current_user.id, sender=MessageSender.AI, text=error_response
        )
        return ProcessCommandResponse(responses=[error_response])


@router.get("/history", response_model=List[ChatMessageResponse])
def read_chat_history(
    current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Retrieves the last 50 chat messages for the current user."""
    history = get_chat_history(db, user_id=current_user.id, limit=50)
    return history
