from sqlalchemy.orm import Session
from fastapi import HTTPException
from typing import Optional, Dict
from datetime import datetime, date, timedelta
from app.models.user_points import UserPoints
from app.models.point_transaction import PointTransaction
from app.models.step_record import StepRecord
from app.services.admin_service import get_setting_float, get_setting_int
from app.core.time import now
import logging
from sqlalchemy import func
import re

logger = logging.getLogger('adimsayar')

def get_user_points(db: Session, user_id: int) -> int:
    """사용자의 현재 보유 포인트를 조회합니다."""
    user_points = db.query(UserPoints).filter(UserPoints.user_id == user_id).first()
    if not user_points:
        # 포인트 레코드가 없으면 0으로 초기화
        user_points = UserPoints(user_id=user_id, total_points=0)
        db.add(user_points)
        db.commit()
        db.refresh(user_points)
    
    return user_points.total_points

def add_points(
    db: Session, 
    user_id: int, 
    amount: int, 
    transaction_type: str, 
    description: str
) -> UserPoints:
    """
    사용자에게 포인트를 적립합니다.
    
    Args:
        db: 데이터베이스 세션
        user_id: 사용자 ID
        amount: 적립할 포인트 양 (양수)
        transaction_type: 트랜잭션 유형 (step, achievement, referral_bonus 등)
        description: 트랜잭션 설명
        
    Returns:
        업데이트된 UserPoints 객체
    """
    if amount <= 0:
        raise HTTPException(status_code=400, detail="Points amount must be positive")
    
    # 사용자 포인트 레코드 조회 또는 생성
    user_points = db.query(UserPoints).filter(UserPoints.user_id == user_id).first()
    if not user_points:
        user_points = UserPoints(user_id=user_id, total_points=0)
        db.add(user_points)
    
    # 포인트 증가
    user_points.total_points += amount
    
    # 트랜잭션 기록
    transaction = PointTransaction(
        user_id=user_id,
        transaction_type=transaction_type,
        amount=amount,
        description=description,
        created_at=now()
    )
    
    db.add(transaction)
    db.commit()
    db.refresh(user_points)
    
    return user_points

def deduct_points(
    db: Session, 
    user_id: int, 
    amount: int, 
    transaction_type: str, 
    description: str
) -> UserPoints:
    """
    사용자의 포인트를 차감합니다.
    
    Args:
        db: 데이터베이스 세션
        user_id: 사용자 ID
        amount: 차감할 포인트 양 (양수로 입력하면 내부적으로 음수 처리)
        transaction_type: 트랜잭션 유형 (redeem 등)
        description: 트랜잭션 설명
        
    Returns:
        업데이트된 UserPoints 객체
        
    Raises:
        HTTPException: 포인트 부족 시 발생
    """
    if amount <= 0:
        raise HTTPException(status_code=400, detail="Points amount must be positive")
    
    # 현재 보유 포인트 확인
    current_points = get_user_points(db, user_id)
    
    # 포인트 부족 체크
    if current_points < amount:
        raise HTTPException(
            status_code=400, 
            detail=f"Not enough points. Required: {amount}, Available: {current_points}"
        )
    
    # 사용자 포인트 레코드 조회
    user_points = db.query(UserPoints).filter(UserPoints.user_id == user_id).first()
    
    # 포인트 차감
    user_points.total_points -= amount
    
    # 트랜잭션 기록 (음수 금액으로 저장)
    transaction = PointTransaction(
        user_id=user_id,
        transaction_type=transaction_type,
        amount=-amount,  # 음수로 저장하여 차감 표시
        description=description,
        created_at=now()
    )
    
    db.add(transaction)
    db.commit()
    db.refresh(user_points)
    
    return user_points

def deduct_points_for_order(
    db: Session, 
    user_id: int, 
    amount: int, 
    order_number: str,
    order_id: int
) -> UserPoints:
    """
    주문에 대한 포인트를 차감합니다.
    
    Args:
        db: 데이터베이스 세션
        user_id: 사용자 ID
        amount: 차감할 포인트 양
        order_number: 주문 번호
        order_id: 주문 ID
        
    Returns:
        업데이트된 UserPoints 객체
    """
    description = f"Order #{order_number} payment"
    
    # 기존 트랜잭션 타입 중 "redeem"을 사용 (주문 결제는 일종의 포인트 사용이므로)
    return deduct_points(
        db=db,
        user_id=user_id,
        amount=amount,
        transaction_type="redeem",
        description=description
    )

