from uuid import UUID
from fastapi import APIRouter, HTTPException
from fastapi.params import Depends
from sqlalchemy import select
from sqlalchemy.orm import Session

from app.database import get_db
from app.schemas.auth_token import AccessToken
from app.schemas.id_token import IdToken, IdTokenCreate, IdTokenUpdate
from app.models.id_token import IdToken as DbIdToken
from app.models.user import User as DbUser
from app.schemas.user import Role
from app.security.jwt_bearer import JWTBearer

router = APIRouter(
    prefix="/id-tokens",
    tags=["IdToken (v1)"]
)

@router.get(path="", response_model=list[IdToken])
async def get_id_tokens(
    skip: int = 0,
    limit: int = 20,
    db: Session = Depends(get_db),
    token: AccessToken = Depends(JWTBearer()),
):
    stmt = select(DbIdToken)
    if token.role != Role.ADMINISTRATOR:
        stmt = stmt.where(DbIdToken.owner_id == token.subject)
    stmt = stmt.order_by(DbIdToken.id).offset(skip).limit(limit)
    result = db.execute(stmt)
    return result.scalars().all()

@router.get(path="/{id_token_id}", response_model=IdToken)
async def get_id_token(
    id_token_id: UUID,
    db: Session = Depends(get_db),
    token: AccessToken = Depends(JWTBearer()),
):
    stmt = select(DbIdToken).where(DbIdToken.id == id_token_id)
    result = db.execute(stmt)
    id_token = result.scalars().first()
    if id_token == None:
        raise HTTPException(status_code=404, detail="IdToken not found")
    if token.role != Role.ADMINISTRATOR and id_token.owner_id != token.subject:
        raise HTTPException(status_code=404, detail="IdToken not found")
    return id_token

@router.post(path="", status_code=201, response_model=IdToken)
async def create_id_token(
    create_id_token: IdTokenCreate,
    db: Session = Depends(get_db),
    token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
    stmt = select(DbUser).where(DbUser.id == create_id_token.owner_id)
    result = db.execute(stmt)
    owner = result.scalars().first()
    if owner == None:
        raise HTTPException(status_code=422, detail=[{
            "loc": ["body", "owner_id"],
            "msg": "Owner not found",
            "type": "invalid_relation" 
        }])
    id_token = DbIdToken(
        friendly_name=create_id_token.friendly_name,
        is_active=create_id_token.is_active,
        token=create_id_token.token,
        owner_id=create_id_token.owner_id
    )
    db.add(id_token)
    db.commit()
    db.refresh(id_token)
    return id_token

@router.patch(path="/{id_token_id}", response_model=IdToken)
async def update_id_token(
    id_token_id: UUID,
    id_token_update: IdTokenUpdate,
    db: Session = Depends(get_db),
    token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
    stmt = select(DbIdToken).where(DbIdToken.id == id_token_id)
    result = db.execute(stmt)
    id_token = result.scalars().first()
    if id_token is None:
        raise HTTPException(status_code=404, detail="IdToken not found")
    for key, value in id_token_update.model_dump(exclude_unset=True).items():
        if key == "owner_id":
            stmt = select(DbUser).where(DbUser.id == id_token_update.owner_id)
            result = db.execute(stmt)
            owner = result.scalars().first()
            if owner == None:
                raise HTTPException(status_code=422, detail=[{
                    "loc": ["body", "owner_id"],
                    "msg": "Owner not found",
                    "type": "invalid_relation" 
                }])
        setattr(id_token, key, value)
    db.commit()
    return id_token

@router.delete(path="/{id_token_id}", response_model=None)
async def delete_id_token(
    id_token_id: UUID,
    db: Session = Depends(get_db),
    token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
    stmt = select(DbIdToken).where(DbIdToken.id == id_token_id)
    result = db.execute(stmt)
    id_token = result.scalars().first()
    if id_token == None:
        raise HTTPException(status_code=404, detail="IdToken not found")
    db.delete(id_token)
    db.commit()
    return []