from sqlalchemy.orm import Session
from sqlalchemy import func
from app.models.step_record import StepRecord
from app.schemas.step_record import StepRecordCreate
from app.services.user_service import UserService
from app.services.admin_service import get_setting_float, get_setting_int
from datetime import date, datetime, timedelta
from typing import List
from app.core.config import settings
from app.models.point_transaction import PointTransaction
from app.models.user_points import UserPoints
from app.models.user_referral import UserReferral
from app.core.time import now
from app.services import point_service
import logging
from app.services.expo_push_notification_service import ExpoPushNotificationService

# 걸음 -> 포인트 변환 함수
def convert_steps_to_points(db: Session, steps: int, country_code=None):
    base_rate = get_setting_float(db, "STEP_BASE_RATE", country_code)
    threshold = get_setting_int(db, "STEP_THRESHOLD", country_code)
    multiplier = get_setting_float(db, "STEP_MULTIPLIER", country_code)
    
    if steps <= threshold:
        return int(steps * base_rate)
    else:
        return int(threshold * base_rate + (steps - threshold) * base_rate * multiplier)

def create_step_record(db: Session, user_id: int, step_data: StepRecordCreate) -> StepRecord:
    """
    사용자의 특정 날짜에 걸음 기록을 생성하거나 업데이트합니다.
    포인트 계산은 하지만 실시간으로 적립하지 않습니다.
    """
    # 사용자의 국가 코드 조회
    from app.models.user import User
    user = db.query(User).filter(User.id == user_id).first()
    country_code = user.country_code if user else None

    # 기존 레코드 조회
    record = db.query(StepRecord).filter(
        StepRecord.user_id == user_id,
        StepRecord.record_date == step_data.record_date
    ).first()

    # 포인트 계산 (저장용)
    points_earned = convert_steps_to_points(db, step_data.steps_count, country_code)

    if record:
        # 기존 레코드 업데이트
        record.steps_count = step_data.steps_count
        record.points_earned = points_earned  # 실제 포인트는 적립하지 않고 계산값만 저장
        record.calories_burned = step_data.calories_burned
        record.distance_km = step_data.distance_km
        record.goal_achieved = step_data.goal_achieved
        record.sync_source = step_data.sync_source
    else:
        # 새로운 레코드 생성
        record = StepRecord(
            user_id=user_id,
            record_date=step_data.record_date,
            steps_count=step_data.steps_count,
            points_earned=points_earned,  # 실제 포인트는 적립하지 않고 계산값만 저장
            calories_burned=step_data.calories_burned,
            distance_km=step_data.distance_km,
            goal_achieved=step_data.goal_achieved,
            sync_source=step_data.sync_source
        )
        db.add(record)
    
    # 첫 걸음 기록에 대한 추천 보너스 로직은 유지
    total_records = db.query(StepRecord).filter(StepRecord.user_id == user_id).count()
    if total_records == 1:
        referral_bonus = get_setting_int(db, "REFERRAL_BONUS", country_code)
        UserService.award_referral_bonus(db, referred_user_id=user_id, bonus_points=referral_bonus)
    
    db.commit()
    db.refresh(record)

    # Check if accumulated steps are enough to convert to points in real-time
    conversion_result = point_service.convert_steps_to_points(db, user_id, country_code)
    if conversion_result.get("success"):
        # Optionally, log or handle the conversion result
        print(conversion_result.get("message"))

    return record

def get_current_step_record(db: Session, user_id: int):
    """
    Retrieves the step record for the given user for the current date.
    Returns None if no record exists.
    """
    record = db.query(StepRecord).filter(
        StepRecord.user_id == user_id,
        StepRecord.record_date == date.today()
    ).first()
    return record

