# SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
#
# SPDX-License-Identifier: MPL-2.0


import os
import uuid
from datetime import datetime

from fastapi import APIRouter, HTTPException
from fastapi.responses import PlainTextResponse
from pydantic import BaseModel
from classquiz.config import redis
from classquiz.db.models import PlayGame, Controller
from classquiz.routers.box_controller.embedded.socket import router as socket_router

router = APIRouter()
router.include_router(socket_router, prefix="/socket")


class JoinGameInput(BaseModel):
    id: uuid.UUID
    secret_key: str
    code: str


class JoinGameResponse(BaseModel):
    id: str


@router.post("/join")
async def join_game(data: JoinGameInput) -> JoinGameResponse:
    controller = await Controller.objects.get_or_none(id=data.id, secret_key=data.secret_key)
    game_pin = await redis.get(f"game:cqc:code:{data.code}")
    if game_pin is None:
        raise HTTPException(status_code=404, detail="Game not found")
    game = await redis.get(f"game:{game_pin}")
    game = PlayGame.model_validate_json(game)
    # Check if game is already running
    if game.started:
        raise HTTPException(status_code=400, detail="Game started already")
    # check if username already exists
    if await redis.get(f"game_session:{game_pin}:players:{controller.player_name}") is not None:
        raise HTTPException(status_code=409, detail="Username already exists")

    player_id = os.urandom(5).hex()
    await redis.set(f"game:cqc:player:{player_id}", controller.player_name)
    return JoinGameResponse(id=f"{player_id}:{game_pin}")


class RegisterWithCodeInput(BaseModel):
    code: str


class RegisterWithCodeResponse(BaseModel):
    id: uuid.UUID
    secret_key: str


@router.post("/register")
async def register_with_code(data: RegisterWithCodeInput) -> RegisterWithCodeResponse:
    c_id = await redis.get(f"controller_setup:{data.code}")
    if c_id is None:
        raise HTTPException(status_code=404, detail="Code not found")
    await redis.delete(f"controller_setup:{data.code}")
    c_id = uuid.UUID(c_id)
    controller = await Controller.objects.get(id=c_id)
    controller.first_seen = datetime.now()
    controller.last_seen = datetime.now()
    await controller.update()
    return RegisterWithCodeResponse(id=controller.id, secret_key=controller.secret_key)


@router.get("/ping")
async def ping_server(id: uuid.UUID, secret_key: str, version: str):
    controller = await Controller.objects.get_or_none(id=id, secret_key=secret_key)
    if controller is None:
        raise HTTPException(status_code=404, detail="Key and/or id invalid")
    controller.last_seen = datetime.now()
    controller.os_version = version
    await controller.update()


@router.get("/update")
async def get_firmware_version(id: uuid.UUID, secret_key: str) -> PlainTextResponse:
    controller = await Controller.objects.get_or_none(id=id, secret_key=secret_key)
    if controller is None:
        return PlainTextResponse(status_code=404, content="Key and/or id invalid")
    if controller.wanted_os_version is None:
        return PlainTextResponse(status_code=400, content="No update needed")
    return PlainTextResponse(status_code=200, content=controller.wanted_os_version)
