from dataclasses import dataclass

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jose import JWTError
from sqlalchemy.orm import Session

from app.core.database import get_db
from app.core.security import decode_access_token
from app.models.account_model import Account
from app.models.user_model import User

bearer_scheme = HTTPBearer(auto_error=True)


@dataclass
class AuthContext:
    user: User | None = None
    account: Account | None = None

    @property
    def auth_type(self) -> str:
        if self.user is not None:
            return "user"
        if self.account is not None:
            return "account"
        raise ValueError("AuthContext has no user or account")


def resolve_auth_from_token(token: str, db: Session) -> AuthContext:
    try:
        payload = decode_access_token(token)
    except JWTError as exc:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or expired token",
            headers={"WWW-Authenticate": "Bearer"},
        ) from exc

    user_id = payload.get("user_id")
    if user_id is not None:
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="User not found",
            )
        if not user.is_verified:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Account not verified",
            )
        return AuthContext(user=user)

    account_id = payload.get("account_id")
    if account_id is not None:
        account = db.query(Account).filter(Account.id == account_id).first()
        if not account:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Account not found",
            )
        return AuthContext(account=account)

    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Invalid token payload",
    )


def resolve_account_from_token(token: str, db: Session) -> Account:
    auth = resolve_auth_from_token(token, db)
    if auth.account is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Account token required",
        )
    return auth.account


async def require_auth(
    credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
    db: Session = Depends(get_db),
) -> AuthContext:
    """
    Accepts JWT from either users table login or accounts table login.
    """
    return resolve_auth_from_token(credentials.credentials, db)


async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
    db: Session = Depends(get_db),
) -> User:
    auth = resolve_auth_from_token(credentials.credentials, db)
    if auth.user is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="User token required",
        )
    return auth.user


async def get_current_account(
    credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
    db: Session = Depends(get_db),
) -> Account:
    auth = resolve_auth_from_token(credentials.credentials, db)
    if auth.account is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Account token required",
        )
    return auth.account
