# src/services/cancel_orchestrator.py
from datetime import datetime, timedelta
from flask import current_app
from sqlalchemy import and_
import os
import uuid
import requests

from src.models.subscription import db, Subscription, SubscriptionStatus, PaymentMethod, PaymentStatus
from src.models.payment import PaymentHistory, PaymentStatus as PHStatus
from src.utils.user_ref import resolve_user_ref

# 기존 취소 라우터에서 쓰던 함수 재사용 (이미 있다면 import 만)
# 사용한 일수 기준으로 각 결제건 환불 가능액을 계산하는 함수
# from src.routes.subscription import calculate_refund_amount_for_payment  # 경로가 다르면 수정

TOSS_REFUND_URL = 'https://pay.toss.im/api/v2/refunds'
TOSS_API_KEY = os.environ.get('TOSS_API_KEY')


def _refund_single_payment(latest_payment, *, reason='반품 취소(전체 환불)'):
    """
    단일 PaymentHistory에 대해 환불 API 호출 + DB 상태 갱신.
    - latest_payment: PaymentHistory (COMPLETED)
    return: dict(success: bool, amount: int, refund_no: str|None, err: str|None)
    """
    # refund_amount = calculate_refund_amount_for_payment(latest_payment)
    # current_app.logger.info(f'[_refund_single_payment] payment_id={latest_payment.id}, key={latest_payment.payment_key}, '
    #                         f'calc_refund_amount={refund_amount}')
    
    refund_amount = int(latest_payment.amount or 0)
    if refund_amount <= 0:
        return {'success': True, 'amount': 0, 'refund_no': None, 'err': None}

    refund_no = f'refund_{uuid.uuid4().hex[:16]}'
    amount_taxable = int(refund_amount / 1.1)  # 공급가액
    amount_vat = refund_amount - amount_taxable

    payload = {
        'apiKey': TOSS_API_KEY,
        'payToken': latest_payment.payment_key,
        'refundNo': refund_no,
        'amount': refund_amount,
        'amountTaxable': amount_taxable,
        'amountTaxFree': 0,
        'amountVat': amount_vat,
        'amountServiceFee': 0,
        'reason': reason,
    }

    try:
        r = requests.post(TOSS_REFUND_URL, headers={'Content-Type': 'application/json'}, json=payload, timeout=30)
        if r.status_code == 200:
            latest_payment.status = PHStatus.REFUNDED
            latest_payment.description = f'환불 완료 - {refund_no}'
            latest_payment.updated_at = datetime.now()
            db.session.commit()
            current_app.logger.info(f'[_refund_single_payment] 환불 성공: payment_id={latest_payment.id}, refund_no={refund_no}')
            return {'success': True, 'amount': refund_amount, 'refund_no': refund_no, 'err': None}
        else:
            current_app.logger.error(f'[_refund_single_payment] 환불 실패: status={r.status_code}, body={r.text}')
            return {'success': False, 'amount': 0, 'refund_no': refund_no, 'err': f'HTTP {r.status_code}: {r.text}'}
    except Exception as e:
        current_app.logger.exception('[_refund_single_payment] 환불 호출 예외')
        return {'success': False, 'amount': 0, 'refund_no': refund_no, 'err': str(e)}


def _collect_target_payments(subscription_id: int):
    """
    환불 대상 결제건 수집 (기간 연장형: 최근 결제부터 처리하는 것이 일반적이나,
    여기선 전 결제 체인 환불을 목표로 COMPLETED 상태 전부를 타겟팅)
    """
    q = (PaymentHistory.query
         .filter_by(subscription_id=subscription_id, status=PHStatus.COMPLETED)
         .order_by(PaymentHistory.created_at.desc()))
    return q.all()


