from datetime import datetime, timezone
from typing import Any

from fastapi import APIRouter, HTTPException, Request
from pydantic import BaseModel, EmailStr

from common.jwt_auth import (
    create_access_token,
    create_refresh_token,
    get_password_hash,
    verify_password,
    decode_token,
)
from common.sqlite_client import sqlite_client as db

router = APIRouter(prefix="/api/v1/auth", tags=["auth"])


def utc_now() -> str:
    return datetime.now(timezone.utc).isoformat()


class UserRegister(BaseModel):
    username: str
    email: EmailStr
    password: str


class UserLogin(BaseModel):
    username: str
    password: str


class RefreshTokenRequest(BaseModel):
    refresh_token: str


@router.post("/register")
async def register(user_data: UserRegister):
    # Check if username already exists
    existing = await db.fetch_one(
        "SELECT id FROM cuccu_users WHERE username = ?", (user_data.username,)
    )
    if existing:
        raise HTTPException(status_code=400, detail="Username already registered")

    # Check if email already exists
    existing_email = await db.fetch_one(
        "SELECT id FROM cuccu_users WHERE email = ?", (user_data.email,)
    )
    if existing_email:
        raise HTTPException(status_code=400, detail="Email already registered")

    hashed_password = get_password_hash(user_data.password)
    now = utc_now()

    # Insert new user — sqlite_client.execute() auto-commits
    cursor = await db.execute(
        "INSERT INTO cuccu_users (username, email, password_hash, nickname, role, created_at) VALUES (?, ?, ?, ?, ?, ?)",
        (user_data.username, user_data.email, hashed_password, user_data.username, "user", now),
    )
    user_id = str(cursor.lastrowid)

    access_token = create_access_token(data={"sub": user_id})
    refresh_token, expires_at = create_refresh_token(data={"sub": user_id})

    await db.execute(
        "INSERT INTO cuccu_refresh_tokens (user_id, token, expires_at, created_at) VALUES (?, ?, ?, ?)",
        (user_id, refresh_token, expires_at.isoformat(), now),
    )

    return {
        "access_token": access_token,
        "refresh_token": refresh_token,
        "token_type": "bearer",
        "user": {
            "id": user_id,
            "username": user_data.username,
            "email": user_data.email,
        },
    }


@router.post("/login")
async def login(user_data: UserLogin):
    user = await db.fetch_one(
        "SELECT * FROM cuccu_users WHERE username = ?", (user_data.username,)
    )
    if not user:
        raise HTTPException(status_code=400, detail="Incorrect username or password")

    if not verify_password(user_data.password, user["password_hash"]):
        raise HTTPException(status_code=400, detail="Incorrect username or password")

    user_id = str(user["id"])
    access_token = create_access_token(data={"sub": user_id})
    refresh_token, expires_at = create_refresh_token(data={"sub": user_id})
    now = utc_now()

    await db.execute(
        "INSERT INTO cuccu_refresh_tokens (user_id, token, expires_at, created_at) VALUES (?, ?, ?, ?)",
        (user_id, refresh_token, expires_at.isoformat(), now),
    )

    return {
        "access_token": access_token,
        "refresh_token": refresh_token,
        "token_type": "bearer",
        "user": {
            "id": user_id,
            "username": user["username"],
            "email": user["email"],
        },
    }


@router.post("/refresh")
async def refresh(data: RefreshTokenRequest):
    token = data.refresh_token
    payload = decode_token(token)
    if not payload or "sub" not in payload:
        raise HTTPException(status_code=401, detail="Invalid refresh token")

    user_id = payload["sub"]
    stored = await db.fetch_one(
        "SELECT id FROM cuccu_refresh_tokens WHERE token = ? AND user_id = ?",
        (token, user_id),
    )
    if not stored:
        raise HTTPException(status_code=401, detail="Refresh token revoked or invalid")

    access_token = create_access_token(data={"sub": user_id})
    return {"access_token": access_token, "token_type": "bearer"}


@router.get("/me")
async def get_me(request: Request):
    if not request.state.is_authenticated:
        raise HTTPException(status_code=401, detail="Not authenticated")

    user_id = request.state.user_id

    user = await db.fetch_one(
        "SELECT id, username, email, role FROM cuccu_users WHERE id = ?", (user_id,)
    )
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    return {
        "id": str(user["id"]),
        "username": user["username"],
        "email": user["email"],
        "role": user["role"] or "user",
    }
