from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.orm import Session

from app.database import get_db
from app.dependencies import require_user_token
from app.models import User
from app.schemas import (
    MemberPointsOut,
    RewardClaimListResponse,
    RewardClaimOut,
    RewardClaimRequest,
    RewardListResponse,
)
from app.services.admin_service import (
    create_reward_claim,
    list_rewards as fetch_rewards,
    list_user_reward_claims,
    serialize_reward,
    serialize_reward_claim,
    calculate_member_points,
    get_member_points,
)

router = APIRouter(tags=["user-rewards"])


@router.get(
    "/list-rewards",
    response_model=RewardListResponse,
    summary="List rewards for the user app (newest first)",
)
def list_rewards(
    db: Session = Depends(get_db),
    user: User = Depends(require_user_token),
):
    rewards = fetch_rewards(db)
    serialized = [serialize_reward(r) for r in rewards]
    points = get_member_points(db, user.id) or calculate_member_points(db, user.id)
    return RewardListResponse(
        total=len(serialized),
        rewards=serialized,
        available_points=points["available_points"],
        total_points=points["assigned_points_total"],
        assigned_points_total=points["assigned_points_total"],
    )


@router.get(
    "/list-reward-claims",
    response_model=RewardClaimListResponse,
    summary="List reward claims for the logged-in user only",
)
def list_my_reward_claims(
    db: Session = Depends(get_db),
    user: User = Depends(require_user_token),
    status_filter: str | None = Query(
        None,
        alias="status",
        description="Optional: pending | accepted | rejected",
    ),
):
    if status_filter:
        status_filter = status_filter.strip().lower()
        if status_filter not in {"pending", "accepted", "rejected"}:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="status must be one of: pending, accepted, rejected",
            )
    claims = list_user_reward_claims(db, user.id, status_filter=status_filter)
    serialized = [RewardClaimOut.model_validate(serialize_reward_claim(c)) for c in claims]
    return RewardClaimListResponse(total=len(serialized), claims=serialized)


@router.post(
    "/claim-reward",
    response_model=RewardClaimOut,
    status_code=status.HTTP_201_CREATED,
    summary="Claim a reward product for the logged-in user",
)
def claim_reward(
    body: RewardClaimRequest,
    db: Session = Depends(get_db),
    user: User = Depends(require_user_token),
):
    try:
        claim = create_reward_claim(db, user, body)
    except ValueError as e:
        msg = str(e or '')
        if 'already claimed' in msg.lower():
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail={
                    'error': 'already_claimed',
                    'message': msg,
                },
            ) from e
        # If the ValueError is about insufficient points, return 400 with structured JSON
        if 'phone number' in msg.lower():
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=msg) from e
        if 'Insufficient' in msg:
            # try to extract numbers (available and requested) from the message
            import re

            nums = re.findall(r"\d+", msg)
            available = int(nums[0]) if len(nums) >= 1 else None
            requested = int(nums[1]) if len(nums) >= 2 else None
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail={
                    'error': 'insufficient_points',
                    'available': available,
                    'requested': requested,
                    'message': msg,
                },
            ) from e
        # otherwise (e.g. reward not found) return 404
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=msg) from e
    return serialize_reward_claim(claim)


@router.get(
    "/points",
    response_model=MemberPointsOut,
    summary="Get current assigned and available points for the logged-in user",
)
def get_points(
    db: Session = Depends(get_db),
    user: User = Depends(require_user_token),
):
    data = get_member_points(db, user.id) or {"user_id": user.id, "assigned_points_total": 0, "available_points": 0}
    return MemberPointsOut(**data)
