from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, UploadFile, status
from sqlalchemy.orm import Session, joinedload

from app.database import SessionLocal, get_db, release_db_connection
from app.dependencies import require_admin_token
from app.models import User
from app.models_admin import Reward
from app.schemas import (
    Message,
    RewardClaimIdRequest,
    RewardClaimListResponse,
    RewardClaimOut,
    RewardClaimStatusRequest,
    RewardIdRequest,
    RewardListResponse,
    RewardOut,
)
from app.services.admin_service import (
    create_reward,
    delete_reward as remove_reward,
    delete_reward_claim,
    list_reward_claims,
    list_rewards as fetch_rewards,
    save_reward_media,
    serialize_reward,
    serialize_reward_claim,
    send_member_notification,
    update_reward_claim_status,
    update_reward,
)

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


def _notify_claim_status(db: Session, admin: User, claim: RewardClaimOut | object, status_value: str) -> None:
    status_normalized = status_value.strip().lower()
    if status_normalized not in {"accepted", "rejected"}:
        return

    product_name = getattr(claim, "product_name", None) or "your reward claim"
    total_points = getattr(claim, "total_points", None)
    available_points = getattr(claim, "available_points", None) or getattr(claim, "remaining_points", None)

    if status_normalized == "accepted":
        title = "Reward claim accepted"
        message = f"Your claim for {product_name} has been accepted."
    else:
        title = "Reward claim rejected"
        message = f"Your claim for {product_name} has been rejected."

    if total_points is not None:
        message += f" Claim points: {int(total_points):,}."
    if available_points is not None:
        message += f" Available balance: {int(available_points):,} points."

    send_member_notification(
        db,
        admin,
        user_id=getattr(claim, "user_id"),
        notification_type="reward",
        title=title,
        message=message,
    )


@router.post(
    "/add-reward",
    response_model=RewardOut,
    status_code=status.HTTP_201_CREATED,
    summary="Create a reward (multipart: name, description, points, category, media)",
)
async def add_reward(
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin_token),
    name: str = Form(..., description="Reward name"),
    description: str = Form(..., description="Reward description"),
    points_required: int = Form(..., ge=1, description="Points needed to claim"),
    category: str = Form(..., description="Reward category"),
    media: UploadFile | None = File(default=None, description="Reward image"),
):
    name_clean = name.strip()
    description_clean = description.strip()
    category_clean = category.strip()
    if not name_clean:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Name is required")
    if not category_clean:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Category is required")

    admin_id = admin.id
    release_db_connection(db)
    media_path: str | None = None
    media_content_type: str | None = None
    if media and media.filename:
        media_path, media_content_type = await save_reward_media(media)

    write_db = SessionLocal()
    try:
        admin_user = write_db.get(User, admin_id)
        if admin_user is None:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Admin not found")
        reward = create_reward(
            db=write_db,
            admin=admin_user,
            name=name_clean,
            description=description_clean,
            points_required=points_required,
            category=category_clean,
            media_path=media_path,
            media_content_type=media_content_type,
        )
        return serialize_reward(reward)
    finally:
        write_db.close()


@router.post(
    "/edit-reward",
    response_model=RewardOut,
    summary="Update a reward (multipart: reward_id, name, description, points, category, media)",
)
async def edit_reward(
    db: Session = Depends(get_db),
    _: User = Depends(require_admin_token),
    reward_id: int = Form(..., ge=1, description="Reward id to update"),
    name: str = Form(..., description="Reward name"),
    description: str = Form(..., description="Reward description"),
    points_required: int = Form(..., ge=1, description="Points needed to claim"),
    category: str = Form(..., description="Reward category"),
    media: UploadFile | None = File(default=None, description="New reward image"),
    remove_media: bool = Form(default=False, description="Set true to remove image without uploading a new one"),
):
    name_clean = name.strip()
    category_clean = category.strip()
    if not name_clean:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Name is required")
    if not category_clean:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Category is required")

    exists = db.query(Reward.id).filter(Reward.id == reward_id).first()
    if not exists:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Reward not found")

    replace_media = False
    media_path: str | None = None
    media_content_type: str | None = None
    release_db_connection(db)
    if media and media.filename:
        media_path, media_content_type = await save_reward_media(media)
        replace_media = True

    write_db = SessionLocal()
    try:
        reward = (
            write_db.query(Reward)
            .options(joinedload(Reward.created_by))
            .filter(Reward.id == reward_id)
            .first()
        )
        if not reward:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Reward not found")
        reward = update_reward(
            db=write_db,
            reward=reward,
            name=name_clean,
            description=description.strip(),
            points_required=points_required,
            category=category_clean,
            media_path=media_path,
            media_content_type=media_content_type,
            replace_media=replace_media,
            clear_media=remove_media and not replace_media,
        )
        return serialize_reward(reward)
    finally:
        write_db.close()


@router.get(
    "/list-rewards",
    response_model=RewardListResponse,
    summary="List all rewards (newest first)",
)
def list_rewards(
    db: Session = Depends(get_db),
    _: User = Depends(require_admin_token),
):
    rewards = fetch_rewards(db)
    serialized = [serialize_reward(r) for r in rewards]
    return RewardListResponse(total=len(serialized), rewards=serialized)


@router.post(
    "/delete-reward",
    response_model=Message,
    summary="Delete a reward by id",
)
def delete_reward(
    body: RewardIdRequest,
    db: Session = Depends(get_db),
    _: User = Depends(require_admin_token),
):
    if not remove_reward(db, body.reward_id):
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Reward not found")
    return Message(message="Reward deleted")


@router.get(
    "/list-reward-claims",
    response_model=RewardClaimListResponse,
    summary="List member reward claim requests for admin review",
)
def list_claims(
    db: Session = Depends(get_db),
    _: User = Depends(require_admin_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_reward_claims(db, status_filter=status_filter)
    return RewardClaimListResponse(
        total=len(claims),
        claims=[serialize_reward_claim(claim) for claim in claims],
    )


@router.post(
    "/update-reward-claim-status",
    response_model=RewardClaimOut,
    summary="Accept, reject, or reset a reward claim request",
)
def update_claim_status(
    body: RewardClaimStatusRequest,
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin_token),
):
    claim = update_reward_claim_status(db, body.claim_id, body.status)
    if not claim:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Reward claim not found")
    _notify_claim_status(db, admin, claim, body.status)
    return serialize_reward_claim(claim)


@router.post(
    "/reject-reward-claim",
    response_model=RewardClaimOut,
    summary="Reject one reward claim request",
)
def reject_claim(
    body: RewardClaimIdRequest,
    db: Session = Depends(get_db),
    admin: User = Depends(require_admin_token),
):
    claim = update_reward_claim_status(db, body.claim_id, "rejected")
    if not claim:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Reward claim not found")
    _notify_claim_status(db, admin, claim, "rejected")
    return serialize_reward_claim(claim)


@router.post(
    "/delete-reward-claim",
    response_model=Message,
    summary="Delete one reward claim request",
)
def delete_claim(
    body: RewardClaimIdRequest,
    db: Session = Depends(get_db),
    _: User = Depends(require_admin_token),
):
    if not delete_reward_claim(db, body.claim_id):
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Reward claim not found")
    return Message(message="Reward claim deleted")
