import datetime
from typing import Optional

from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from sqlmodel import Field
from sqlmodel import select
from sqlmodel import SQLModel

from ghostforge.db import get_session
from ghostforge.db import User
from ghostforge.ghosts import can_view_ghost
from ghostforge.ghosts import Ghost
from ghostforge.users import get_current_user


class GhostNote(SQLModel, table=True):
    id: Optional[int] = Field(primary_key=True)
    ghost_id: int = Field(foreign_key=Ghost.id)
    user_id: Optional[int] = Field(foreign_key=User.id)
    event_time: datetime = Field(default_factory=datetime.utcnow)
    content: str
    version: int = Field(default=1)
    is_deleted: bool = False


class GhostNoteEdit(SQLModel, table=True):
    id: Optional[int] = Field(primary_key=True)
    note_id: int = Field(foreign_key=GhostNote.id)
    user_id: Optional[int] = Field(foreign_key=User.id)
    edit_time: datetime = Field(default_factory=datetime.utcnow)
    content: str
    version: int = Field()


class GhostNoteCreate(GhostNote, table=False):
    pass


class GhostNoteUpdate(GhostNote, table=False):
    pass


class GhostNoteRead(GhostNote, table=False):
    pass


gf = APIRouter()


# Route to read all latest notes for the specified ghost
@gf.get("/ghosts/{ghost_id}/notes/", response_model=list[GhostNoteRead])
async def read_ghost_notes(
    ghost_id: int, session: AsyncSession = Depends(get_session), can_view: bool = Depends(can_view_ghost)
):
    if not can_view:
        raise HTTPException(status_code=403, detail="You're not authorized to see this ghost")

    query = (
        select(GhostNote)
        .join(GhostNoteEdit, isouter=True)
        .filter(GhostNote.ghost_id == ghost_id, GhostNote.is_deleted is False)
        .order_by(GhostNote.event_time.desc(), GhostNoteEdit.version.desc())
        .group_by(GhostNote.id, GhostNoteEdit.note_id)
        .options(selectinload(GhostNote.user))
    )

    notes = (await session.execute(query)).all()
    return notes


# Route to create a new note for the specified ghost for the current user
@gf.post("/ghosts/{ghost_id}/notes/new", response_model=GhostNoteRead)
async def create_ghost_note(
    ghost_id: int,
    note: GhostNoteCreate,
    current_user: User = Depends(get_current_user()),
    session: AsyncSession = Depends(get_session),
    can_view: bool = Depends(can_view_ghost),
):
    if not can_view:
        raise HTTPException(status_code=403, detail="You're not authorized to see this ghost")
    db_note = GhostNote.from_orm(note)
    db_note.ghost_id = ghost_id
    db_note.user_id = current_user.id
    session.add(db_note)
    await session.commit()
    await session.refresh(db_note)
    return db_note


# Route to read the latest version of a note
@gf.get("/ghosts/{ghost_id}/notes/{note_id}", response_model=GhostNoteRead)
async def read_ghost_note(
    ghost_id: int, note_id: int, session: AsyncSession = Depends(get_session), can_view: bool = Depends(can_view_ghost)
):
    if not can_view:
        raise HTTPException(status_code=403, detail="You're not authorized to see this ghost")

    query = (
        select(GhostNote)
        .join(GhostNoteEdit, isouter=True)
        .filter(
            GhostNote.id == note_id,
            GhostNote.ghost_id == ghost_id,
            GhostNote.is_deleted is False,
        )
        .order_by(GhostNote.event_time.desc(), GhostNoteEdit.version.desc())
        .group_by(GhostNote.id, GhostNoteEdit.note_id)
        .options(selectinload(GhostNote.user))
    )

    note = (await session.execute(query)).first()
    if not note:
        raise HTTPException(
            status_code=404,
            detail="Note not found",
        )
    return note


# Route to edit a note
@gf.put("/ghosts/{ghost_id}/notes/{note_id}", response_model=GhostNoteRead)
async def update_ghost_note(
    ghost_id: int,
    note_id: int,
    note: GhostNoteUpdate,
    current_user: User = Depends(get_current_user()),
    session: AsyncSession = Depends(get_session),
    can_view: bool = Depends(can_view_ghost),
):
    if not can_view:
        raise HTTPException(status_code=403, detail="You're not authorized to see this ghost")

    query = (
        select(GhostNote)
        .filter(
            GhostNote.id == note_id,
            GhostNote.ghost_id == ghost_id,
            GhostNote.is_deleted is False,
            GhostNote.user_id == current_user.id,
        )
        .options(selectinload(GhostNote.user))
    )
    check_note = (await session.execute(query)).first()
    if not check_note:
        raise HTTPException(
            status_code=404,
            detail="Note not found",
        )

    new_edit = GhostNoteEdit(
        note_id=check_note.note_id,
        user_id=current_user.id,
        content=note.content,
        version=check_note.version + 1,
    )

    session.add(new_edit)
    await session.commit()
    await session.refresh(new_edit)

    return new_edit