def _revert_subscription_period_after_refunds(subscription: Subscription, refunded_payments: list[PaymentHistory]):
    """
    모든 환불이 성공한 뒤, 각 결제건이 부여했던 일수(subscription_days)만큼
    종료일을 되돌립니다. (기간 연장형 모델 회수)
    """
    total_days_recover = 0
    for ph in refunded_payments:
        try:
            total_days_recover += int(ph.subscription_days or 0)
        except Exception:
            pass

    if total_days_recover > 0:
        subscription.end_date = subscription.end_date - timedelta(days=total_days_recover)
        subscription.updated_at = datetime.now()
        db.session.commit()
        current_app.logger.info(f'[_revert_subscription_period_after_refunds] 회수 일수: {total_days_recover}일, '
                                f'new_end_date={subscription.end_date}')


def _finalize_cancellation(subscription: Subscription):
    """
    구독 최종 취소 처리 + 사용자 권한 회수 + 크레딧 사용 완료 처리
    """
    # 오늘 기준 만료 여부
    today = datetime.now().date()
    end_date_only = subscription.end_date.date() if subscription.end_date else today

    if end_date_only <= today:
        subscription.status = SubscriptionStatus.CANCELLED
        subscription.next_billing_date = None
    else:
        # 아직 기간이 남았지만 '체인 전체 환불' 컨셉이면 즉시 취소
        subscription.status = SubscriptionStatus.CANCELLED
        subscription.next_billing_date = None

    subscription.updated_at = datetime.now()
    db.session.commit()

    # 크레딧 기준 구독자인 경우 남은 크레딧 사용 완료 처리
    try:
        from src.models.user import User, SubscriptionType
        from src.utils.credit_manager import consume_all_remaining_credits
        
        user = resolve_user_ref(subscription.user_id)
        if user and user.subscription_type == SubscriptionType.CREDIT:
            credit_result = consume_all_remaining_credits(
                user_id=subscription.user_id,
                description='구독 취소로 인한 남은 크레딧 사용 완료',
                reference_id=str(subscription.id),
                reference_type='subscription_cancellation'
            )
            if credit_result.get('success'):
                current_app.logger.info(f'[_finalize_cancellation] 남은 크레딧 사용 완료: {credit_result.get("consumed", 0)} 크레딧')
            else:
                current_app.logger.warning(f'[_finalize_cancellation] 남은 크레딧 사용 실패: {credit_result.get("error")}')
    except Exception as e:
        current_app.logger.warning(f'[_finalize_cancellation] 크레딧 사용 처리 실패(무시): {str(e)}')

    # 역할 회수(실패해도 진행)
    try:
        from src.models.user import User
        user = resolve_user_ref(subscription.user_id)
        if user:
            user.role = 'role_free'
            db.session.commit()
            current_app.logger.info(f'[_finalize_cancellation] 사용자 권한 회수: user_id={user.id}')
    except Exception as e:
        current_app.logger.warning(f'[_finalize_cancellation] 역할 회수 실패(무시): {e}')

    current_app.logger.info(f'[_finalize_cancellation] 구독 취소 완료: sub_id={subscription.id}')