def update_step_record_accumulation(db: Session, user_id: int, steps_increment: int) -> StepRecord:
    """
    Accumulates steps for the current day's record for the given user.
    If a record exists, the new steps are added to the existing value.
    Otherwise, a new record is created.
    포인트 계산은 하지만 실시간으로 적립하지 않습니다.
    """
    # 사용자의 국가 코드 조회
    from app.models.user import User
    user = db.query(User).filter(User.id == user_id).first()
    country_code = user.country_code if user else None
    
    record = db.query(StepRecord).filter(
        StepRecord.user_id == user_id,
        StepRecord.record_date == date.today()
    ).first()
    
    # 기존에 레코드가 있으면 스텝 업데이트, 없으면 새로 생성
    if record:
        record.steps_count += steps_increment
        
        # 포인트 재계산 (저장용)
        current_points = convert_steps_to_points(db, record.steps_count, country_code)
        record.points_earned = current_points  # 실제 포인트는 적립하지 않고 계산값만 저장
    else:
        # 새 레코드 생성
        record = StepRecord(
            user_id=user_id,
            record_date=date.today(),
            steps_count=steps_increment,
            points_earned=convert_steps_to_points(db, steps_increment, country_code),  # 계산값만 저장
            calories_burned=0.0,
            distance_km=0.0,
            goal_achieved=False,
            sync_source="real-time-update",
        )
        db.add(record)
    
    db.commit()
    db.refresh(record)
    
    # 추천 보너스 로직
    # UserReferral 테이블에서 보너스 지급여부(awarded)를 체크
    referral = db.query(UserReferral).filter(
        UserReferral.referred_user_id == user_id
    ).first()
    
    if referral and not referral.awarded:
        # 첫 걸음 기록으로 인한 bonus 지급 (referral_bonus)
        referral_bonus = get_setting_int(db, "REFERRAL_BONUS", country_code)
        UserService.award_referral_bonus(db, referred_user_id=user_id, bonus_points=referral_bonus)
    
    # Retrieve the user's daily step goal
    daily_goal = user.daily_step_goal if user and user.daily_step_goal else get_setting_int(db, "ACHIEVEMENT_STEP_GOAL", country_code)

    # Add logging to debug achievement bonus condition
    logger = logging.getLogger(__name__)
    logger.info(f"[Achievement Bonus Check] user_id={user_id}, steps_count={record.steps_count}, daily_goal={daily_goal}, goal_achieved={record.goal_achieved}")

    achievement_bonus = get_setting_int(db, "ACHIEVEMENT_BONUS", country_code)

    # Award achievement bonus only once per day using the goal_achieved flag
    if not record.goal_achieved and record.steps_count >= daily_goal:
        achievement_tx = PointTransaction(
            user_id=user_id,
            transaction_type="achievement",
            amount=achievement_bonus,
            description=f"Achievement bonus for reaching daily goal of {daily_goal} steps"
        )
        db.add(achievement_tx)

        user_points_record = db.query(UserPoints).filter(UserPoints.user_id == user_id).first()
        if user_points_record:
            user_points_record.total_points += achievement_bonus
        else:
            user_points_record = UserPoints(user_id=user_id, total_points=achievement_bonus)
            db.add(user_points_record)

        # Mark achievement as awarded so that bonus is not given again
        record.goal_achieved = True
        db.commit()

        # Log the achievement bonus transaction from DB for debugging
        achievement_entry = db.query(PointTransaction).filter_by(user_id=user_id, transaction_type="achievement").order_by(PointTransaction.created_at.desc()).first()
        logger.info(f"Achievement bonus transaction committed: {achievement_entry}")

    # Always attempt conversion based on accumulated steps
    conversion_result = point_service.convert_steps_to_points(db, user_id, country_code)
    if conversion_result.get("success"):
        print(conversion_result.get("message"))

    # 여기서부터 알림 관련 로직 추가
    check_and_send_step_notification(db, user_id, record)
    
    return record

def get_all_step_records(db: Session, user_id: int) -> List[StepRecord]:
    """
    Retrieves all step records for the given user across all dates.
    Records are ordered by record_date in descending order.
    """
    records = db.query(StepRecord).filter(
        StepRecord.user_id == user_id
    ).order_by(StepRecord.record_date.desc()).all()
    return records

def check_and_send_step_notification(db: Session, user_id: int, step_record: StepRecord):
    """
    사용자의 현재 걸음수를 확인하고, 목표에 미달하는 경우 설정된 빈도에 따라 알림을 보냅니다.
    """
    # 추가: User 모델 import
    from app.models.user import User

    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        return
    
    # 오늘 날짜 확인
    today = date.today()
    
    # 사용자가 설정한 하루 최대 알림 횟수 체크
    max_notifications = user.step_notification_frequency
    
    # 오늘 보낸 알림 수 체크 (자정 기준으로 리셋)
    if user.last_step_notification_time is None or user.last_step_notification_time.date() < today:
        # 날짜가 바뀌었으면 카운터 리셋
        user.step_notifications_sent_today = 0
    
    # 오늘 이미 최대 알림을 보냈으면 더 이상 보내지 않음
    if user.step_notifications_sent_today >= max_notifications:
        return
    
    # 현재 걸음수가 목표의 몇 퍼센트인지 계산
    current_steps = step_record.steps_count
    goal_steps = user.daily_step_goal
    step_percentage = (current_steps / goal_steps) * 100
    
    # 마지막 알림 시간으로부터 최소 3시간 이상 지났는지 체크
    min_hours_between_notifications = 3
    can_send_notification = (
        user.last_step_notification_time is None or 
        datetime.now() - user.last_step_notification_time > timedelta(hours=min_hours_between_notifications)
    )
    
    # 목표의 80% 미만이고, 알림을 보낼 수 있는 상태면 알림 전송
    if step_percentage < 80 and can_send_notification:
        remaining_steps = goal_steps - current_steps
        percentage_text = f"{int(step_percentage)}%"
        
        title = "Adım Hedefi Bildirimi"
        message = f"Bugün hedef adım sayısının {percentage_text}'ini yalnızca başardınız. Hedefe ulaşmak için {remaining_steps} adım daha atmanız gerekiyor!"
        
        # Expo Push 알림 전송
        ExpoPushNotificationService.send_push_to_user(
            db=db,
            user_id=user_id,
            title=title,
            message=message,
            notification_type="step_reminder",
            data={"current_steps": current_steps, "goal_steps": goal_steps}
        )
        
        # 알림 전송 후 사용자 정보 업데이트
        user.last_step_notification_time = datetime.now()
        user.step_notifications_sent_today += 1
        db.commit()