from sqlalchemy.orm import Session
from fastapi import HTTPException

from app.models.user_model import User
from app.models.role_model import Role
from app.models.otp_model import OTPVerification
from app.models.account_model import Account
from jose import JWTError

from app.core.security import (
    create_token_pair,
    decode_refresh_token,
    hash_password,
    verify_password,
)

from app.services.otp_service import create_and_send_otp
from app.services.email_service import EmailDeliveryError
from app.services.account_service import authenticate_account, account_auth_payload


def user_auth_payload(user: User) -> dict:
    role_name = user.role.role_name.upper() if user.role else "USER"
    if role_name not in ("ADMIN", "USER"):
        role_name = "USER"

    name_parts = user.full_name.split(" ") if user.full_name else []
    first_name = name_parts[0] if len(name_parts) > 0 else ""
    last_name = " ".join(name_parts[1:]) if len(name_parts) > 1 else ""

    return {
        "id": str(user.id),
        "email": user.email,
        "name": user.full_name or "",
        "firstName": first_name,
        "lastName": last_name,
        "bio": "",
        "role": role_name,
        "auth_type": "user",
    }


def _token_data_for_user(user: User) -> dict:
    return {
        "user_id": user.id,
        "email": user.email,
        "auth_type": "user",
    }


def _token_data_for_account(account: Account) -> dict:
    return {
        "account_id": account.id,
        "email": account.hudl_email,
        "auth_type": "account",
    }


def _login_response(token_data: dict, user_payload: dict) -> dict:
    return {
        "tokens": create_token_pair(token_data),
        "user": user_payload,
    }


def signup_service(data, db: Session):

    user = db.query(User).filter(
        User.email == data.email
    ).first()

    if user:
        raise HTTPException(400, "Email already exists")

    if data.confirm_password is not None and data.password != data.confirm_password:
        raise HTTPException(400, "Passwords do not match")

    role = db.query(Role).filter(
        Role.role_name == "user"
    ).first()

    if not role:
        raise HTTPException(500, "Default user role not configured")

    user = User(
        full_name=data.full_name,
        email=data.email,
        password=hash_password(data.password),
        role_id=role.id
    )

    db.add(user)
    db.commit()
    db.refresh(user)

    try:
        create_and_send_otp(
            db,
            user.id,
            user.email,
            "signup"
        )
    except EmailDeliveryError as exc:
        raise HTTPException(503, str(exc)) from exc

    return {"message": "OTP sent successfully"}


def verify_signup_otp_service(data, db):

    user = db.query(User).filter(
        User.email == data.email
    ).first()

    otp = db.query(OTPVerification).filter(
        OTPVerification.user_id == user.id,
        OTPVerification.otp_code == data.otp,
        OTPVerification.purpose == "signup",
        OTPVerification.is_used == False
    ).first()

    if not otp:
        raise HTTPException(400, "Invalid OTP")

    otp.is_used = True

    user.is_verified = True

    db.commit()

    return {"message": "Account verified successfully"}


def login_service(data, db):

    user = db.query(User).filter(
        User.email == data.email
    ).first()

    if user and verify_password(data.password, user.password):
        if not user.is_verified:
            raise HTTPException(400, "Account not verified")

        return _login_response(_token_data_for_user(user), user_auth_payload(user))

    account = authenticate_account(db, data.email, data.password)

    if account:
        return _login_response(
            _token_data_for_account(account),
            account_auth_payload(account),
        )

    raise HTTPException(400, "Invalid credentials")


def refresh_token_service(refresh_token: str, db: Session) -> dict:
    try:
        payload = decode_refresh_token(refresh_token)
    except JWTError as exc:
        raise HTTPException(
            status_code=401,
            detail="Invalid or expired refresh token",
        ) from exc

    auth_type = payload.get("auth_type")

    if auth_type == "user":
        user_id = payload.get("user_id")
        user = db.query(User).filter(User.id == user_id).first()
        if not user or not user.is_verified:
            raise HTTPException(
                status_code=401,
                detail="Invalid or expired refresh token",
            )
        token_data = _token_data_for_user(user)
    elif auth_type == "account":
        account_id = payload.get("account_id")
        account = db.query(Account).filter(Account.id == account_id).first()
        if not account:
            raise HTTPException(
                status_code=401,
                detail="Invalid or expired refresh token",
            )
        token_data = _token_data_for_account(account)
    else:
        raise HTTPException(
            status_code=401,
            detail="Invalid or expired refresh token",
        )

    return {"tokens": create_token_pair(token_data)}


def forgot_password_service(data, db):

    user = db.query(User).filter(
        User.email == data.email
    ).first()

    if not user:
        raise HTTPException(404, "User not found")

    try:
        otp = create_and_send_otp(
            db,
            user.id,
            user.email,
            "forgot_password"
        )
    except EmailDeliveryError as exc:
        raise HTTPException(503, str(exc)) from exc

    return {
        "message": "OTP sent successfully",
        "otp": otp,
        "data": {
            "resetToken": data.email,
            "emailToken": data.email
        }
    }


def verify_forgot_otp_service(data, db):

    user = db.query(User).filter(
        User.email == data.email
    ).first()

    otp = db.query(OTPVerification).filter(
        OTPVerification.user_id == user.id,
        OTPVerification.otp_code == data.otp,
        OTPVerification.purpose == "forgot_password",
        OTPVerification.is_used == False
    ).first()

    if not otp:
        raise HTTPException(400, "Invalid OTP")

    otp.is_used = True

    db.commit()

    return {"message": "OTP verified successfully"}


def reset_password_service(data, db):

    user = db.query(User).filter(
        User.email == data.email
    ).first()

    if not user:
        raise HTTPException(404, "User not found")

    if data.new_password != data.confirm_password:
        raise HTTPException(400, "Passwords do not match")

    if verify_password(data.new_password, user.password):
        raise HTTPException(400, "New password cannot be the same as the old password")

    user.password = hash_password(
        data.new_password
    )

    db.commit()

    return {"message": "Password reset successful"}