from datetime import datetime, timedelta
import random
from typing import Literal
from fastapi import BackgroundTasks, HTTPException
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
import app.api.user.schemas as schemas
from app.dependency.authantication import JWTManager, JWTPayloadSchema
from app.locale.messages import Messages
from app.models.main.user import UserBase, TblUser
from app.utils.email import RESET_OTP_EXPIRE_MINUTES, send_otp_email
from app.utils.schemas_utils import CustomResponse

VALID_ROLES: tuple[Literal["student", "admin", "superadmin"], ...] = ("student", "admin", "superadmin")

class UserService:
    def __init__(self, db: Session, token:JWTPayloadSchema):
        self.db = db
        self.token = token

    async def create_user(self, request: schemas.UserCreate):
        created_user = UserBase.model_validate(request)
        TblUser.create(created_user, self.db)
        self.db.commit()
        return CustomResponse(status="1", message=Messages.USER_CREAT)

    @staticmethod
    async def user_login(credentials: OAuth2PasswordRequestForm, db: Session):
        user_filter = {"email_id": credentials.username}
        user_log = TblUser.get_by_user_filter(user_filter, db)
        if not user_log:
            raise HTTPException(status_code=404, detail=Messages.USER_NOT_FOUND)
        if credentials.password != user_log.password:
            raise HTTPException(status_code=401, detail=Messages.INCORRECT_PASSWORD)
        role = user_log.role.strip()
        if role not in VALID_ROLES:
            raise HTTPException(status_code=400, detail=f"Invalid role: {role}")
        payload = JWTPayloadSchema(
            user_id=user_log.user_id,
            user_type="admin",  # or determine dynamically if needed
            user_role=role,
            exp=datetime.utcnow()
        )
        access_token = JWTManager.create_access_token(payload)
        return {
            "access_token": access_token,
            "token_type": "bearer",
            "email_id": user_log.email_id,
            "role": role,
            "user_id":user_log.user_id
        }

    async def get_demo_user(self):
        if self.token.user_id is None:
            raise HTTPException(status_code=400, detail=Messages.USER_ID_MISSING)
        user = TblUser.get_usr_id(self.token.user_id, self.db)
        if not user:
            return CustomResponse(status="-1", message=Messages.USER_NOT_FOUND)
        return user

    async def logout(self):
        if self.token.user_id is None:
            raise HTTPException(status_code=400, detail=Messages.USER_ID_MISSING)
        user = self.db.query(TblUser).filter(TblUser.user_id == self.token.user_id).first()
        if not user:
            return CustomResponse(status="-1", message=Messages.USER_NOT_FOUND)
        self.db.commit()
        return CustomResponse(status="1", message=Messages.USER_LOGOUT)

    async def update_pass(self, request: schemas.UserPassUpdate):
        user_id = self.token.user_id
        if not user_id:
            return CustomResponse(status="-1", message=Messages.USER_ID_MISSING)
        updated_user = schemas.UserPassUpdate.model_validate(request)
        user = TblUser.get_usr_id(user_id, self.db)
        if not user:
            return CustomResponse(status="-1", message=Messages.USER_NOT_FOUND)
        if updated_user.current_password != user.password:
            return CustomResponse(status="-1", message=Messages.CURRENT_PASSORD_INCORRECT)
        if updated_user.new_password != updated_user.confirm_password:
            return CustomResponse(status="-1", message=Messages.NEWPASSWORD_CONFIRMPASSWORD_NOT_MATCH)
        user.password = updated_user.new_password
        self.db.commit()  
        return CustomResponse(status="1", message=Messages.PASSWORD_UPDATED)


    async def forgot_password_service(self, request: schemas.ForgotPasswordRequest, background_tasks: BackgroundTasks):
        """Generates an OTP and sends it via email."""
        user = self.db.query(TblUser).filter(TblUser.email_id == request.email_id).first()
        if not user:
            raise HTTPException(status_code=404,detail=Messages.USER_NOT_FOUND)
        otp = str(random.randint(100000, 999999))
        user.otp = otp
        user.otp_expiry = datetime.utcnow() + timedelta(minutes=RESET_OTP_EXPIRE_MINUTES)
        self.db.add(user)
        self.db.commit()
        background_tasks.add_task(send_otp_email, request.email_id, otp)
        return CustomResponse(status="1", message=Messages.OTP_SEND)

    def reset_password_service(self, request: schemas.VerifyOTPRequest):
        """Verifies OTP and allows password reset."""
        user = self.db.query(TblUser).filter(TblUser.email_id == request.email_id).first()
        if not user:
            raise HTTPException(status_code=404, detail=Messages.USER_NOT_FOUND)
        if (
            user.otp != request.otp or user.otp_expiry is None or user.otp_expiry < datetime.utcnow()
        ):
            raise HTTPException(status_code=400, detail=Messages.INVALID_OTP)
        user.password = request.new_password
        user.otp_expiry = None
        self.db.add(user)
        self.db.commit()
        return CustomResponse(status="1", message=Messages.PASSWORD_UPDATED)