def calculate_unconverted_points(db: Session, user_id: int, country_code: str = None) -> Dict:
    """
    사용자의 아직 포인트로 전환되지 않은 걸음수를 계산합니다.
    포인트는 1000걸음 단위로 전환됩니다.
    
    Args:
        db: 데이터베이스 세션
        user_id: 사용자 ID
        country_code: 국가 코드 (설정값 조회용)
        
    Returns:
        계산된 정보를 담은 Dictionary
    """
    from datetime import date

    # Use today's date
    today = date.today()

    # Total steps for today
    records = db.query(StepRecord).filter(
        StepRecord.user_id == user_id,
        StepRecord.record_date == today
    ).all()
    total_steps = sum(record.steps_count for record in records) if records else 0

    # Sum up the actual steps that were converted by parsing the transaction description.
    transactions = db.query(PointTransaction).filter(
        PointTransaction.user_id == user_id,
        PointTransaction.transaction_type == "step",
        func.date(PointTransaction.created_at) == today
    ).all()

    converted_steps = 0
    for tx in transactions:
        # Expecting description like: "Step points conversion: {conversion_points} points from {convertible_steps} steps with multiplier {multiplier}."
        m = re.search(r"from (\d+) steps", tx.description)
        if m:
            converted_steps += int(m.group(1))
        else:
            # Fallback if pattern not found, assume no steps were accounted
            pass

    # Calculate unconverted steps
    unconverted_steps = total_steps - converted_steps
    if unconverted_steps < 0:
        unconverted_steps = 0

    # Only full 1000-step units can be converted
    convertible_steps = (unconverted_steps // 1000) * 1000

    return {
        "unconverted_steps": unconverted_steps,
        "potential_points": unconverted_steps,
        "convertible_points": convertible_steps
    }

def convert_steps_to_points(db: Session, user_id: int, country_code: str = None) -> Dict:
    """
    걸음 기록에서 아직 전환되지 않은 걸음수를 포인트로 전환합니다.
    포인트는 1000점 단위로만 전환됩니다.
    
    Args:
        db: 데이터베이스 세션
        user_id: 사용자 ID
        country_code: 국가 코드 (설정값 조회용)
        
    Returns:
        전환 결과를 담은 Dictionary
    """
    # 미전환 포인트 계산
    conversion_info = calculate_unconverted_points(db, user_id, country_code)
    
    # 전환 가능한 걸음 단위가 1000미만이면 전환하지 않음
    convertible_steps = conversion_info["convertible_points"]
    if convertible_steps < 1000:
        return {
            "success": False,
            "message": "Not enough points to convert. Minimum 1000 steps required.",
            "converted_points": 0,
            "remaining_unconverted_points": conversion_info["potential_points"],
            "current_total_points": get_user_points(db, user_id)
        }

    # Use admin settings for step threshold and multiplier
    today = date.today()
    records = db.query(StepRecord).filter(
        StepRecord.user_id == user_id,
        StepRecord.record_date == today
    ).all()
    total_steps_day = sum(record.steps_count for record in records) if records else 0

    threshold = get_setting_int(db, "STEP_THRESHOLD", country_code)
    step_multiplier = get_setting_float(db, "STEP_MULTIPLIER", country_code)
    multiplier = step_multiplier if total_steps_day >= threshold else 1
    conversion_points = int(convertible_steps * multiplier)

    # 포인트 트랜잭션 생성
    point_tx = PointTransaction(
        user_id=user_id,
        transaction_type="step",
        amount=conversion_points,
        description=f"Step points conversion: {conversion_points} points from {convertible_steps} steps with multiplier {multiplier}."
    )
    db.add(point_tx)

    # 사용자 포인트 업데이트
    user_points = db.query(UserPoints).filter(UserPoints.user_id == user_id).first()
    if user_points:
        user_points.total_points += conversion_points
    else:
        user_points = UserPoints(user_id=user_id, total_points=conversion_points)
        db.add(user_points)

    db.commit()

    # 추천인에게 포인트 지급 (referral chain)
    from app.services.user_service import UserService
    UserService.award_referral_chain_bonus(db, user_id, conversion_points)

    # 남은 미전환 걸음 계산 (steps remain unconverted)
    remaining_steps = conversion_info["potential_points"] - convertible_steps

    return {
        "success": True,
        "message": f"Successfully converted {conversion_points} points from steps.",
        "converted_points": conversion_points,
        "remaining_unconverted_points": remaining_steps,
        "current_total_points": user_points.total_points
    } 