def finish_settlement_then_refund(*, order_id: str, pay_token: str | None, amount: int | None):
    """
    (정산 결제 성공 후 호출)
    1) 정산 PENDING PH → COMPLETED 보장
    2) 현재 활성 구독의 결제 체인 전체 환불 시도 (COMPLETED들 전부 환불)
    3) 모든 환불 성공 시 구독 상태 CANCELLED + 권한 회수
    4) 일부 환불 실패 시: 구독은 유지(취소 미완료)하고, 실패 내역을 리턴/로그

    return: dict {
      success: bool,
      settlement: {order_id, amount},
      refund: {
        total_refund: int,
        success_count: int,
        fail_count: int,
        items: [{payment_id, refund_amount, success, refund_no, error}]
      },
      cancelled: bool
    }
    """
    result = {
        'success': False,
        'settlement': {'order_id': order_id, 'amount': int(amount or 0)},
        'refund': {'total_refund': 0, 'success_count': 0, 'fail_count': 0, 'items': []},
        'cancelled': False
    }

    try:
        # 0) 정산 주문 형식 확인(안전)
        is_settlement = bool(order_id) and (order_id.startswith('mlink_usage_') or '_usage_' in order_id)
        if not is_settlement:
            current_app.logger.warning(f'[finish_settlement_then_refund] 정산 주문이 아님: {order_id}')
            return result

        # 1) 정산 PH COMPLETE 보장
        ph = PaymentHistory.query.filter_by(order_id=order_id).order_by(PaymentHistory.created_at.desc()).first()
        if not ph:
            current_app.logger.error(f'[finish_settlement_then_refund] PH not found for order_id={order_id}')
            return result

        if ph.status != PHStatus.COMPLETED:
            ph.status = PHStatus.COMPLETED
            ph.updated_at = datetime.now()
            db.session.commit()
            current_app.logger.info(f'[finish_settlement_then_refund] 정산 PH COMPLETED 보정: ph_id={ph.id}')

        # 2) 활성 구독 조회 (정산 PH는 subscription_id가 있을 수도/없을 수도)
        subscription = Subscription.query.filter_by(user_id=ph.user_id, status=SubscriptionStatus.ACTIVE).first()
        if not subscription:
            current_app.logger.warning('[finish_settlement_then_refund] ACTIVE 구독 없음 — 취소할 대상이 없어 환불만 발생한 상태.')
            # 케이스: 이미 어딘가에서 취소 처리됨
            result['success'] = True
            return result

        # 3) 환불 대상 결제 수집
        target_payments = _collect_target_payments(subscription.id)
        if not target_payments:
            current_app.logger.info('[finish_settlement_then_refund] 환불 대상 결제 없음')
            result['success'] = True
            # 환불할 게 없으면 그냥 구독 취소만 진행
            _finalize_cancellation(subscription)
            result['cancelled'] = True
            return result

        def _is_usage_settlement(ph: PaymentHistory) -> bool:
            oid = (ph.order_id or "")
            name = (ph.product_name or "")
            # mlink_usage_* 규칙, onetime 주기, 명칭 등으로 폭넓게 방어
            return (
                oid.startswith("mlink_usage_")
                or (ph.billing_cycle or "").lower() == "onetime"
                or "정산" in name  # '반품 정산' / '구독 사용분 정산' 등
            )
        
        completed_list = [
            ph for ph in target_payments
            if not _is_usage_settlement(ph)
            and not (order_id and ph.order_id == order_id)
        ]

        # 4) 각 결제 환불 시도
        all_refund_ok = True
        refundable_completed = []
        for p in target_payments:
            if _is_usage_settlement(p):
                current_app.logger.info(f"[FinishSettle] skip usage settlement PH: id={p.id}, order={p.order_id}")
                continue

            rr = _refund_single_payment(p, reason='전체 환불(정산 선결제 후)')
            item = {
                'payment_id': p.id,
                'refund_amount': rr.get('amount', 0),
                'success': rr.get('success', False),
                'refund_no': rr.get('refund_no'),
                'error': rr.get('err')
            }
            result['refund']['items'].append(item)

            if rr.get('success'):
                result['refund']['success_count'] += 1
                result['refund']['total_refund'] += rr.get('amount', 0)
                refundable_completed.append(p)
            else:
                result['refund']['fail_count'] += 1
                all_refund_ok = False

        # 5) 환불이 모두 성공해야 구독 취소/회수
        if all_refund_ok:
            # 환불로 부여됐던 일수 회수
            _revert_subscription_period_after_refunds(subscription, refundable_completed)
            # 최종 취소
            _finalize_cancellation(subscription)
            result['cancelled'] = True
            result['success'] = True
            current_app.logger.info('[finish_settlement_then_refund] 전체 환불 성공 → 구독 취소 완료')
        else:
            # 일부 환불 실패 → 구독은 유지 (사용자 보호)
            db.session.rollback()  # 안전 차원에서 트랜잭션 경계 정리 (외부 API는 롤백 불가)
            current_app.logger.error('[finish_settlement_then_refund] 일부 환불 실패 — 구독 취소 보류(유지). 운영介入 필요')
            result['success'] = False

        return result

    except Exception as e:
        current_app.logger.exception('[finish_settlement_then_refund] 예외 발생')
        db.session.rollback()
        return result
