"""
Fetch China - Order Service
Business logic for order creation and management
"""

from datetime import datetime
import secrets
import json
from decimal import Decimal, ROUND_HALF_UP
from typing import Optional

from sqlalchemy.orm import Session

from app.models.order import Order, OrderItem
from app.models.user import User
from app.models.transaction import Transaction
from app.schemas.order import OrderCreate
from app.core.config import settings
from app.core.image_service import ImageService
from app.core.money_helper import MoneyHelper
from app.core.idempotency import IdempotencyHelper
from app.services.notification_service import NotificationService
from app.services.audit_service import AuditService
from app.core.logging_config import get_logger

logger = get_logger(__name__)


class OrderService:
    MAX_ORDER_NUMBER_RETRIES = 5

    @staticmethod
    def _generate_order_number(db: Session) -> str:
        """
        Generate a unique order number: UC + YYYYMMDD + Random 4 digits
        Example: UC202412161234
        Uses retry mechanism to handle high-concurrency collisions.
        """

        for attempt in range(OrderService.MAX_ORDER_NUMBER_RETRIES):
            date_str = datetime.utcnow().strftime("%Y%m%d")
            random_digits = secrets.randbelow(10000)
            order_number = f"UC{date_str}{random_digits:04d}"

            # Check if order number already exists
            existing = (
                db.query(Order).filter(Order.order_number == order_number).first()
            )
            if not existing:
                return order_number

        # If still colliding after retries, use more entropy
        random_digits = secrets.randbelow(1000000)
        return f"UC{date_str}{random_digits:06d}"

    @staticmethod
    def create_order(
        db: Session,
        user_id: str,
        order_data: OrderCreate,
        background_tasks=None,
        idempotency_key: Optional[str] = None,
        partner_id: Optional[str] = None,
    ) -> Order:
        """
        Create a new order, calculate fees, deduct balance, and record transaction.
        Uses row-level locking to prevent concurrent balance deduction issues.
        Supports idempotency: if idempotency_key is provided, it is saved with the order.
        """
        # 1. Get User with row-level lock to prevent concurrent deduction issues
        # SQLite: with_for_update() is ignored, but PostgreSQL will lock the row
        user = db.query(User).filter(User.id == user_id).with_for_update().first()
        if not user:
            raise Exception(
                "User not found"
            )  # Should be caught by API Exception Handler

        # 2. Calculate Totals (using Decimal for precision)
        total_cny = Decimal("0.00")

        # Prepare Order Items models
        db_items = []
        for item in order_data.items:
            unit_price = Decimal(str(item.unit_price_cny))
            shipping = Decimal(str(item.domestic_shipping_cny))
            item_total_cny = unit_price * item.quantity + shipping
            total_cny += item_total_cny

            # Handle Freight Collect Deposit
            fc_deposit = 0.0
            if item.is_freight_collect:
                # Deposit logic: typically deducted separately or part of total?
                # User said: "Freight Collect orders need extra 50 deposit".
                # Usually this deposit is frozen.
                # Let's assume for this "Order Total" calculation, we are calculating the PURCHASE cost.
                # The deposit is handled as a separate Freeze or added to cost?
                # PRD says: "Unpaid shipping/deposit > Frozen Balance" handling.
                # Simplified logic for now: We treat the deposit as part of the total cost deducted now?
                # Or just freeze it?
                # Let's stick to simple: The user pays for the item + known shipping now.
                # Freight collect deposit logic might be complex.
                # FOR THIS V1: Let's assume we just deduct the Item Cost + Shipping.
                # If Freight Collect is checked, domestic_shipping_cny is 0, so we just pay Item Cost.
                # The "Freeze 50" logic: Let's implement it if safe.
                # Only freeze if freight collect.
                if settings.FREIGHT_COLLECT_DEPOSIT_CNY > 0:
                    fc_deposit = settings.FREIGHT_COLLECT_DEPOSIT_CNY
                    # We will freeze this amount in USD later? Or just deduct it?
                    # "Extra 50 deposit... remaining balance collected upon delivery"
                    # Let's add it to the total_cny to be deducted/frozen.
                    # This simplifies "Freeze" to just "Pay Deposit".
                    # But the field is `freight_collect_frozen_cny`.
                    pass

            db_item = OrderItem(
                order_id="",  # Will be set by relationship or manually
                product_url=str(item.product_url),  # Convert HttpUrl to str
                product_name=item.product_name,
                specification=item.specification,
                unit_price_cny=round(item.unit_price_cny, 2),
                quantity=item.quantity,
                domestic_shipping_cny=round(item.domestic_shipping_cny, 2),
                is_freight_collect=item.is_freight_collect,
                freight_collect_frozen_cny=round(
                    fc_deposit, 2
                ),  # Set it but maybe handle fund flow separately?
                plan_b=item.plan_b,
                user_image_url=OrderService._process_item_image(item.user_image_url)
                if item.user_image_url
                else None,
                # USD Tracking fields (will be populated below)
                unit_price_usd=0.0,
                domestic_shipping_usd=0.0,
                service_fee_usd=0.0,
                # Customs Declaration (optional, for international shipping)
                declared_name_en=getattr(item, "declared_name_en", None),
                declared_value_usd=getattr(item, "declared_value_usd", None),
                is_sensitive=getattr(item, "is_sensitive", False),
            )
            db_items.append(db_item)

        # 3. Currency Conversion & Service Fee (using Decimal)
        # 优先使用手动设置的汇率，否则从文件读取
        # 汇率计算：BOC汇率 / 1.02（加2% buffer，有利于Martin）
        from app.utils.system_config import get_manual_exchange_rate

        manual_rate = get_manual_exchange_rate()
        if manual_rate:
            rate = Decimal(str(manual_rate))
            print(f"Using manual exchange rate: {rate}")
        else:
            # 从文件读取汇率（如果不存在则使用.env配置）
            from app.utils.boc_scraper import load_rate_from_file

            rate_data = load_rate_from_file()
            if rate_data and "rate" in rate_data:
                raw_rate = Decimal(str(rate_data["rate"]))
                # 加2% buffer：除以1.02，让客户付更多美元
                rate = (raw_rate / Decimal("1.02")).quantize(
                    Decimal("0.0001"), rounding=ROUND_HALF_UP
                )
                print(
                    f"Using exchange rate from file: {raw_rate} -> {rate} (after 2% buffer)"
                )
            else:
                raw_rate = Decimal(str(settings.EXCHANGE_RATE_CNY_USD))
                rate = (raw_rate / Decimal("1.02")).quantize(
                    Decimal("0.0001"), rounding=ROUND_HALF_UP
                )
                print(
                    f"Using exchange rate from .env: {raw_rate} -> {rate} (after 2% buffer)"
                )

        # Total USD (Product + Shipping)
        total_item_usd = (total_cny / rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )

        # Service Fee (10%, Min $5)
        # Service Fee (10%, Min $5) - Check for custom rate
        if user.custom_service_fee_rate is not None:
            fee_rate = Decimal(str(user.custom_service_fee_rate))
            print(f"Using custom service fee rate for user {user.email}: {fee_rate}")
        else:
            fee_rate = Decimal(str(settings.SERVICE_FEE_RATE))

        # Minimum service fee: priority is custom > registration snapshot > global
        if user.custom_min_service_fee_usd is not None:
            min_fee = Decimal(str(user.custom_min_service_fee_usd))
            print(f"Using custom min service fee for user {user.email}: ${min_fee}")
        elif user.min_service_fee_usd is not None:
            min_fee = Decimal(str(user.min_service_fee_usd))
        else:
            min_fee = Decimal(str(settings.MIN_SERVICE_FEE_USD))

        raw_service_fee = total_item_usd * fee_rate
        service_fee_usd = max(min_fee, raw_service_fee).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )

        # Freight Collect Frozen USD (if separate) or just Included?
        # Calculate total frozen for freight collect items
        total_frozen_cny = Decimal("0.00")
        for item in order_data.items:
            if item.is_freight_collect and settings.FREIGHT_COLLECT_DEPOSIT_CNY > 0:
                total_frozen_cny += Decimal(str(settings.FREIGHT_COLLECT_DEPOSIT_CNY))

        frozen_usd = (total_frozen_cny / rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )
        grand_total_usd = (total_item_usd + service_fee_usd).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )

        # 3.1 Ironclad USD Ledger: Distribute Order Totals to Items
        # We distribute total_item_usd and service_fee_usd proportionally.
        # To avoid any penny leaks, the last item absorbs the rounding remainder.

        distributed_product_usd = Decimal("0.00")
        distributed_shipping_usd = Decimal("0.00")
        distributed_service_fee_usd = Decimal("0.00")

        # Calculate sub-totals for distribution mapping
        total_shipping_cny = sum(
            Decimal(str(item.domestic_shipping_cny)) for item in db_items
        )
        total_product_cny = total_cny - total_shipping_cny

        # Total USD targets (already quantized above)
        target_product_total_usd = (total_product_cny / rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )
        target_shipping_total_usd = (total_shipping_cny / rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )

        # Ensure target_product_total_usd + target_shipping_total_usd == total_item_usd
        # If not (due to separate quantization), adjust product total to match total_item_usd
        if (target_product_total_usd + target_shipping_total_usd) != total_item_usd:
            target_product_total_usd = total_item_usd - target_shipping_total_usd

        for i, item in enumerate(db_items):
            is_last = i == len(db_items) - 1

            # 1. Product Distribution
            if is_last:
                item_product_usd = target_product_total_usd - distributed_product_usd
            else:
                item_product_cny = Decimal(str(item.unit_price_cny)) * item.quantity
                if total_product_cny > 0:
                    item_product_usd = (
                        target_product_total_usd
                        * (item_product_cny / total_product_cny)
                    ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
                else:
                    item_product_usd = Decimal("0.00")
                distributed_product_usd += item_product_usd

            # Unit price in USD for records
            item.unit_price_usd = float(
                (item_product_usd / item.quantity).quantize(
                    Decimal("0.01"), rounding=ROUND_HALF_UP
                )
            )

            # 2. Shipping Distribution
            if is_last:
                item_shipping_usd = target_shipping_total_usd - distributed_shipping_usd
            else:
                item_shipping_cny = Decimal(str(item.domestic_shipping_cny))
                if total_shipping_cny > 0:
                    item_shipping_usd = (
                        target_shipping_total_usd
                        * (item_shipping_cny / total_shipping_cny)
                    ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
                else:
                    item_shipping_usd = Decimal("0.00")
                distributed_shipping_usd += item_shipping_usd

            item.domestic_shipping_usd = float(item_shipping_usd)

            # 3. Service Fee Distribution
            if is_last:
                item_service_fee = service_fee_usd - distributed_service_fee_usd
            else:
                item_total_cny = (
                    Decimal(str(item.unit_price_cny)) * item.quantity
                ) + Decimal(str(item.domestic_shipping_cny))
                if total_cny > 0:
                    item_service_fee = (
                        service_fee_usd * (item_total_cny / total_cny)
                    ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
                else:
                    item_service_fee = Decimal("0.00")
                distributed_service_fee_usd += item_service_fee

            item.service_fee_usd = float(item_service_fee)

        # Total required = grand_total + frozen deposit
        total_required = grand_total_usd + frozen_usd

        # 4. Check Balance (compare as Decimal)
        balance = MoneyHelper.to_decimal(user.balance_usd)
        if balance < total_required:
            raise ValueError(
                f"Insufficient balance. Required: ${total_required}, Available: ${balance}"
            )

        # 5. Transaction: Deduct Balance + Create Order (atomic)
        # IMPORTANT: If anything fails after this, we must rollback
        original_balance = balance
        original_frozen = MoneyHelper.to_decimal(user.frozen_usd or 0)

        try:
            # 5a. Deduct total required amount (grand_total + frozen) from balance
            user.balance_usd = MoneyHelper.to_float(
                MoneyHelper.round_money(balance - total_required)
            )

            # Move frozen amount from balance to frozen_usd (without additional deduction)
            if frozen_usd > 0:
                current_frozen = MoneyHelper.to_decimal(user.frozen_usd or 0)
                user.frozen_usd = MoneyHelper.to_float(current_frozen + frozen_usd)
                
                # Create transaction record for the freeze operation
                freeze_transaction = Transaction(
                    user_id=str(user.id),
                    partner_id=user.partner_id,
                    type="deposit_freeze",
                    amount_usd=-float(frozen_usd),
                    balance_after=user.balance_usd,
                    reference_type="order",
                    reference_id="",  # Will be updated after order creation
                    description=f"Frozen Deposit for Freight Collect Items"
                )
                db.add(freeze_transaction)

            # Create Order
            order_partner_id = partner_id if partner_id else user.partner_id
            new_order = Order(
                user_id=user_id,
                partner_id=order_partner_id,
                order_number=OrderService._generate_order_number(db),
                idempotency_key=idempotency_key,
                total_cny=round(total_cny, 2),
                total_usd=total_item_usd,
                service_fee_usd=service_fee_usd,
                exchange_rate=rate,
                status="processing",  # 订单直接进入处理中状态（合伙人角色已统一，无需接单）
                items=db_items,
            )

            db.add(new_order)
            db.flush()  # Get order ID before creating transaction

            # Build product summary for transaction description
            first_item = order_data.items[0] if order_data.items else None
            item_count = len(order_data.items)
            if first_item:
                product_summary = first_item.product_name[:30] + (
                    "..." if len(first_item.product_name) > 30 else ""
                )
                if item_count > 1:
                    product_summary += f" (+{item_count - 1} more)"
            else:
                product_summary = "Items"

            # Transaction Record with proper reference_id
            transaction = Transaction(
                user_id=user_id,
                partner_id=user.partner_id,
                type="order_payment",
                amount_usd=-grand_total_usd,
                balance_after=user.balance_usd,
                reference_type="order",
                reference_id=new_order.id,
                description=f"Order {new_order.order_number}: {product_summary}",
            )
            db.add(transaction)

            # Commit all changes atomically
            db.commit()
            db.refresh(new_order)

        except Exception as e:
            # CRITICAL: Rollback on ANY failure to prevent balance loss
            db.rollback()

            # Restore user's balance and frozen amount from saved values
            user.balance_usd = MoneyHelper.to_float(
                MoneyHelper.round_money(original_balance)
            )
            user.frozen_usd = MoneyHelper.to_float(
                MoneyHelper.round_money(original_frozen)
            )
            db.commit()  # Commit the rollback

            import logging

            logging.error(f"Order creation failed, balance restored: {e}")
            raise

        # Record domain event for order creation
        try:
            from app.services.event_service import EventService

            EventService.log_event(
                db=db,
                event_type=EventService.EVENT_ORDER_CREATED,
                aggregate_type="order",
                aggregate_id=new_order.id,
                actor_id=user_id,
                actor_role="client",
                payload={
                    "order_number": new_order.order_number,
                    "total_cny": float(total_cny),
                    "total_usd": float(total_item_usd),
                    "service_fee_usd": float(service_fee_usd),
                    "item_count": len(db_items),
                },
                description=f"Order {new_order.order_number} created by client",
                idempotency_key=idempotency_key,
            )
        except Exception as e:
            import logging

            logging.warning(f"Failed to log order creation event: {e}")

        # Notify admins and buyers via email (async supported)
        try:
            NotificationService.notify_stakeholders_new_order(
                db, new_order, background_tasks
            )
        except Exception as e:
            print(f"Failed to schedule stakeholder order email: {e}")

        # Notify partner about new order (if user has a partner)
        if new_order.partner_id:
            try:
                from app.services.email_service import EmailService

                # Get user info for notification
                order_user = db.query(User).filter(User.id == user_id).first()
                partner = db.query(User).filter(User.id == new_order.partner_id).first()
                if partner and order_user:
                    commission_rate = 0.40
                    commission_amount = float(service_fee_usd) * commission_rate
                    EmailService.send_partner_new_order_email(
                        partner_email=partner.email,
                        order_number=new_order.order_number,
                        customer_name=order_user.email.split("@")[0],
                        order_amount_usd=float(total_item_usd),
                        commission_amount=commission_amount,
                        order_url=f"{settings.FRONTEND_URL}/partner/orders",
                    )
            except Exception as e:
                print(f"Failed to send partner new order email: {e}")

        # Send order confirmation email to user
        try:
            from app.services.email_service import send_order_confirmation

            order_user = db.query(User).filter(User.id == user_id).first()
            if order_user:
                order_details = {
                    "order_number": new_order.order_number,
                    "total_usd": float(new_order.total_usd),
                    "service_fee_usd": float(new_order.service_fee_usd),
                    "item_count": len(new_order.items),
                    "status": new_order.status,
                }
                send_order_confirmation(
                    user_email=order_user.email,
                    order_number=new_order.order_number,
                    order_details=order_details,
                )
        except Exception as e:
            print(f"Failed to send order confirmation email: {e}")

        return new_order

    @staticmethod
    def _process_item_image(image_data: str) -> Optional[str]:
        """Helper to process base64 item images"""
        if not image_data or not image_data.startswith("data:image"):
            return image_data

        try:
            return ImageService.process_and_save(image_data, folder="orders")
        except Exception as e:
            print(f"Failed to process item image: {e}")
            return None

    @staticmethod
    def get_user_orders(db: Session, user_id: str, skip: int = 0, limit: int = 100):
        orders = (
            db.query(Order)
            .filter(Order.user_id == user_id)
            .order_by(Order.created_at.desc())
            .offset(skip)
            .limit(limit)
            .all()
        )
        # Populate parcel info for items (for frontend linking)
        for order in orders:
            for item in order.items:
                if item.parcel:
                    item.parcel_number = item.parcel.parcel_number
                    item.international_tracking_no = (
                        item.parcel.international_tracking_no
                    )
        return orders

    @staticmethod
    def get_order(db: Session, order_id: str, user_id: str):
        order = (
            db.query(Order)
            .filter(Order.id == order_id, Order.user_id == user_id)
            .first()
        )
        if order:
            for item in order.items:
                if item.parcel:
                    item.parcel_number = item.parcel.parcel_number
                    item.international_tracking_no = (
                        item.parcel.international_tracking_no
                    )
        return order

    @staticmethod
    def cancel_order(db: Session, order_id: str, user_id: str) -> Order:
        """
        Cancel an order and refund the user.
        Orders in 'processing' or 'on_hold' status can be cancelled.
        """
        order = (
            db.query(Order)
            .filter(Order.id == order_id, Order.user_id == user_id)
            .first()
        )

        if not order:
            raise ValueError("Order not found")

        # Check if order can be cancelled
        allowed_statuses = ["processing", "on_hold"]
        if order.status not in allowed_statuses:
            raise ValueError(
                f"Cannot cancel order in '{order.status}' status. Orders can be cancelled in: {', '.join(allowed_statuses)}"
            )

        # Get user for refund
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise ValueError("User not found")

        # Check idempotency
        idempotency_key = IdempotencyHelper.ensure_unique(
            db, "cancel_order", "order", order.id, user_id
        )

        # Calculate refund amount (total_usd + service_fee_usd) - use Decimal for precision
        refund_amount = Decimal(str(order.total_usd)) + Decimal(
            str(order.service_fee_usd)
        )

        # Refund to user balance (convert to Decimal first)
        current_balance = MoneyHelper.to_decimal(user.balance_usd or 0)
        new_balance = current_balance + refund_amount
        user.balance_usd = MoneyHelper.to_float(MoneyHelper.round_money(new_balance))

        # Update order status
        order.status = "cancelled"
        order.updated_at = datetime.utcnow()

        # Update item statuses
        items = db.query(OrderItem).filter(OrderItem.order_id == order.id).all()
        for item in items:
            item.status = "cancelled"
            item.updated_at = datetime.utcnow()

        # Calculate frozen deposit to refund
        frozen_usd = Decimal("0.00")
        rate = Decimal(str(order.exchange_rate))

        # Calculate frozen CNY from items
        frozen_cny = Decimal("0.00")
        for item in order.items:
            frozen_cny += Decimal(str(item.freight_collect_frozen_cny or 0))

        if frozen_cny > 0:
            frozen_usd = (frozen_cny / rate).quantize(
                Decimal("0.01"), rounding=ROUND_HALF_UP
            )

            # Unfreeze and refund
            if user.frozen_usd:
                current_frozen = MoneyHelper.to_decimal(user.frozen_usd)
                new_frozen = max(Decimal("0.00"), current_frozen - frozen_usd)
                user.frozen_usd = MoneyHelper.to_float(
                    MoneyHelper.round_money(new_frozen)
                )

            # Add frozen deposit refund to balance
            current_balance_after = MoneyHelper.to_decimal(user.balance_usd)
            final_balance = current_balance_after + frozen_usd
            user.balance_usd = MoneyHelper.to_float(
                MoneyHelper.round_money(final_balance)
            )

        # Create refund transaction for purchase
        transaction = Transaction(
            user_id=user_id,
            partner_id=user.partner_id,
            type="order_refund",
            amount_usd=float(refund_amount),
            balance_after=float(
                Decimal(str(user.balance_usd)) - frozen_usd
            ),
            reference_type="order",
            reference_id=order.id,
            idempotency_key=idempotency_key,
            description=f"Refund for cancelled Order {order.order_number}",
        )
        db.add(transaction)

        # Transaction for Deposit Refund
        if frozen_usd > 0:
            transaction_deposit = Transaction(
                user_id=user_id,
                partner_id=user.partner_id,
                type="deposit_refund",
                amount_usd=frozen_usd,
                balance_after=user.balance_usd,  # Final balance
                reference_type="order",
                reference_id=order.id,
                description=f"Unfreeze Deposit for Order {order.order_number}",
            )
            db.add(transaction_deposit)

        # Commit
        db.commit()
        db.refresh(order)

        # Record domain event for order cancellation
        try:
            from app.services.event_service import EventService

            EventService.log_event(
                db=db,
                event_type=EventService.EVENT_ORDER_CANCELLED,
                aggregate_type="order",
                aggregate_id=order.id,
                actor_id=user_id,
                actor_role="client",
                payload={
                    "order_number": order.order_number,
                    "refund_amount_usd": float(refund_amount),
                    "frozen_refund_usd": float(frozen_usd) if frozen_usd > 0 else 0,
                },
                description=f"Order {order.order_number} cancelled by client",
            )
        except Exception as e:
            import logging

            logging.warning(f"Failed to log order cancellation event: {e}")

        return order

    @staticmethod
    def handle_risk_blocked_order(db: Session, order_id: str, admin_id: str) -> Order:
        """
        Handle risk_blocked order refund.
        Refunds total amount + service fee + unfreezes deposit.
        """
        order = db.query(Order).filter(Order.id == order_id).first()
        if not order:
            raise ValueError("Order not found")

        if order.status != "risk_blocked":
            raise ValueError(f"Order must be in 'risk_blocked' status, current: {order.status}")

        user = db.query(User).filter(User.id == order.user_id).first()
        if not user:
            raise ValueError("User not found")

        # Check idempotency
        idempotency_key = IdempotencyHelper.ensure_unique(
            db, "risk_blocked_refund", "order", order.id, str(user.id)
        )

        # Calculate refund
        refund_amount = Decimal(str(order.total_usd)) + Decimal(str(order.service_fee_usd))

        # Calculate frozen deposit
        frozen_cny = sum(Decimal(str(item.freight_collect_frozen_cny or 0)) for item in order.items)
        frozen_usd = Decimal("0.00")
        if frozen_cny > 0:
            frozen_usd = (frozen_cny / Decimal(str(order.exchange_rate))).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)

        # Refund to balance
        current_balance = MoneyHelper.to_decimal(user.balance_usd or 0)
        user.balance_usd = MoneyHelper.to_float(current_balance + refund_amount)

        # Unfreeze deposit using DepositHelper
        if frozen_usd > 0 and user.frozen_usd:
            from app.core.deposit_helper import DepositHelper

            DepositHelper.unfreeze_deposit(
                db=db,
                user=user,
                amount_usd=frozen_usd,
                reference_type="order",
                reference_id=order.id,
                description=f"Unfreeze deposit for risk blocked Order {order.order_number}"
            )

        # Update order status
        order.status = "cancelled"
        order.updated_at = datetime.utcnow()

        # Update items
        for item in order.items:
            item.status = "cancelled"
            item.updated_at = datetime.utcnow()

        # Create transactions
        db.add(Transaction(
            user_id=str(user.id),
            partner_id=user.partner_id,
            type="order_refund",
            amount_usd=float(refund_amount),
            balance_after=float(user.balance_usd),
            reference_type="order",
            reference_id=order.id,
            idempotency_key=idempotency_key,
            description=f"Risk blocked refund for Order {order.order_number}",
        ))

        db.commit()
        db.refresh(order)

        # Notify user
        NotificationService.create_notification(
            db=db,
            user_id=str(user.id),
            notification_type="order_refund",
            title="Order Cancelled - Risk Blocked",
            message=f"Order {order.order_number} was cancelled due to risk control. Full refund of ${float(refund_amount + frozen_usd):.2f} has been issued.",
            order_id=str(order.id),
        )

        logger.info(f"Risk blocked order {order.order_number} refunded: ${float(refund_amount + frozen_usd):.2f}")
        return order

    @staticmethod
    def update_order(db: Session, order_id: str, user_id: str, update_data, expected_version: int = None) -> Order:
        """
        Update an order that is still in 'processing' status.
        Recalculates totals and adjusts user balance if needed.
        
        使用乐观锁防止并发编辑时的丢失更新问题。
        
        Args:
            db: 数据库会话
            order_id: 订单ID
            user_id: 用户ID
            update_data: 更新数据
            expected_version: 期望的版本号（用于乐观锁）
        
        Returns:
            Order: 更新后的订单
        
        Raises:
            ValueError: 订单不存在、状态不允许编辑、或版本冲突
        """
        from fastapi import HTTPException, status as http_status

        # Get order and verify ownership (使用行锁)
        order = (
            db.query(Order)
            .filter(Order.id == order_id, Order.user_id == user_id)
            .with_for_update()  # 行级锁，防止并发修改
            .first()
        )
        if not order:
            raise ValueError("Order not found")

        if order.status != "processing":
            raise ValueError("Only processing orders can be edited")
        
        # 乐观锁版本检查
        if expected_version is not None and order.version != expected_version:
            raise HTTPException(
                status_code=http_status.HTTP_409_CONFLICT,
                detail={
                    "error": "version_conflict",
                    "message": "Order has been modified by another process. Please refresh and try again.",
                    "current_version": order.version,
                    "expected_version": expected_version
                }
            )

        # Get user
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise ValueError("User not found")

        # Store original totals for comparison
        original_total_usd = Decimal(str(order.total_usd))
        original_service_fee = Decimal(str(order.service_fee_usd))
        original_grand_total = original_total_usd + original_service_fee

        # Get exchange rate
        exchange_rate = Decimal(str(order.exchange_rate))

        # Update items and recalculate totals
        new_total_cny = Decimal("0.00")

        for item_update in update_data.items:
            item = (
                db.query(OrderItem)
                .filter(OrderItem.id == item_update.id, OrderItem.order_id == order_id)
                .first()
            )

            if not item:
                raise ValueError(f"Item {item_update.id} not found in this order")

            # Update fields if provided
            if item_update.product_url is not None:
                item.product_url = item_update.product_url
            if item_update.product_name is not None:
                item.product_name = item_update.product_name
            if item_update.specification is not None:
                item.specification = item_update.specification
            if item_update.unit_price_cny is not None:
                item.unit_price_cny = item_update.unit_price_cny
            if item_update.quantity is not None:
                item.quantity = item_update.quantity
            if item_update.domestic_shipping_cny is not None:
                item.domestic_shipping_cny = item_update.domestic_shipping_cny
            if item_update.user_image_url is not None:
                # Process image if base64
                item.user_image_url = OrderService._process_item_image(
                    item_update.user_image_url
                )

            item.updated_at = datetime.utcnow()

        # Recalculate total from all items
        all_items = db.query(OrderItem).filter(OrderItem.order_id == order_id).all()
        for item in all_items:
            item_total = Decimal(str(item.unit_price_cny)) * item.quantity + Decimal(
                str(item.domestic_shipping_cny or 0)
            )
            new_total_cny += item_total

        # Convert to USD
        new_total_usd = (new_total_cny / exchange_rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )

        # Calculate new service fee (10%)
        # Calculate new service fee (10% or custom)
        if user.custom_service_fee_rate is not None:
            service_fee_rate = Decimal(str(user.custom_service_fee_rate))
        else:
            service_fee_rate = Decimal(str(settings.SERVICE_FEE_RATE))

        new_service_fee = (new_total_usd * service_fee_rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )
        # Apply min fee check if calculating from scratch, but for update we usually just recalc percentage?
        # Update logic usually respects the original scale?
        # Actually create_order enforces min_fee. update_order should too.
        min_fee = Decimal(str(settings.MIN_SERVICE_FEE_USD))
        new_service_fee = max(min_fee, new_service_fee)

        # New grand total
        new_grand_total = new_total_usd + new_service_fee

        # Calculate difference
        difference = new_grand_total - original_grand_total

        # Handle balance adjustment
        if difference > 0:
            # Need to charge more - check if user has enough balance
            available = Decimal(str(user.balance_usd)) - Decimal(str(user.frozen_usd))
            if available < difference:
                raise ValueError(
                    f"Insufficient balance. Need additional ${difference:.2f}"
                )

            # Deduct additional amount
            user.balance_usd = float(Decimal(str(user.balance_usd)) - difference)

            # Record transaction
            transaction = Transaction(
                user_id=user_id,
                partner_id=user.partner_id,
                type="order_adjustment",
                amount_usd=float(-difference),
                balance_after=float(user.balance_usd),
                reference_type="order",
                reference_id=order.id,
                description=f"Additional charge for Order {order.order_number} edit",
            )
            db.add(transaction)

        elif difference < 0:
            # Refund the difference
            refund_amount = abs(difference)
            user.balance_usd = float(Decimal(str(user.balance_usd)) + refund_amount)

            # Record transaction
            transaction = Transaction(
                user_id=user_id,
                partner_id=user.partner_id,
                type="order_adjustment",
                amount_usd=float(refund_amount),
                balance_after=float(user.balance_usd),
                reference_type="order",
                reference_id=order.id,
                description=f"Refund for Order {order.order_number} edit",
            )
            db.add(transaction)

        # Update order totals
        order.total_cny = float(new_total_cny)
        order.total_usd = float(new_total_usd)
        order.service_fee_usd = float(new_service_fee)
        order.updated_at = datetime.utcnow()

        # Update item USD records and distribute service fee (Ironclad Ledger)
        distributed_product_usd = Decimal("0.00")
        distributed_shipping_usd = Decimal("0.00")
        distributed_service_fee_usd = Decimal("0.00")

        target_product_total_usd = (new_total_cny / exchange_rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )
        target_shipping_total_usd = Decimal(
            "0.00"
        )  # We sum shipping in loop to be safe
        for item in all_items:
            target_shipping_total_usd += (
                Decimal(str(item.domestic_shipping_cny or 0)) / exchange_rate
            ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)

        # Recalculate component targets
        total_shipping_cny = sum(
            Decimal(str(item.domestic_shipping_cny or 0)) for item in all_items
        )
        total_product_cny = new_total_cny - total_shipping_cny

        target_product_total_usd = (total_product_cny / exchange_rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )
        target_shipping_total_usd = (total_shipping_cny / exchange_rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )

        if (target_product_total_usd + target_shipping_total_usd) != new_total_usd:
            target_product_total_usd = new_total_usd - target_shipping_total_usd

        for i, item in enumerate(all_items):
            is_last = i == len(all_items) - 1

            # 1. Product
            if is_last:
                item_product_usd = target_product_total_usd - distributed_product_usd
            else:
                item_product_cny = Decimal(str(item.unit_price_cny)) * item.quantity
                if total_product_cny > 0:
                    item_product_usd = (
                        target_product_total_usd
                        * (item_product_cny / total_product_cny)
                    ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
                else:
                    item_product_usd = Decimal("0.00")
                distributed_product_usd += item_product_usd

            item.unit_price_usd = float(
                (item_product_usd / item.quantity).quantize(
                    Decimal("0.01"), rounding=ROUND_HALF_UP
                )
            )

            # 2. Shipping
            if is_last:
                item_shipping_usd = target_shipping_total_usd - distributed_shipping_usd
            else:
                item_shipping_cny = Decimal(str(item.domestic_shipping_cny or 0))
                if total_shipping_cny > 0:
                    item_shipping_usd = (
                        target_shipping_total_usd
                        * (item_shipping_cny / total_shipping_cny)
                    ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
                else:
                    item_shipping_usd = Decimal("0.00")
                distributed_shipping_usd += item_shipping_usd

            item.domestic_shipping_usd = float(item_shipping_usd)

            # 3. Service Fee
            if is_last:
                item_service_fee = new_service_fee - distributed_service_fee_usd
            else:
                item_total_cny = (
                    Decimal(str(item.unit_price_cny)) * item.quantity
                ) + Decimal(str(item.domestic_shipping_cny or 0))
                if new_total_cny > 0:
                    item_service_fee = (
                        new_service_fee * (item_total_cny / new_total_cny)
                    ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
                else:
                    item_service_fee = Decimal("0.00")
                distributed_service_fee_usd += item_service_fee

            item.service_fee_usd = float(item_service_fee)

        # 增加订单版本号（乐观锁）
        order.version += 1

        db.commit()
        db.refresh(order)

        return order

    @staticmethod
    def confirm_proposed_price(db: Session, order_id: str, user_id: str):
        """
        User confirms the proposed price. Recalculate order with new price and continue.
        May charge additional amount or refund difference.
        """
        from app.services.notification_service import NotificationService
        from app.core.idempotency import IdempotencyHelper

        order = (
            db.query(Order)
            .filter(Order.id == order_id, Order.user_id == user_id)
            .first()
        )
        if not order:
            raise ValueError("Order not found")

        if order.status != "pending_price_confirmation":
            raise ValueError(
                f"Cannot confirm price for order in status: {order.status}"
            )

        if not order.proposed_price_cny:
            raise ValueError("No proposed price found")

        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise ValueError("User not found")

        # Idempotency check - prevent duplicate price adjustment
        idempotency_key = IdempotencyHelper.ensure_unique(
            db, "confirm_proposed_price", "order", order_id, user_id
        )

        # Get first item (we're updating based on proposed price for the whole order's items)
        items = db.query(OrderItem).filter(OrderItem.order_id == order.id).all()
        if not items:
            raise ValueError("No items in order")

        # Calculate price difference
        rate = Decimal(str(order.exchange_rate))
        old_total_cny = Decimal(str(order.total_cny))
        new_total_cny = Decimal(str(order.proposed_price_cny))

        old_total_usd = (old_total_cny / rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )
        new_total_usd = (new_total_cny / rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )

        # Recalculate service fee (10% of new price, min $5)
        # Recalculate service fee (10% or custom, min $5)
        if user.custom_service_fee_rate is not None:
            fee_rate = Decimal(str(user.custom_service_fee_rate))
        else:
            fee_rate = Decimal(str(settings.SERVICE_FEE_RATE))

        min_fee = Decimal(str(settings.MIN_SERVICE_FEE_USD))
        new_service_fee = max(new_total_usd * fee_rate, min_fee).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )

        # Calculate difference in total amount
        old_total_with_fee = Decimal(str(order.total_usd)) + Decimal(
            str(order.service_fee_usd)
        )
        new_total_with_fee = new_total_usd + new_service_fee
        difference = new_total_with_fee - old_total_with_fee

        if difference > 0:
            # Need to charge more
            if Decimal(str(user.balance_usd)) < difference:
                raise ValueError(
                    f"Insufficient balance. Need additional ${float(difference):.2f}"
                )

            user.balance_usd = float(Decimal(str(user.balance_usd)) - difference)

            transaction = Transaction(
                user_id=str(user.id),
                type="order_price_adjustment",
                amount_usd=-float(difference),
                balance_after=float(user.balance_usd),
                reference_type="order",
                reference_id=order_id,
                idempotency_key=idempotency_key,
                description=f"Additional charge for price adjustment on order {order.order_number}",
            )
            db.add(transaction)
        elif difference < 0:
            # Refund difference
            refund = abs(difference)
            user.balance_usd = float(Decimal(str(user.balance_usd)) + refund)

            transaction = Transaction(
                user_id=str(user.id),
                type="order_price_adjustment",
                amount_usd=float(refund),
                balance_after=float(user.balance_usd),
                reference_type="order",
                reference_id=order_id,
                idempotency_key=idempotency_key,
                description=f"Refund for price adjustment on order {order.order_number}",
            )
            db.add(transaction)

        # Update order
        order.total_cny = float(new_total_cny)
        order.total_usd = float(new_total_usd)
        order.service_fee_usd = float(new_service_fee)
        order.status = "processing"  # Back to queue for partner
        order.rejection_reason = None
        order.rejection_note = None
        order.proposed_price_cny = None
        order.updated_at = datetime.utcnow()

        # Update item price and distribute everything (Ironclad Ledger)
        distributed_product_usd = Decimal("0.00")
        distributed_shipping_usd = Decimal("0.00")
        distributed_service_fee_usd = Decimal("0.00")

        total_shipping_cny = sum(
            Decimal(str(item.domestic_shipping_cny or 0)) for item in items
        )
        total_product_cny = new_total_cny - total_shipping_cny

        target_product_total_usd = (total_product_cny / rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )
        target_shipping_total_usd = (total_shipping_cny / rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP
        )

        if (target_product_total_usd + target_shipping_total_usd) != new_total_usd:
            target_product_total_usd = new_total_usd - target_shipping_total_usd

        for i, item in enumerate(items):
            is_last = i == len(items) - 1

            # Update item CNY if it was single item adjustment
            if len(items) == 1:
                item.unit_price_cny = float(new_total_cny / Decimal(str(item.quantity)))

            # 1. Product Distribution
            if is_last:
                item_product_usd = target_product_total_usd - distributed_product_usd
            else:
                item_product_cny = Decimal(str(item.unit_price_cny)) * item.quantity
                if total_product_cny > 0:
                    item_product_usd = (
                        target_product_total_usd
                        * (item_product_cny / total_product_cny)
                    ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
                else:
                    item_product_usd = Decimal("0.00")
                distributed_product_usd += item_product_usd

            item.unit_price_usd = float(
                (item_product_usd / item.quantity).quantize(
                    Decimal("0.01"), rounding=ROUND_HALF_UP
                )
            )

            # 2. Shipping Distribution
            if is_last:
                item_shipping_usd = target_shipping_total_usd - distributed_shipping_usd
            else:
                item_shipping_cny = Decimal(str(item.domestic_shipping_cny or 0))
                if total_shipping_cny > 0:
                    item_shipping_usd = (
                        target_shipping_total_usd
                        * (item_shipping_cny / total_shipping_cny)
                    ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
                else:
                    item_shipping_usd = Decimal("0.00")
                distributed_shipping_usd += item_shipping_usd

            item.domestic_shipping_usd = float(item_shipping_usd)

            # 3. Service Fee Distribution
            if is_last:
                item_service_fee = new_service_fee - distributed_service_fee_usd
            else:
                item_total_cny = (
                    Decimal(str(item.unit_price_cny)) * item.quantity
                ) + Decimal(str(item.domestic_shipping_cny or 0))
                if new_total_cny > 0:
                    item_service_fee = (
                        new_service_fee * (item_total_cny / new_total_cny)
                    ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
                else:
                    item_service_fee = Decimal("0.00")
                distributed_service_fee_usd += item_service_fee

            item.service_fee_usd = float(item_service_fee)
            item.status = "processing"

        NotificationService.create_notification(
            db=db,
            user_id=str(user.id),
            notification_type="price_confirmed",
            title=f"Price Confirmed - {order.order_number}",
            message="You have confirmed the new price. Your order is back in the queue.",
            order_id=str(order.id),
        )

        # 记录审计日志
        try:
            AuditService.log_action(
                db=db,
                admin_id=user_id,
                action_type="confirm_price_change",
                target_type="order",
                target_id=str(order.id),
                description=f"用户确认价格调整：¥{old_total_cny} → ¥{new_total_cny}",
                extra_data=json.dumps({
                    "old_total_cny": float(old_total_cny),
                    "new_total_cny": float(new_total_cny),
                    "old_total_usd": float(old_total_with_fee),
                    "new_total_usd": float(new_total_with_fee),
                    "difference_usd": float(difference)
                })
            )
        except Exception as e:
            logger.error(f"Failed to log audit for price confirmation: {e}")
            # 不中断主流程

        # --- Email Hook: Notify Buyers ---
        try:
            from app.services.email_service import send_price_confirmed_notification

            # Get buyer emails
            buyer_emails = NotificationService.get_stakeholder_emails(db)

            # Calculate USD values for email
            original_usd = (old_total_cny / rate).as_integer_ratio()[0] / (
                old_total_cny / rate
            ).as_integer_ratio()[1]  # Approx or use old_total_usd
            # Better to use the variables we calculated

            send_price_confirmed_notification(
                buyer_emails=buyer_emails,
                order_number=order.order_number,
                original_price=float(old_total_with_fee),
                confirmed_price=float(new_total_with_fee),
                user_email=user.email,
                buyer_url=f"{settings.FRONTEND_URL}/admin/orders",
            )
        except Exception as e:
            print(f"Failed to send price confirmation email: {e}")

        db.commit()
        db.refresh(order)
        return order

    @staticmethod
    def reject_proposed_price(db: Session, order_id: str, user_id: str):
        """
        User rejects the proposed price. Cancel order and refund.
        """
        from app.services.notification_service import NotificationService

        order = (
            db.query(Order)
            .filter(Order.id == order_id, Order.user_id == user_id)
            .first()
        )
        if not order:
            raise ValueError("Order not found")

        if order.status != "pending_price_confirmation":
            raise ValueError(f"Cannot reject price for order in status: {order.status}")

        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise ValueError("User not found")

        # Calculate refund
        refund_amount = Decimal(str(order.total_usd)) + Decimal(
            str(order.service_fee_usd)
        )

        # Update order
        order.status = "cancelled"
        order.updated_at = datetime.utcnow()

        # Update items
        items = db.query(OrderItem).filter(OrderItem.order_id == order.id).all()
        for item in items:
            item.status = "cancelled"

        # Handle frozen deposit (Freight Collect) - unfreeze and add to refund
        frozen_usd = Decimal("0.00")
        rate = Decimal(str(order.exchange_rate))
        frozen_cny = sum(
            Decimal(str(item.freight_collect_frozen_cny or 0)) for item in items
        )

        if frozen_cny > 0:
            frozen_usd = (frozen_cny / rate).quantize(
                Decimal("0.01"), rounding=ROUND_HALF_UP
            )
            if user.frozen_usd:
                current_frozen = Decimal(str(user.frozen_usd))
                user.frozen_usd = float(
                    max(Decimal("0.00"), current_frozen - frozen_usd)
                )
            refund_amount += frozen_usd

        # Refund user
        user.balance_usd = float(Decimal(str(user.balance_usd)) + refund_amount)

        transaction = Transaction(
            user_id=str(user.id),
            type="order_refund",
            amount_usd=float(refund_amount),
            balance_after=float(user.balance_usd),
            description=f"Refund for cancelled order {order.order_number} (user rejected price)",
        )
        db.add(transaction)

        NotificationService.create_notification(
            db=db,
            user_id=str(user.id),
            notification_type="order_cancelled",
            title=f"Order Cancelled - {order.order_number}",
            message=f"You have rejected the new price. A refund of ${refund_amount:.2f} has been credited to your wallet.",
            order_id=str(order.id),
        )

        # 记录审计日志
        try:
            AuditService.log_action(
                db=db,
                admin_id=user_id,
                action_type="reject_price_change",
                target_type="order",
                target_id=str(order.id),
                description=f"用户拒绝价格调整，订单已取消",
                extra_data=json.dumps({
                    "proposed_price_cny": float(order.proposed_price_cny) if order.proposed_price_cny else None,
                    "refund_amount_usd": float(refund_amount)
                })
            )
        except Exception as e:
            logger.error(f"Failed to log audit for price rejection: {e}")
            # 不中断主流程

        db.commit()
        db.refresh(order)
        return order

    @staticmethod
    def reject_item_cannot_purchase(
        db: Session,
        item_id: str,
        partner_id: str,
        reason: str,
        note: Optional[str] = None,
        proposed_price_cny: Optional[float] = None,
    ):
        """
        Partner marks an item as cannot be purchased.
        Sets the order to 'pending_price_confirmation' status with rejection info.
        User will be notified and can accept new price or cancel.
        
        P2 Security Fix (Task 9): 添加48小时价格确认超时机制

        Args:
            reason: out_of_stock, price_mismatch, prohibited, fake_goods, other
            note: Optional text note
            proposed_price_cny: Required if reason is price_mismatch
        """
        from app.services.notification_service import NotificationService
        from datetime import timedelta

        # Validate reason
        valid_reasons = [
            "out_of_stock",
            "price_mismatch",
            "prohibited",
            "fake_goods",
            "other",
        ]
        if reason not in valid_reasons:
            raise ValueError(
                f"Invalid reason. Must be one of: {', '.join(valid_reasons)}"
            )

        # For price_mismatch, proposed_price_cny is required
        if reason == "price_mismatch" and not proposed_price_cny:
            raise ValueError(
                "proposed_price_cny is required when reason is price_mismatch"
            )

        # Get the item
        item = db.query(OrderItem).filter(OrderItem.id == item_id).first()
        if not item:
            raise ValueError("Item not found")

        # Get the order
        order = db.query(Order).filter(Order.id == item.order_id).first()
        if not order:
            raise ValueError("Order not found")

        # Verify partner has access (partner_id should match order's buyer_id or partner_id)
        if order.buyer_id != partner_id and order.partner_id != partner_id:
            raise ValueError("You don't have permission to reject this item")

        # Can only reject items that are in 'processing' status
        if item.status not in ["processing"]:
            raise ValueError(f"Cannot reject item with status: {item.status}")

        # Get the user (buyer)
        user = db.query(User).filter(User.id == order.user_id).first()
        if not user:
            raise ValueError("User not found")

        # Handle different rejection reasons differently
        if reason == "price_mismatch":
            # Price mismatch: need user confirmation
            deadline = datetime.utcnow() + timedelta(hours=48)

            order.rejection_reason = reason
            order.rejection_note = note
            order.proposed_price_cny = proposed_price_cny
            order.status = "pending_price_confirmation"
            order.price_confirmation_deadline = deadline
            order.updated_at = datetime.utcnow()

            item.status = "processing"  # Keep item processing until user confirms
            item.updated_at = datetime.utcnow()

            message = f"The actual price is ¥{proposed_price_cny} (original: ¥{item.unit_price_cny})"
            if note:
                message += f". Note: {note}"
            message += ". Please review and confirm the new price or cancel the order."

            NotificationService.create_notification(
                db=db,
                user_id=str(user.id),
                notification_type="price_confirmation_required",
                title=f"Price Change - Order {order.order_number}",
                message=message,
                order_id=str(order.id),
            )

            # 记录审计日志
            try:
                AuditService.log_action(
                    db=db,
                    admin_id=partner_id,
                    action_type="propose_price_change",
                    target_type="order",
                    target_id=str(order.id),
                    description=f"合伙人提出价格调整：¥{item.unit_price_cny} → ¥{proposed_price_cny}",
                    extra_data=json.dumps({
                        "old_price_cny": float(item.unit_price_cny),
                        "new_price_cny": proposed_price_cny,
                        "reason": reason,
                        "note": note,
                        "item_id": item_id
                    })
                )
            except Exception as e:
                logger.error(f"Failed to log audit for price change: {e}")
                # 不中断主流程
        else:
            # Other reasons: directly reject the item
            order.rejection_reason = reason
            order.rejection_note = note
            order.status = "rejected"
            order.updated_at = datetime.utcnow()

            item.status = "rejected"
            item.updated_at = datetime.utcnow()

            reason_messages = {
                "out_of_stock": "The item is out of stock and cannot be purchased",
                "prohibited": "This item cannot be shipped internationally due to customs restrictions",
                "fake_goods": "This item appears to be counterfeit and cannot be purchased",
                "other": "There is an issue with this item",
            }
            message = reason_messages.get(reason, f"Issue: {reason}")
            if note:
                message += f". Note: {note}"
            message += ". Your payment will be refunded."

            NotificationService.create_notification(
                db=db,
                user_id=str(user.id),
                notification_type="order_rejected",
                title=f"Order Rejected - {order.order_number}",
                message=message,
                order_id=str(order.id),
            )

            # 记录审计日志
            try:
                action_type_map = {
                    "out_of_stock": "reject_item_out_of_stock",
                    "prohibited": "reject_item_prohibited",
                    "fake_goods": "reject_item_fake_goods",
                    "other": "reject_item_other"
                }
                action_type = action_type_map.get(reason, "reject_item")

                AuditService.log_action(
                    db=db,
                    admin_id=partner_id,
                    action_type=action_type,
                    target_type="order",
                    target_id=str(order.id),
                    description=f"合伙人拒绝商品：{reason_messages.get(reason, reason)}",
                    extra_data=json.dumps({
                        "reason": reason,
                        "note": note,
                        "item_id": item_id
                    })
                )
            except Exception as e:
                logger.error(f"Failed to log audit for item rejection: {e}")
                # 不中断主流程
            order.rejection_note = note
            order.status = "rejected"
            order.updated_at = datetime.utcnow()

            item.status = "rejected"
            item.updated_at = datetime.utcnow()

            reason_messages = {
                "out_of_stock": "The item is out of stock and cannot be purchased",
                "prohibited": "This item cannot be shipped internationally due to customs restrictions",
                "fake_goods": "This item appears to be counterfeit and cannot be purchased",
                "other": "There is an issue with this item",
            }
            message = reason_messages.get(reason, f"Issue: {reason}")
            if note:
                message += f". Note: {note}"
            message += ". Your payment will be refunded."

            NotificationService.create_notification(
                db=db,
                user_id=str(user.id),
                notification_type="order_rejected",
                title=f"Order Rejected - {order.order_number}",
                message=message,
                order_id=str(order.id),
            )

            # Refund user - match the logic from BuyerService.reject_order
            from app.models.transaction import Transaction
            from decimal import ROUND_HALF_UP

            refund_amount = Decimal(str(order.total_usd)) + Decimal(str(order.service_fee_usd))

            # Handle frozen deposit (Freight Collect) - unfreeze and add to refund
            rate = Decimal(str(order.exchange_rate))
            frozen_cny = sum(
                Decimal(str(i.freight_collect_frozen_cny or 0)) for i in order.items
            )

            if frozen_cny > 0:
                frozen_usd = (frozen_cny / rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
                if user.frozen_usd:
                    current_frozen = Decimal(str(user.frozen_usd))
                    user.frozen_usd = float(max(Decimal("0.00"), current_frozen - frozen_usd))
                refund_amount += frozen_usd

            # Refund user
            user.balance_usd = float(Decimal(str(user.balance_usd)) + refund_amount)

            # Create refund transaction
            reason_text = f"{reason}: {note}" if note else reason
            transaction = Transaction(
                user_id=str(user.id),
                type="order_refund",
                amount_usd=float(refund_amount),
                balance_after=float(user.balance_usd),
                description=f"Refund for rejected order {order.order_number}. Reason: {reason_text}",
            )
            db.add(transaction)

        db.commit()
        db.refresh(order)

        return {"order": order, "item": item}

    @staticmethod
    def adjust_order_amount(
        db: Session,
        order_id: str,
        refund_amount: float,
        reason: str,
        admin_id: str
    ) -> Order:
        """
        管理员调整订单金额（部分退款）
        退款后订单状态变为 cancelled
        """
        # 1. 验证订单存在
        order = db.query(Order).filter(Order.id == order_id).first()
        if not order:
            logger.error(f"Order not found: {order_id}")
            raise ValueError("Order not found")

        # 2. 验证退款金额不超过订单总额
        order_total = Decimal(str(order.total_usd)) + Decimal(str(order.service_fee_usd))
        refund_decimal = Decimal(str(refund_amount))

        if refund_decimal > order_total:
            logger.error(f"Refund amount {refund_amount} exceeds order total {order_total}")
            raise ValueError(f"Refund amount cannot exceed order total (${order_total})")

        # 3. 获取用户
        user = db.query(User).filter(User.id == order.user_id).first()
        if not user:
            logger.error(f"User not found: {order.user_id}")
            raise ValueError("User not found")

        # 4. 更新订单字段
        order.refund_amount = float(refund_decimal)
        order.refund_reason = reason
        order.refunded_at = datetime.utcnow()
        order.refunded_by = admin_id
        order.status = "cancelled"  # 退款后关闭订单

        # 5. 退款到用户余额
        user.balance_usd = float(Decimal(str(user.balance_usd)) + refund_decimal)

        # 6. 创建退款交易记录
        transaction = Transaction(
            user_id=str(user.id),
            type="admin_refund",
            amount_usd=float(refund_decimal),
            balance_after=float(user.balance_usd),
            description=f"Admin refund for order {order.order_number}. Reason: {reason}",
        )
        db.add(transaction)

        # 7. 记录日志
        logger.info(
            f"Admin {admin_id} adjusted order {order.order_number}: "
            f"refunded ${refund_amount}, reason: {reason}"
        )

        # 8. 发送通知给买家
        try:
            NotificationService.notify_order_amount_adjusted(db, order)
        except Exception as e:
            logger.error(f"Failed to send refund notification: {e}")
            # 不中断流程，但记录错误

        db.commit()
        db.refresh(order)

        return order
