# src/routes/payments_v2.py
from flask import Blueprint, request, jsonify, session, current_app
from flask_login import login_required, current_user
import os
import uuid
import requests
import base64
import hmac
import hashlib
from datetime import datetime, timedelta, timezone, date
from calendar import monthrange
from src.models.subscription import db, Subscription, PaymentMethod, PaymentStatus
from src.config.env_loader import env
from src.utils.user_ref import parse_mlink_order_user_id, resolve_user_ref

payments_v2_bp = Blueprint('payments_v2', __name__)

def _add_one_month_same_day(base_date: date, start_day: int) -> date:
    """base_date 기준으로 +1개월, 시작일의 '일(day)' 유지. 말일 보정."""
    # 다음 달 임시 날짜
    if base_date.month == 12:
        y, m = base_date.year + 1, 1
    else:
        y, m = base_date.year, base_date.month + 1
    
    # 다음 달 말일
    last_day = monthrange(y, m)[1]
    keep_day = min(start_day, last_day)
    return date(y, m, keep_day)

def _calculate_subscription_credit_fields(start_date: datetime, billing_cycle: str):
    """구독 생성 시 크레딧 관련 필드 계산
    Returns:
        tuple: (prepaid_cycles_remaining, next_credit_date, last_credit_date)
    """
    # prepaid_cycles_remaining: 연간 선결제면 12, 월간이면 0
    prepaid_cycles_remaining = 12 if billing_cycle == 'yearly' else 0
    
    # next_credit_date: 시작일 기준으로 다음 달 같은 날짜 계산
    start_date_kst = start_date.replace(tzinfo=timezone(timedelta(hours=9))) if start_date.tzinfo is None else start_date
    start_date_local = start_date_kst.astimezone(timezone(timedelta(hours=9)))
    start_day = start_date_local.day
    start_date_only = start_date_local.date()
    next_credit_date = _add_one_month_same_day(start_date_only, start_day)
    
    # last_credit_date: 구독 생성 시점에는 NULL (아직 지급되지 않음)
    last_credit_date = None
    
    return prepaid_cycles_remaining, next_credit_date, last_credit_date

# 환경에 따라 URL 동적 설정
def get_front_url():
    """프론트엔드 URL 반환 (환경별)"""
    front_url = os.environ.get('FRONT_URL')
    if front_url:
        return front_url
    # 환경 변수가 없으면 환경에 따라 기본값 설정
    if env.is_production:
        return 'https://mlink.sellmall.co.kr'
    return 'http://localhost:3001'

def get_back_url():
    """백엔드 URL 반환 (환경별)"""
    back_url = os.environ.get('BACK_URL')
    if back_url:
        return back_url
    # 환경 변수가 없으면 환경에 따라 기본값 설정
    if env.is_production:
        return 'https://mlink.sellmall.co.kr'
    return 'http://localhost:8011'

FRONT_URL = get_front_url()
BACK_URL = get_back_url()

# 로깅으로 URL 확인
import logging
logger = logging.getLogger(__name__)
logger.info(f"환경 설정: ENVIRONMENT={os.environ.get('ENVIRONMENT', 'development')}, FRONT_URL={FRONT_URL}, BACK_URL={BACK_URL}")

# 토스페이먼츠 설정
TOSS_CONFIG = {
    'api_key': os.environ.get('TOSS_API_KEY'),  # 제공받은 테스트 API 키
    'secret_key': os.environ.get('TOSS_SECRET_KEY'),  # 서명 검증용 시크릿 키
    'base_url': 'https://pay.toss.im/api/v2/payments',
    'refund_url': 'https://pay.toss.im/api/v2/refunds',
    'status_url': 'https://pay.toss.im/api/v2/status',
    'execute_url': 'https://pay.toss.im/api/v2/execute'
}

