"""
Chat CRUD and message streaming — v4.0
Now with GitLab project linking and project-aware generation.
"""

import json
from datetime import datetime
from pydantic import BaseModel
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session

from backend.database import get_db
from backend.models import User, Chat, Message, ChatAttachment
from backend.auth import get_current_user
from backend.services import attachment_service
from backend.services.generation_manager import manager as gen_manager

router = APIRouter()


class CreateChatBody(BaseModel):
    title: str = "New Chat"
    model: str = "eu.anthropic.claude-opus-4-6-v1"
    knowledge_base_id: Optional[str] = None
    gitlab_project_id: Optional[int] = None
    gitlab_branch: Optional[str] = "main"
    max_tokens: int = 4096
    reasoning_budget: int = 0


class UpdateChatBody(BaseModel):
    title: Optional[str] = None
    model: Optional[str] = None
    max_tokens: Optional[int] = None
    reasoning_budget: Optional[int] = None
    knowledge_base_id: Optional[str] = None
    gitlab_project_id: Optional[int] = None
    gitlab_branch: Optional[str] = None


class SendMessageBody(BaseModel):
    content: str
    model: Optional[str] = None
    max_tokens: int = 4096
    reasoning_budget: int = 0
    knowledge_base_id: Optional[str] = None
    attachment_ids: list[str] = []


@router.get("")
def list_chats(user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    chats = db.query(Chat).filter(Chat.user_id == user.id).order_by(Chat.updated_at.desc()).all()
    return [_chat_dict(c) for c in chats]


@router.post("")
def create_chat(body: CreateChatBody, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    chat = Chat(
        user_id=user.id, title=body.title, model=body.model,
        knowledge_base_id=body.knowledge_base_id or None,
        gitlab_project_id=body.gitlab_project_id,
        gitlab_branch=body.gitlab_branch or "main",
        max_tokens=body.max_tokens, reasoning_budget=body.reasoning_budget,
    )
    db.add(chat)
    db.commit()
    db.refresh(chat)
    return _chat_dict(chat)


@router.get("/{chat_id}")
def get_chat(chat_id: str, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    chat = db.query(Chat).filter(Chat.id == chat_id, Chat.user_id == user.id).first()
    if not chat:
        raise HTTPException(404, "Chat not found")
    return _chat_dict(chat)


@router.put("/{chat_id}")
def update_chat(chat_id: str, body: UpdateChatBody, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    chat = db.query(Chat).filter(Chat.id == chat_id, Chat.user_id == user.id).first()
    if not chat:
        raise HTTPException(404)
    if body.title is not None:
        chat.title = body.title
    if body.model is not None:
        chat.model = body.model
    if body.max_tokens is not None:
        chat.max_tokens = body.max_tokens
    if body.reasoning_budget is not None:
        chat.reasoning_budget = body.reasoning_budget
    if body.knowledge_base_id is not None:
        chat.knowledge_base_id = body.knowledge_base_id or None
    if body.gitlab_project_id is not None:
        chat.gitlab_project_id = body.gitlab_project_id if body.gitlab_project_id != 0 else None
    if body.gitlab_branch is not None:
        chat.gitlab_branch = body.gitlab_branch or "main"
    db.commit()
    return _chat_dict(chat)


@router.delete("/{chat_id}")
def delete_chat(chat_id: str, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    chat = db.query(Chat).filter(Chat.id == chat_id, Chat.user_id == user.id).first()
    if not chat:
        raise HTTPException(404)
    attachment_service.delete_chat_attachments(chat_id)
    db.delete(chat)
    db.commit()
    return {"ok": True}


@router.get("/{chat_id}/messages")
def get_messages(chat_id: str, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    chat = db.query(Chat).filter(Chat.id == chat_id, Chat.user_id == user.id).first()
    if not chat:
        raise HTTPException(404)
    msgs = []
    for m in chat.messages:
        d = _msg_dict(m)
        atts = db.query(ChatAttachment).filter(ChatAttachment.message_id == m.id).all()
        d["attachments"] = [_att_brief(a) for a in atts]
        msgs.append(d)
    return msgs


@router.get("/{chat_id}/generating")
def check_generating(chat_id: str, user: User = Depends(get_current_user)):
    return {"active": gen_manager.is_active(chat_id)}


@router.get("/{chat_id}/stream")
async def reconnect_stream(chat_id: str, user: User = Depends(get_current_user)):
    if not gen_manager.is_active(chat_id):
        async def empty():
            yield _sse({"type": "done", "message_id": ""})
        return StreamingResponse(empty(), media_type="text/event-stream")

    async def generate():
        async for event in gen_manager.stream_events(chat_id):
            yield _sse(event)

    return StreamingResponse(generate(), media_type="text/event-stream")


@router.post("/{chat_id}/messages")
async def send_message(chat_id: str, body: SendMessageBody, user: User = Depends(get_current_user)):
    user_id = user.id

    gen_manager.start(
        chat_id=chat_id,
        user_id=user_id,
        content=body.content,
        model=body.model or "eu.anthropic.claude-opus-4-6-v1",
        max_tokens=body.max_tokens,
        reasoning_budget=body.reasoning_budget,
        knowledge_base_id=body.knowledge_base_id,
        attachment_ids=body.attachment_ids,
    )

    async def generate():
        async for event in gen_manager.stream_events(chat_id):
            yield _sse(event)

    return StreamingResponse(generate(), media_type="text/event-stream")


def _sse(data):
    return f"data: {json.dumps(data)}\n\n"


def _chat_dict(c):
    return {
        "id": c.id, "title": c.title, "model": c.model,
        "knowledge_base_id": c.knowledge_base_id,
        "gitlab_project_id": c.gitlab_project_id,
        "gitlab_branch": c.gitlab_branch or "main",
        "max_tokens": c.max_tokens or 4096,
        "reasoning_budget": c.reasoning_budget or 0,
        "created_at": str(c.created_at), "updated_at": str(c.updated_at),
    }


def _msg_dict(m):
    return {
        "id": m.id, "role": m.role, "content": m.content,
        "thinking_content": m.thinking_content,
        "input_tokens": m.input_tokens, "output_tokens": m.output_tokens,
        "created_at": str(m.created_at),
    }


def _att_brief(a):
    return {
        "id": a.id, "original_filename": a.original_filename,
        "mime_type": a.mime_type, "file_type": a.file_type,
        "file_size": a.file_size,
    }