import secrets
from logging import getLogger
from typing import Literal

from fastapi import APIRouter
from sqlmodel import select

from apps.esia.sign import get_url
from apps.users.models import User
from clients import clients as c
from database import AsyncSessionDep
from shared import exceptions as e
from shared.redis import client as cache

from . import schema as s

logger = getLogger(__name__)
router = APIRouter(
    prefix='/esia',
    tags=[
        'ESIA',
    ],
)


@router.get('/login', response_model=s.LoginURL)
async def login(platform: Literal['max', 'app'] = 'app'):
    url = get_url(platform)
    return s.LoginURL(url=url)


@router.post('/callback')
async def callback(session: AsyncSessionDep, code: str):
    token = None
    for i in range(3):
        try:
            token = await c.esia_api.access_token(code)
            break
        except Exception:
            logger.warning(
                'Error occurred while accessing ESI API. Retrying...'
            )
            if i == 2:
                raise

    if token is None:
        raise e.BadRequestException

    esia_user = await c.esia_api.get_user_info(
        token.access_token, token.id_token
    )

    try:
        vita_user = await c.vitacore_api.findBySnils(esia_user.snils)
        patId = vita_user.patId
    except e.UnknownException:
        raise e.BadRequestException(detail='Patient not found') from None

    existing_user_stmt = select(User).where(User.vita_id == patId).limit(1)
    existing_user = (
        await session.execute(existing_user_stmt)
    ).scalar_one_or_none()

    if existing_user is None:
        user = User(vita_id=patId)
        session.add(user)
        await session.commit()
        await session.refresh(user)

    else:
        user = existing_user

    access_token = secrets.token_urlsafe(32)
    await cache.set(access_token, f'user:{user.id}')

    return s.Token(access_token=access_token)


@router.post('/max/callback')
async def max_callback(session: AsyncSessionDep, code: str):
    token = None
    for i in range(3):
        try:
            token = await c.esia_api.access_token(code)
            break
        except Exception:
            logger.warning(
                'Error occurred while accessing ESI API. Retrying...'
            )
            if i == 2:
                raise

    if token is None:
        raise e.BadRequestException

    esia_user = await c.esia_api.get_user_info(
        token.access_token, token.id_token
    )

    try:
        vita_user = await c.vitacore_api.findBySnils(esia_user.snils)
        patId = vita_user.patId
    except e.UnknownException:
        raise e.BadRequestException(detail='Patient not found') from None

    existing_user_stmt = select(User).where(User.vita_id == patId).limit(1)
    existing_user = (
        await session.execute(existing_user_stmt)
    ).scalar_one_or_none()

    if existing_user is None:
        user = User(vita_id=patId)
        session.add(user)
        await session.commit()
        await session.refresh(user)

    else:
        user = existing_user

    verify_token = secrets.token_urlsafe(32)
    await cache.set(f'verify:{verify_token}', f'user:{user.id}')

    return s.Token(access_token=verify_token)


@router.post('/max/verify')
async def max_verify(token: str):
    user = await cache.get(f'verify:{token}')

    if user is None:
        raise e.BadRequestException(detail='Invalid token')

    access_token = secrets.token_urlsafe(32)
    await cache.set(access_token, user.decode())

    return s.Token(access_token=access_token)