def verify_toss_signature(payload, signature, secret_key):
    """토스페이먼츠 서명 검증"""
    try:
        if not secret_key:
            current_app.logger.warning('TOSS_SECRET_KEY가 설정되지 않음 - 서명 검증 건너뜀')
            return True  # 개발 환경에서는 검증 건너뜀
        
        # 토스페이먼츠 서명 검증 로직
        # 실제 토스페이먼츠 문서에 따라 구현 필요
        expected_signature = hmac.new(
            secret_key.encode('utf-8'),
            payload.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()
        
        # 서명 비교 (타이밍 공격 방지를 위해 hmac.compare_digest 사용)
        is_valid = hmac.compare_digest(signature, expected_signature)
        
        if not is_valid:
            current_app.logger.error(f'토스페이먼츠 서명 검증 실패: expected={expected_signature}, received={signature}')
        
        return is_valid
        
    except Exception as e:
        current_app.logger.error(f'토스페이먼츠 서명 검증 오류: {str(e)}')
        return False

def fetch_toss_status(pay_token=None, order_no=None):
    """토스페이먼츠 결제 상태 조회 공통 함수"""
    try:
        if not pay_token and not order_no:
            return None, "payToken 또는 orderNo가 필요합니다"
        
        status_response = requests.post(
            TOSS_CONFIG['status_url'],
            headers={'Content-Type': 'application/json'},
            json={
                'apiKey': TOSS_CONFIG['api_key'],
                'payToken': pay_token,
                'orderNo': order_no
            },
            timeout=10
        )
        
        if status_response.status_code == 200:
            status_data = status_response.json()
            current_app.logger.info(f'토스페이먼츠 상태 조회 성공: {status_data}')
            return status_data, None
        else:
            error_msg = f'토스페이먼츠 상태 조회 실패: {status_response.status_code} - {status_response.text}'
            current_app.logger.error(error_msg)
            return None, error_msg
            
    except Exception as e:
        error_msg = f'토스페이먼츠 상태 조회 중 오류: {str(e)}'
        current_app.logger.error(error_msg)
        return None, error_msg

def process_toss_payment_success(pay_token, order_id, amount=None):
    """토스페이먼츠 결제 성공 처리 공통 함수"""
    try:
        # 토스페이먼츠 상태 조회
        status_data, error = fetch_toss_status(pay_token=pay_token)
        if error:
            return False, error
        
        # 결제 상태 확인
        pay_status = status_data.get('payStatus') or status_data.get('status')
        paid_amount = int(status_data.get('amount') or status_data.get('payAmount') or 0)
        
        current_app.logger.info(f'토스페이먼츠 상태: payStatus={pay_status}, amount={paid_amount}')
        
        if pay_status == 'PAY_COMPLETE':
            # 실제 결제 금액 사용 (API에서 받은 값)
            actual_amount = paid_amount if paid_amount > 0 else amount
            
            # 구독 생성 처리
            payment_result = {
                'paymentKey': pay_token,
                'orderId': order_id,
                'amount': actual_amount,
                'status': 'DONE'
            }
            
            success = create_toss_subscription(payment_result)
            if success:
                current_app.logger.info(f'토스페이먼츠 결제 성공 처리 완료: {order_id}')
                return True, None
            else:
                return False, "구독 생성 실패"
        else:
            current_app.logger.warning(f'토스페이먼츠 상태가 PAY_COMPLETE가 아님: {pay_status}')
            return False, f"결제 미완료: {pay_status}"
            
    except Exception as e:
        error_msg = f'토스페이먼츠 결제 성공 처리 중 오류: {str(e)}'
        current_app.logger.error(error_msg)
        return False, error_msg

def calculate_end_date(billing_cycle):
    """결제 주기에 따른 종료일 계산 (오늘 제외)"""
    now = datetime.now()
    
    if billing_cycle == 'monthly':
        return now + timedelta(days=29)  # 30일 (오늘 제외)
    elif billing_cycle == 'yearly':
        return now + timedelta(days=364)  # 365일 (오늘 제외)
    else:
        return now + timedelta(days=29)  # 기본값

# =========================
# 토스페이먼츠 결제 API
# =========================
@payments_v2_bp.route('/api/v2/payments/toss/prepare', methods=['POST'])
# @login_required  # 임시로 주석 처리 (테스트용)
def prepare_toss_payment():
    """토스페이먼츠 결제 준비 - 결제 키 생성"""
    try:
        user_id = session.get('user_id')
        if not user_id:
            return jsonify({'success': False, 'error': '로그인이 필요합니다.'}), 401

        data = request.get_json()
        product_id = data.get('product_id')
        product_name = data.get('product_name')
        amount = data.get('amount')
        billing_cycle = data.get('billing_cycle')
        # customer_info = data.get('customer_info', {})

        if not all([product_id, product_name, amount, billing_cycle]):
            return jsonify({'error': '필수 정보가 누락되었습니다.'}), 400

        # 금액 검증 (정수 KRW)
        if not isinstance(amount, int) or amount <= 0:
            return jsonify({'error': '유효하지 않은 금액입니다.'}), 400

        # 고유 주문 ID 생성 (금액 정보 포함)
        # user_id = getattr(current_user, 'id', 'test_user')
        order_id = f"mlink_{user_id}_{uuid.uuid4().hex[:8]}_{amount}_{billing_cycle}_{int(datetime.now().timestamp())}"
        
        # 토스페이먼츠 결제 요청 데이터 구성
        headers = {
            'Content-Type': 'application/json'
        }
        
        # 세금 계산 (부가세 10% 기준)
        amount_taxable = int(amount / 1.1)  # 과세금액 (부가세 제외)
        amount_vat = amount - amount_taxable  # 부가세
        
        payment_data = {
            'orderNo': order_id,
            'amount': amount,
            'amountTaxFree': 0,  # 비과세금액 (0으로 설정)
            'amountTaxable': amount_taxable,  # 과세금액 (부가세 제외)
            'amountVat': amount_vat,  # 부가세
            'productDesc': product_name,
            'apiKey': TOSS_CONFIG['api_key'],
            'autoExecute': True,  # 자동 승인 + 콜백/상태 제공
            'resultCallback': 'https://pay.toss.im/payfront/demo/callback',  # 토스페이먼츠 데모 콜백 사용
            'callbackVersion': 'V2',
            'retUrl': f'{BACK_URL}/api/v2/payments/toss/success?orderno={order_id}&status=PAY_COMPLETE',
            'retCancelUrl': f'{FRONT_URL}/payment/fail?code=PAY_PROCESS_CANCELED&message=결제가 취소되었습니다&orderId={order_id}'
        }

        # 토스페이먼츠 API 호출
        try:
            response = requests.post(
                TOSS_CONFIG['base_url'],
                headers=headers,
                json=payment_data,
                timeout=10
            )
            
            current_app.logger.info(f'토스페이먼츠 API 응답: {response.status_code} - {response.text}')

            if response.status_code == 200:
                result = response.json()
                pay_token = result.get('payToken')
                current_app.logger.info(f'토스페이먼츠 결제 준비 완료: {order_id} - payToken: {pay_token}')
                current_app.logger.info(f'세금 정보 - 총액: {amount}원, 과세: {amount_taxable}원, 부가세: {amount_vat}원')
                
                # 임시 payment_history 생성 (결제 대기 상태)
                try:
                    from src.models.payment import PaymentHistory
                    from src.models.subscription import SubscriptionStatus
                    
                    # 사용자 ID 추출 (업무 user_id 문자열)
                    user_id = '1'
                    if order_id:
                        parsed = parse_mlink_order_user_id(order_id)
                        if parsed:
                            user_id = parsed
                    
                    # 임시 payment_history 생성 (상품 정보 포함)
                    temp_payment_history = PaymentHistory(
                        user_id=user_id,
                        subscription_id=None,  # 아직 구독이 생성되지 않음
                        payment_key=pay_token,  # 실제 payToken 저장
                        order_id=order_id,
                        amount=amount,
                        status=PaymentStatus.PENDING,  # 결제 대기 상태
                        payment_method='toss_payments',
                        product_id=product_id,  # 상품 ID 저장
                        product_name=product_name,  # 상품명 저장
                        product_price=amount,  # 상품 가격 저장
                        billing_cycle=billing_cycle,  # 결제 주기 저장
                        created_at=datetime.now()
                    )
                    db.session.add(temp_payment_history)
                    db.session.commit()
                    current_app.logger.info(f'임시 payment_history 생성 완료: {temp_payment_history.id} - payToken: {pay_token}')
                    
                except Exception as e:
                    current_app.logger.error(f'임시 payment_history 생성 실패: {str(e)}')
                
                # 토스페이먼츠 API 응답에서 필요한 정보 추출
                return jsonify({
                    'paymentKey': pay_token,
                    'orderId': order_id,
                    'amount': amount,
                    'orderName': product_name,
                    'payUrl': result.get('checkoutPage', ''),
                    'simulation': False
                })
            else:
                # API 오류인 경우 시뮬레이션 모드로 동작
                current_app.logger.warning(f'토스페이먼츠 API 오류 - 시뮬레이션 모드로 동작: {response.status_code} - {response.text}')
                return jsonify({
                    'paymentKey': f'sim_payment_key_{order_id}',
                    'orderId': order_id,
                    'amount': amount,
                    'orderName': product_name,
                    'payUrl': '',
                    'simulation': True
                })
                    
        except requests.exceptions.RequestException as e:
            current_app.logger.warning(f'토스페이먼츠 API 호출 실패 - 시뮬레이션 모드로 동작: {str(e)}')
            return jsonify({
                'paymentKey': f'sim_payment_key_{order_id}',
                'orderId': order_id,
                'amount': amount,
                'orderName': product_name,
                'simulation': True
            })

    except Exception as e:
        current_app.logger.error(f'토스페이먼츠 결제 준비 실패: {str(e)}')
        return jsonify({'error': '결제 준비 중 오류가 발생했습니다.'}), 500

@payments_v2_bp.route('/api/v2/payments/toss/callback', methods=['POST'])
def toss_payment_callback():
    """토스페이먼츠 결제 완료 콜백 처리"""
    try:
        # 서명 검증
        signature = request.headers.get('X-Toss-Signature') or request.headers.get('X-Signature')
        if signature:
            # 원본 요청 본문 가져오기
            payload = request.get_data(as_text=True)
            if not verify_toss_signature(payload, signature, TOSS_CONFIG['secret_key']):
                current_app.logger.error('토스페이먼츠 콜백 서명 검증 실패')
                return jsonify({'error': 'Invalid signature'}), 401
            current_app.logger.info('토스페이먼츠 콜백 서명 검증 성공')
        else:
            current_app.logger.warning('토스페이먼츠 콜백 서명이 없음 - 검증 건너뜀')
        
        data = request.get_json()
        current_app.logger.info(f'토스페이먼츠 콜백 수신: {data}')
        
        # 콜백 데이터에서 결제 정보 추출
        order_id = data.get('orderNo')
        status = data.get('status')
        pay_token = data.get('payToken')
        amount = data.get('amount')
        
        if status == 'PAY_COMPLETE' and order_id:
            # 공통 함수를 사용한 결제 성공 처리
            success, error = process_toss_payment_success(pay_token, order_id, amount)
            if success:
                current_app.logger.info(f'토스페이먼츠 콜백 처리 완료: {order_id}')
                return jsonify({'success': True, 'message': '결제 완료 처리됨'})
            else:
                current_app.logger.error(f'토스페이먼츠 콜백 처리 실패: {error}')
                # 콜백에서는 실패해도 구독 생성 시도 (fallback)
                try:
                    create_toss_subscription({
                        'paymentKey': pay_token,
                        'orderId': order_id,
                        'amount': amount,
                        'status': 'DONE'
                    })
                    return jsonify({'success': True, 'message': '결제 완료 처리됨 (fallback)'})
                except Exception as fallback_error:
                    current_app.logger.error(f'콜백 fallback 처리 실패: {str(fallback_error)}')
                    return jsonify({'success': False, 'error': error}), 500
        else:
            current_app.logger.warning(f'결제 미완료 또는 오류: {data}')
            return jsonify({'success': False, 'message': '결제 미완료'})
            
    except Exception as e:
        current_app.logger.error(f'토스페이먼츠 콜백 처리 실패: {str(e)}')
        return jsonify({'error': '콜백 처리 중 오류가 발생했습니다.'}), 500

@payments_v2_bp.route('/api/v2/payments/toss/webhook', methods=['POST'])
def toss_payment_webhook():
    """토스페이먼츠 웹훅 처리 (서명 검증 포함)"""
    try:
        # 서명 검증 (웹훅은 반드시 서명 검증 필요)
        signature = request.headers.get('X-Toss-Signature') or request.headers.get('X-Signature')
        if not signature:
            current_app.logger.error('토스페이먼츠 웹훅 서명이 없음')
            return jsonify({'error': 'Missing signature'}), 401
        
        # 원본 요청 본문 가져오기
        payload = request.get_data(as_text=True)
        if not verify_toss_signature(payload, signature, TOSS_CONFIG['secret_key']):
            current_app.logger.error('토스페이먼츠 웹훅 서명 검증 실패')
            return jsonify({'error': 'Invalid signature'}), 401
        
        current_app.logger.info('토스페이먼츠 웹훅 서명 검증 성공')
        
        data = request.get_json()
        current_app.logger.info(f'토스페이먼츠 웹훅 수신: {data}')
        
        # 웹훅 데이터에서 결제 정보 추출
        order_id = data.get('orderNo')
        status = data.get('status')
        pay_token = data.get('payToken')
        amount = data.get('amount')
        
        if status == 'PAY_COMPLETE' and order_id:
            # 공통 함수를 사용한 결제 성공 처리
            success, error = process_toss_payment_success(pay_token, order_id, amount)
            if success:
                current_app.logger.info(f'토스페이먼츠 웹훅 처리 완료: {order_id}')
                return jsonify({'status': 'success'}), 200
            else:
                current_app.logger.error(f'토스페이먼츠 웹훅 처리 실패: {error}')
                return jsonify({'error': error}), 500
        else:
            current_app.logger.info(f'토스페이먼츠 웹훅 무시: status={status}, order_id={order_id}')
            return jsonify({'status': 'ignored'}), 200
            
    except Exception as e:
        current_app.logger.error(f'토스페이먼츠 웹훅 처리 중 오류: {str(e)}')
        return jsonify({'error': 'Webhook processing error'}), 500

@payments_v2_bp.route('/api/v2/payments/toss/create-subscription', methods=['POST'])
def create_toss_subscription_api():
    """토스페이먼츠 결제 완료 후 구독 생성 API"""
    try:
        data = request.get_json()
        pay_token = data.get('payToken')
        order_id = data.get('orderId')
        
        current_app.logger.info(f'토스페이먼츠 구독 생성 요청: payToken={pay_token}, orderId={order_id}')
        
        if not pay_token or not order_id:
            return jsonify({'error': 'payToken과 orderId가 필요합니다.'}), 400
        
        # 공통 함수를 사용한 결제 성공 처리
        success, error = process_toss_payment_success(pay_token, order_id)
        if success:
            return jsonify({
                'success': True,
                'message': '구독이 성공적으로 생성되었습니다.',
                'payToken': pay_token,
                'orderId': order_id
            })
        else:
            current_app.logger.error(f'구독 생성 실패: {error}')
            return jsonify({
                'success': False,
                'error': error
            }), 400
        
    except Exception as e:
        current_app.logger.error(f'토스페이먼츠 구독 생성 실패: {str(e)}')
        return jsonify({'error': '구독 생성 중 오류가 발생했습니다.'}), 500


@payments_v2_bp.route('/api/v2/payments/toss/success', methods=['GET'])
def toss_payment_success():
    """토스페이먼츠 결제 성공 페이지 - payToken 조회 후 구독 생성"""
    try:
        user_id = session.get('user_id')
        if not user_id:
            return jsonify({'success': False, 'error': '로그인이 필요합니다.'}), 401

        order_id = request.args.get('orderno')
        pay_token = request.args.get('payToken')
        status = request.args.get('status')
        
        current_app.logger.info(f'토스페이먼츠 결제 성공 페이지 접근: order_id={order_id}, payToken={pay_token}, status={status}')

        # ✅ 0) 정산 결제 여부 식별
        # 예: mlink_usage_<userId>_<rand>_<amount>_onetime_<ts>
        is_settlement = bool(order_id) and (order_id.startswith('mlink_usage_') or '_usage_' in order_id)

        # payToken이 없는 경우 payment_histories에서 조회
        if not pay_token:
            try:
                from src.models.payment import PaymentHistory
                payment_history = PaymentHistory.query.filter_by(
                    order_id=order_id,
                    status=PaymentStatus.PENDING,
                    user_id=user_id
                ).first()
                
                if payment_history and payment_history.payment_key:
                    pay_token = payment_history.payment_key
                    current_app.logger.info(f'payment_histories에서 payToken 조회: {pay_token}')
                else:
                    current_app.logger.error(f'payToken을 찾을 수 없음 - order_id: {order_id}')
                    return f"""
                    <!DOCTYPE html>
                    <html>
                    <head>
                        <title>결제 오류</title>
                    </head>
                    <body>
                        <h1>결제 정보를 찾을 수 없습니다.</h1>
                        <p>고객센터에 문의해주세요.</p>
                    </body>
                    </html>
                    """, 400
                    
            except Exception as e:
                current_app.logger.error(f'payToken 조회 실패: {str(e)}')
                return f"""
                <!DOCTYPE html>
                <html>
                <head>
                    <title>결제 오류</title>
                </head>
                <body>
                    <h1>결제 처리 중 오류가 발생했습니다.</h1>
                    <p>고객센터에 문의해주세요.</p>
                </body>
                </html>
                """, 500

        # ✅ 1) 상태 조회에서 실제 금액 가져오기
        status_data, _ = fetch_toss_status(pay_token=pay_token)
        paid_amount = int(status_data.get('amount') or status_data.get('payAmount') or 0) if status_data else 0      

        # ✅ 3) 정산 결제면: 구독 생성 로직 우회, PH 완료 처리 후 사가 진행
        if is_settlement:
            try:
                from src.models.payment import PaymentHistory, PaymentStatus as PHStatus
                ph = PaymentHistory.query.filter_by(order_id=order_id, status=PHStatus.PENDING).first()
                if ph:
                    ph.status = PHStatus.COMPLETED
                    ph.updated_at = datetime.now()
                    db.session.commit()
                    current_app.logger.info(f'[Settlement] PENDING → COMPLETED 갱신: ph_id={ph.id}, amount={paid_amount}')
                else:
                    current_app.logger.warning(f'[Settlement] PENDING PH 미발견: order_id={order_id}')

                # 🔁 사가: 정산 완료 후 전체 환불/취소 이어가기 (실제 구현 함수명으로 교체)
                try:
                    from src.services.cancel_orchestrator import finish_settlement_then_refund
                    finish_settlement_then_refund(order_id=order_id, pay_token=pay_token, amount=paid_amount)
                    current_app.logger.info('[Settlement] 사가 트리거 완료: finish_settlement_then_refund')
                except Exception as saga_err:
                    current_app.logger.error(f'[Settlement] 사가 트리거 실패: {saga_err}')

            except Exception as e:
                current_app.logger.error(f'[Settlement] 처리 실패: {str(e)}')
                # 실패해도 사용자에게는 결제 완료 알림은 전달

            # 프론트에 정산 결제 성공 시그널 전송
            return f"""
            <!DOCTYPE html>
            <html>
            <head><title>정산 결제 완료</title></head>
            <body>
                <h1>정산 결제가 완료되었습니다.</h1>
                <p>결제 금액: ₩{paid_amount:,}원</p>
                <script>
                    if (window.opener) {{
                        window.opener.postMessage({{
                            type: 'SETTLEMENT_SUCCESS',
                            orderId: '{order_id}',
                            payToken: '{pay_token}',
                            amount: {paid_amount}
                        }}, '*');
                        window.close();
                    }} else {{
                        window.location.href = '{FRONT_URL}/mypage';
                    }}
                </script>
            </body>
            </html>
            """          
        
        # 4) 일반 구독 결제면: 기존 로직(구독 생성) 실행
        success, error = process_toss_payment_success(pay_token, order_id)
        if success:
            # 🔵 NEW: carryover days 조회 (PENDING PH 기준)
            carryover_days = 0
            try:
                from src.models.payment import PaymentHistory, PaymentStatus as PHStatus
                temp_ph = PaymentHistory.query.filter_by(order_id=order_id, status=PHStatus.PENDING).first()
                if temp_ph and hasattr(temp_ph, 'carryover_credit_days') and temp_ph.carryover_credit_days:
                    carryover_days = int(temp_ph.carryover_credit_days or 0)
            except Exception as e:
                current_app.logger.error(f'carryover days lookup failed: {str(e)}')
                carryover_days = 0

            return f"""
            <!DOCTYPE html>
            <html>
            <head>
                <title>결제 완료</title>
            </head>
            <body>
                <h1>결제가 완료되었습니다.</h1>
                <p>결제 금액: ₩{paid_amount:,}원</p>
                <script>
                    if (window.opener) {{
                        window.opener.postMessage({{
                            type: 'PAYMENT_SUCCESS',
                            orderId: '{order_id}',
                            payToken: '{pay_token}',
                            amount: {paid_amount},
                            creditDays: {carryover_days}
                        }}, '*');
                        window.close();
                    }} else {{
                        window.location.href = '{FRONT_URL}/mypage';
                    }}
                </script>
            </body>
            </html>
            """
        else:
            current_app.logger.error(f'결제 성공 처리 실패: {error}')
            return f"""
            <!DOCTYPE html>
            <html>
            <head>
                <title>결제 오류</title>
            </head>
            <body>
                <h1>결제 처리 중 오류가 발생했습니다.</h1>
                <p>오류: {error}</p>
                <p>고객센터에 문의해주세요.</p>
            </body>
            </html>
            """, 500
        
    except Exception as e:
        current_app.logger.error(f'토스페이먼츠 결제 성공 페이지 처리 실패: {str(e)}')
        return f"""
        <!DOCTYPE html>
        <html>
        <head>
            <title>결제 오류</title>
        </head>
        <body>
            <h1>결제 처리 중 오류가 발생했습니다.</h1>
            <p>고객센터에 문의해주세요.</p>
        </body>
        </html>
        """, 500

def create_toss_subscription(payment_result):
    """토스페이먼츠 결제 성공 시 구독 생성/연장"""
    try:
        # 지역 import로 의존성 명확화
        from src.models.payment import PaymentHistory, PaymentStatus as PHStatus
        from src.models.subscription import SubscriptionStatus
        # db, Subscription, PaymentMethod 은 파일 상단 import 가정

        current_app.logger.info(f'토스페이먼츠 구독 생성 시작: {payment_result}')

        # 1) 결제 파라미터 파싱
        payment_key = payment_result.get('paymentKey')
        order_id    = payment_result.get('orderId')
        amount      = payment_result.get('amount')
        status      = payment_result.get('status', 'DONE')

        # 3) PENDING PaymentHistory 에서 필요한 컬럼만 안전 조회
        #    (carryover 칼럼이 DB에 없더라도 select 리스트에 안 올려 에러 방지)
        ph_row = (db.session.query(
                    PaymentHistory.product_id,
                    PaymentHistory.product_name,
                    PaymentHistory.product_price,
                    PaymentHistory.billing_cycle,
                    PaymentHistory.carryover_credit_amount,
                    PaymentHistory.carryover_credit_days,
                    PaymentHistory.user_id
                  )
                  .filter(PaymentHistory.order_id == order_id,
                          PaymentHistory.status == PHStatus.PENDING)
                  .first())

        # carryover 일수는 별도 시도(마이그레이션 전/후 모두 안전)
        carryover_days = 0
        try:
            carry_row = (db.session.query(PaymentHistory.carryover_credit_days)
                         .filter(PaymentHistory.order_id == order_id,
                                 PaymentHistory.status == PHStatus.PENDING)
                         .first())
            if carry_row and carry_row[0]:
                carryover_days = int(carry_row[0] or 0)
        except Exception as e:
            # 칼럼이 없거나 기타 오류면 0으로 처리
            current_app.logger.warning(f'carryover days 조회 스킵: {str(e)}')
            carryover_days = 0

        if ph_row:
            product_id, product_name, product_price, ph_cycle, carryover_credit_amount, carryover_credit_days, user_id = ph_row
            current_app.logger.info(
                f'PENDING PH 추출: product_id={product_id}, name={product_name}, price={product_price}, cycle={ph_cycle}, carryover_days={carryover_credit_days}'
            )
        else:
            # PENDING PH 없으면 최소 정보 fallback
            product_id   = 77
            product_name = 'Basic'
            product_price = amount
            ph_cycle = None
            carryover_credit_days = 0
            carryover_credit_amount = 0
            user_id = '1'
            current_app.logger.warning('PENDING PaymentHistory 없음 — 기본값 사용')

        # 4) billing_cycle 결정 (PH 우선, 없으면 order_id 파싱, 최종 monthly)
        if ph_cycle:
            billing_cycle = ph_cycle
        else:
            billing_cycle = 'monthly'
            if order_id and 'mlink_' in order_id:
                try:
                    parts = order_id.split('_')
                    if len(parts) >= 3:
                        maybe_cycle = parts[-3+1]  # [-3]=amount, [-2]=cycle, [-1]=ts
                        if maybe_cycle in ('monthly', 'yearly'):
                            billing_cycle = maybe_cycle
                except Exception as e:
                    current_app.logger.warning(f'주문 ID에서 billing_cycle 추출 실패: {str(e)}')

        # 5) 기존 활성 구독 조회
        existing = Subscription.query.filter_by(
            user_id=user_id,
            status=SubscriptionStatus.ACTIVE
        ).first()

        # 구독 기간 계산 유틸
        def _add_cycle_days(base_dt, cycle):
            days = 30 if cycle == 'monthly' else 365
            return base_dt + timedelta(days=days)

        # 6) 구독 생성/연장
        if existing:
            current_app.logger.info(f'기존 활성 구독 처리: sub_id={existing.id}')
            
            if carryover_days > 0:
                # 플랜 변경: 기존 구독을 오늘로 종료하고 새로운 플랜을 오늘부터 시작
                current_app.logger.info(f'플랜 변경 감지: carryover_days={carryover_days}')
                today = datetime.now()
                
                # 기존 구독을 오늘로 종료
                existing.end_date = today
                existing.updated_at = today
                
                # 새로운 플랜 시작일은 오늘
                new_start_date = today
                new_end_date = _add_cycle_days(today - timedelta(days=1), billing_cycle)  # 오늘 포함 30/365일
                
                # carryover days 추가
                new_end_date = new_end_date + timedelta(days=carryover_days)
                
                current_app.logger.info(f'플랜 변경: 기존 구독 종료일={existing.end_date}, 새 플랜 시작일={new_start_date}, 새 플랜 종료일={new_end_date}')
            else:
                # 기간 연장형: 기존 종료일 기준으로 연장
                current_app.logger.info(f'기간 연장: sub_id={existing.id}')
                current_end = existing.end_date
                # 연장 시작/종료
                new_start_date = current_end + timedelta(days=1)
                new_end_date = _add_cycle_days(current_end, billing_cycle)

            # 구독 레코드 갱신
            if carryover_days > 0:
                # 플랜 변경: 기존 구독은 이미 오늘로 종료됨, 새로운 플랜 정보로 업데이트
                existing.product_id = product_id
                existing.payment_method = PaymentMethod.TOSS_PAYMENTS
                existing.payment_id = payment_key
                existing.amount = float(amount)
                existing.billing_cycle = str(billing_cycle)
                existing.start_date = new_start_date
                existing.end_date = new_end_date
                existing.next_billing_date = new_end_date
                existing.updated_at = datetime.now()
                # 플랜 변경 시 크레딧 관련 필드 재계산
                prepaid_cycles_remaining, next_credit_date, last_credit_date = _calculate_subscription_credit_fields(
                    new_start_date, billing_cycle
                )
                existing.prepaid_cycles_remaining = prepaid_cycles_remaining
                existing.next_credit_date = next_credit_date
                existing.last_credit_date = last_credit_date
                subscription = existing
            else:
                # 기간 연장: 기존 구독 정보 유지하고 종료일만 연장
                # 크레딧 관련 필드는 유지 (배치 작업에서 관리)
                existing.product_id = product_id
                existing.end_date = new_end_date
                existing.next_billing_date = new_end_date
                existing.updated_at = datetime.now()
                subscription = existing

            # PaymentHistory(완료 건) 추가
            subscription_days = 30 if billing_cycle == 'monthly' else 365
            ph = PaymentHistory(
                user_id=user_id,
                subscription_id=subscription.id,
                payment_key=payment_key,
                order_id=order_id,
                amount=amount,
                status=PHStatus.COMPLETED,
                payment_method='toss_payments',
                product_id=product_id,
                product_name=product_name,
                product_price=product_price,
                billing_cycle=billing_cycle,
                subscription_days=subscription_days + carryover_days,
                subscription_start_date=new_start_date,
                subscription_end_date=new_end_date,
                carryover_credit_days=carryover_credit_days,
                carryover_credit_amount=carryover_credit_amount,
                created_at=datetime.now()
            )
            db.session.add(ph)

        else:
            # 첫 결제: 새 구독 생성
            start_date = datetime.now()
            end_date   = _add_cycle_days(start_date - timedelta(days=1), billing_cycle)  # 오늘 제외 30/365일을 맞추려면 -1 후 +days
            # carryover 적용
            if carryover_days > 0:
                end_date = end_date + timedelta(days=carryover_days)

            # 크레딧 관련 필드 계산
            prepaid_cycles_remaining, next_credit_date, last_credit_date = _calculate_subscription_credit_fields(
                start_date, billing_cycle
            )

            subscription = Subscription(
                user_id=user_id,
                product_id=product_id,
                status=SubscriptionStatus.ACTIVE,
                payment_method=PaymentMethod.TOSS_PAYMENTS,
                payment_id=payment_key,
                amount=float(amount),
                billing_cycle=str(billing_cycle),
                start_date=start_date,
                end_date=end_date,
                next_billing_date=end_date,
                prepaid_cycles_remaining=prepaid_cycles_remaining,
                next_credit_date=next_credit_date,
                last_credit_date=last_credit_date,
                created_at=datetime.now(),
                updated_at=datetime.now()
            )
            db.session.add(subscription)

            # 첫 결제 PH 기록
            subscription_days = 30 if billing_cycle == 'monthly' else 365
            ph = PaymentHistory(
                user_id=user_id,
                subscription_id=None,  # 아래 flush 이후 갱신
                payment_key=payment_key,
                order_id=order_id,
                amount=amount,
                status=PHStatus.COMPLETED,
                payment_method='toss_payments',
                product_id=product_id,
                product_name=product_name,
                product_price=product_price,
                billing_cycle=billing_cycle,
                subscription_days=subscription_days + carryover_days,
                subscription_start_date=start_date,
                subscription_end_date=end_date,
                carryover_credit_days=carryover_credit_days,
                carryover_credit_amount=carryover_credit_amount,
                created_at=datetime.now()
            )
            db.session.add(ph)
            db.session.flush()  # subscription.id 확보
            ph.subscription_id = subscription.id

            # PRO 권한 부여 (실패해도 진행)
            try:
                from src.models.user import User
                u = resolve_user_ref(user_id)
                if u:
                    u.role = 'role_pro'
            except Exception as e:
                current_app.logger.warning(f'역할 업데이트 실패(무시): {str(e)}')

        # 7) 커밋
        db.session.commit()
        
        # 8) 크레딧 기준 구독자인 경우 구독 크레딧 추가 (구독 생성/연장 시)
        try:
            from src.models.user import User, SubscriptionType
            from src.utils.credit_manager import renew_subscription_credits

            user = resolve_user_ref(subscription.user_id)
            if user and user.subscription_type == SubscriptionType.CREDIT:
                credit_result = renew_subscription_credits(subscription.user_id, subscription.id)
                if credit_result['success']:
                    current_app.logger.info(f'구독 크레딧 추가 완료: user_id={subscription.user_id}, added={credit_result["added"]}')
                else:
                    current_app.logger.warning(f'구독 크레딧 추가 실패: {credit_result.get("error")}')
        except Exception as e:
            current_app.logger.error(f'구독 크레딧 추가 중 오류 (무시): {str(e)}')
            # 크레딧 추가 실패해도 구독은 성공한 것으로 처리

        current_app.logger.info(f'구독 생성/연장 완료: sub_id={subscription.id}, user_id={user_id}, carryover_days={carryover_days}')
        return True

    except Exception as e:
        current_app.logger.error(f'토스페이먼츠 구독 생성 실패: {str(e)}')
        db.session.rollback()
        return False

# =========================
# 토스페이먼츠 환불 API
# =========================
@payments_v2_bp.route('/api/v2/payments/refund', methods=['POST'])
def refund_payment():
    """토스페이먼츠 환불 처리"""
    try:
        data = request.get_json()
        pay_token = data.get('payToken')
        refund_amount = data.get('amount')
        reason = data.get('reason', '반품 취소')
        
        if not pay_token or not refund_amount:
            return jsonify({
                'success': False,
                'error': 'payToken과 환불 금액이 필요합니다.'
            }), 400
        
        # 환불 고유 번호 생성
        import uuid
        refund_no = f'refund_{uuid.uuid4().hex[:16]}'
        
        # 환불 세금 계산 (부가세 10% 기준)
        refund_amount_taxable = int(refund_amount / 1.1)  # 환불할 금액 중 과세금액 (부가세 제외)
        refund_amount_vat = refund_amount - refund_amount_taxable  # 환불할 금액 중 부가세
        
        # 환불 요청 데이터 구성
        refund_data = {
            'apiKey': TOSS_CONFIG['api_key'],
            'payToken': pay_token,
            'refundNo': refund_no,
            'amount': refund_amount,
            'amountTaxFree': 0,  # 환불할 금액 중 비과세금액 (필수, 0으로 설정)
            'amountTaxable': refund_amount_taxable,  # 환불할 금액 중 과세금액 (부가세 제외)
            'amountVat': refund_amount_vat,  # 환불할 금액 중 부가세
            'amountServiceFee': 0,  # 환불할 금액 중 봉사료 (0으로 설정)
            'reason': reason
        }
        
        # 토스페이먼츠 환불 API 호출
        response = requests.post(
            TOSS_CONFIG['refund_url'],
            headers={'Content-Type': 'application/json'},
            json=refund_data
        )
        
        if response.status_code == 200:
            result = response.json()
            current_app.logger.info(f'토스페이먼츠 환불 성공: {result}')
            current_app.logger.info(f'환불 세금 정보 - 총액: {refund_amount}원, 과세: {refund_amount_taxable}원, 부가세: {refund_amount_vat}원')
            
            return jsonify({
                'success': True,
                'refund_no': refund_no,
                'amount': refund_amount,
                'amount_taxable': refund_amount_taxable,
                'amount_vat': refund_amount_vat,
                'message': '환불이 성공적으로 처리되었습니다.'
            })
        else:
            current_app.logger.error(f'토스페이먼츠 환불 실패: {response.status_code} - {response.text}')
            return jsonify({
                'success': False,
                'error': f'환불 처리 실패: {response.text}'
            }), 400
            
    except Exception as e:
        current_app.logger.error(f'토스페이먼츠 환불 처리 실패: {str(e)}')
        return jsonify({
            'success': False,
            'error': '환불 처리 중 오류가 발생했습니다.'
        }), 500

# ===========================================================
# 토스페이먼츠 결제 승인(Execute) API (autoExecute=false일때 사용)
# ===========================================================
@payments_v2_bp.route('/api/v2/payments/toss/execute', methods=['POST'])
def execute_toss_payment():
    """
    결제 승인(Execute): PAY_APPROVED 상태의 결제건을 최종 승인.
    req: { payToken: string }
    """
    data = request.get_json()
    pay_token = data.get('payToken')
    if not pay_token:
        return jsonify({'error': 'payToken이 필요합니다.'}), 400

    r = requests.post(
        'https://pay.toss.im/api/v2/execute',
        headers={'Content-Type': 'application/json'},
        json={
            'apiKey': TOSS_CONFIG['api_key'],
            'payToken': pay_token
        },
        timeout=10
    )
    current_app.logger.info(f'Execute resp: {r.status_code} {r.text}')
    if r.status_code != 200:
        return jsonify({'error': '승인 실패', 'raw': r.text}), 400

    result = r.json()  # 여기서 payStatus가 PAY_COMPLETE로 변경되는지 확인
    # 안전하게 status 재확인(아래 status 라우트 재사용 권장)
    return jsonify({'success': True, 'result': result})

# ============================
# 토스페이먼츠 결제 상태 조회 API 
# ============================
@payments_v2_bp.route('/api/v2/payments/toss/status', methods=['GET'])
def status_toss_payment():
    """토스페이먼츠 결제 상태 조회 API (공통 함수 사용)"""
    order_no = request.args.get('orderNo') or request.args.get('orderno')
    pay_token = request.args.get('payToken')

    if not pay_token and not order_no:
        return jsonify({'error': 'payToken 또는 orderNo 필요'}), 400

    # 공통 함수 사용
    status_data, error = fetch_toss_status(pay_token=pay_token, order_no=order_no)
    if error:
        return jsonify({'error': error}), 400

    return jsonify(status_data)
    

@payments_v2_bp.route('/api/payments/status', methods=['GET'])
def legacy_status_alias():
    """
    프론트 레거시 호환:
    /api/payments/status?orderId=... -> { success: bool, status: 'COMPLETED'|'PENDING'|'FAILED' }
    """
    order_id = request.args.get('orderId') or request.args.get('orderno') or request.args.get('orderNo')
    pay_token = request.args.get('payToken')

    if not order_id and not pay_token:
        return jsonify({'success': False, 'error': 'orderId 또는 payToken 필요'}), 400

    data, err = fetch_toss_status(pay_token=pay_token, order_no=order_id)
    if err:
        return jsonify({'success': False, 'error': err}), 200  # 200으로 돌려 폴링 계속되게

    pay_stat = (data or {}).get('payStatus') or (data or {}).get('status')
    mapped = 'PENDING'
    if pay_stat == 'PAY_COMPLETE':
        mapped = 'COMPLETED'
    elif pay_stat in ('PAY_CANCEL', 'CANCELED', 'FAILED', 'FAIL'):
        mapped = 'FAILED'

    return jsonify({'success': True, 'status': mapped}), 200