package postgres

import (
	"context"
	"fmt"
	"math/rand"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/store"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
	"github.com/redis/go-redis/v9"
	"gorm.io/gorm"
)

type OrderStore struct {
	db    *gorm.DB
	redis *redis.Client
}

func NewOrderStore(db *gorm.DB, redis *redis.Client) *OrderStore {
	return &OrderStore{
		db:    db,
		redis: redis,
	}
}

func (s *OrderStore) Create(ctx context.Context, order *model.Order, items []*model.OrderItem) error {
	return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if err := tx.Create(order).Error; err != nil {
			return err
		}

		for _, item := range items {
			item.OrderID = order.ID
			if err := tx.Create(item).Error; err != nil {
				return err
			}
		}

		return nil
	})
}

func (s *OrderStore) GetByID(ctx context.Context, id uint) (*model.Order, error) {
	var order model.Order
	query := s.db.WithContext(ctx).Where("id = ?", id)
	query = s.applyOrderScope(ctx, query)
	if err := query.First(&order).Error; err != nil {
		return nil, err
	}
	return &order, nil
}

func (s *OrderStore) GetByIDWithItems(ctx context.Context, id uint) (*model.Order, []*model.OrderItem, error) {
	var order model.Order
	query := s.db.WithContext(ctx).Where("id = ?", id)
	query = s.applyOrderScope(ctx, query)
	if err := query.First(&order).Error; err != nil {
		return nil, nil, err
	}

	var items []*model.OrderItem
	if err := s.db.WithContext(ctx).Where("order_id = ?", id).Find(&items).Error; err != nil {
		return nil, nil, err
	}

	return &order, items, nil
}

func (s *OrderStore) GetByOrderNo(ctx context.Context, orderNo string) (*model.Order, error) {
	var order model.Order
	query := s.db.WithContext(ctx).Where("order_no = ?", orderNo)
	query = s.applyOrderScope(ctx, query)
	if err := query.First(&order).Error; err != nil {
		return nil, err
	}
	return &order, nil
}

func (s *OrderStore) Update(ctx context.Context, order *model.Order) error {
	return s.db.WithContext(ctx).Save(order).Error
}

func (s *OrderStore) List(ctx context.Context, opts *store.QueryOptions, filters map[string]any) ([]*model.Order, int64, error) {
	var orders []*model.Order
	var total int64

	query := s.db.WithContext(ctx).Model(&model.Order{})

	query = s.applyOrderScope(ctx, query)

	if v, ok := filters["payment_status"]; ok {
		query = query.Where("payment_status = ?", v)
	}
	if v, ok := filters["order_type"]; ok {
		query = query.Where("order_type = ?", v)
	}
	if v, ok := filters["payment_method"]; ok {
		query = query.Where("payment_method = ?", v)
	}
	if v, ok := filters["order_no"]; ok {
		query = query.Where("order_no = ?", v)
	}
	if v, ok := filters["buyer_type"]; ok {
		query = query.Where("buyer_type = ?", v)
	}
	if v, ok := filters["buyer_id"]; ok {
		query = query.Where("buyer_id = ?", v)
	}
	if v, ok := filters["purchase_role"]; ok {
		query = query.Where("purchase_role = ?", v)
	}
	identifierValue, hasIdentifier := filters["identifier"]
	iotCardIDValue, hasIotCardID := filters["iot_card_id"]
	deviceIDValue, hasDeviceID := filters["device_id"]
	if hasIdentifier || hasIotCardID || hasDeviceID {
		assetQuery := s.db
		hasAnyAssetCondition := false
		if hasIdentifier {
			assetQuery = assetQuery.Where("asset_identifier = ?", identifierValue)
			hasAnyAssetCondition = true
		}
		if hasIotCardID {
			if hasAnyAssetCondition {
				assetQuery = assetQuery.Or("iot_card_id = ?", iotCardIDValue)
			} else {
				assetQuery = assetQuery.Where("iot_card_id = ?", iotCardIDValue)
				hasAnyAssetCondition = true
			}
		}
		if hasDeviceID {
			if hasAnyAssetCondition {
				assetQuery = assetQuery.Or("device_id = ?", deviceIDValue)
			} else {
				assetQuery = assetQuery.Where("device_id = ?", deviceIDValue)
			}
		}
		query = query.Where(assetQuery)
	}
	if v, ok := filters["seller_shop_id"]; ok {
		query = query.Where("seller_shop_id = ?", v)
	}
	if v, ok := filters["start_time"]; ok {
		query = query.Where("created_at >= ?", v)
	}
	if v, ok := filters["end_time"]; ok {
		query = query.Where("created_at <= ?", v)
	}
	if v, ok := filters["buyer_phone"]; ok {
		query = query.Where("buyer_phone = ?", v)
	}
	if v, ok := filters["is_expired"]; ok {
		isExpired, _ := v.(bool)
		if isExpired {
			// 已过期：expires_at 不为空且小于当前时间，且订单仍为待支付状态
			query = query.Where("expires_at IS NOT NULL AND expires_at <= NOW() AND payment_status = ?", model.PaymentStatusPending)
		} else {
			// 未过期：expires_at 为空或 expires_at 大于当前时间
			query = query.Where("expires_at IS NULL OR expires_at > NOW()")
		}
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	if opts == nil {
		opts = store.DefaultQueryOptions()
	}

	offset := (opts.Page - 1) * opts.PageSize
	if opts.OrderBy != "" {
		query = query.Order(opts.OrderBy)
	} else {
		query = query.Order("id DESC")
	}

	if err := query.Offset(offset).Limit(opts.PageSize).Find(&orders).Error; err != nil {
		return nil, 0, err
	}

	return orders, total, nil
}

func (s *OrderStore) UpdatePaymentStatus(ctx context.Context, id uint, status int, paidAt *time.Time, expiresAt ...*time.Time) error {
	updates := map[string]any{
		"payment_status": status,
	}
	if paidAt != nil {
		updates["paid_at"] = paidAt
	}
	// 支持可选的 expiresAt 参数，用于支付成功后清除过期时间或取消时清除过期时间
	if len(expiresAt) > 0 {
		updates["expires_at"] = expiresAt[0]
	}
	return s.db.WithContext(ctx).Model(&model.Order{}).Where("id = ?", id).Updates(updates).Error
}

func (s *OrderStore) applyOrderScope(ctx context.Context, query *gorm.DB) *gorm.DB {
	shopIDs := middleware.GetSubordinateShopIDs(ctx)
	if len(shopIDs) == 0 {
		return query
	}

	return query.Where(
		s.db.Where("buyer_type = ? AND buyer_id IN ?", model.BuyerTypeAgent, shopIDs).
			Or("seller_shop_id IN ?", shopIDs).
			Or(s.db.Where("seller_shop_id IS NULL AND operator_type = ? AND operator_id IN ?", model.OperatorAccountTypeAgent, shopIDs)),
	)
}

func (s *OrderStore) GenerateOrderNo() string {
	now := time.Now()
	randomNum := rand.Intn(1000000)
	return fmt.Sprintf("ORD%s%06d", now.Format("20060102150405"), randomNum)
}

// ListByAssetIdentifier 按资产标识符分页查询订单（倒序）
// 用于资产历史订单查询，直接走 asset_identifier 快照字段精确匹配
// 数据权限已在调用方（asset service）的 Resolve 环节检查，此处不重复过滤
func (s *OrderStore) ListByAssetIdentifier(ctx context.Context, identifier string, page, pageSize int) ([]*model.Order, int64, error) {
	var orders []*model.Order
	var total int64

	query := s.db.WithContext(ctx).Model(&model.Order{}).
		Where("asset_identifier = ?", identifier)

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	if err := query.Order("id DESC").Offset(offset).Limit(pageSize).Find(&orders).Error; err != nil {
		return nil, 0, err
	}

	return orders, total, nil
}

// FindExpiredOrders 查询已超时的待支付订单
// 查询条件：expires_at <= NOW() AND payment_status = 1（待支付）
// limit 参数限制每次批量处理的数量，避免一次性加载太多数据
func (s *OrderStore) FindExpiredOrders(ctx context.Context, limit int) ([]*model.Order, error) {
	var orders []*model.Order
	err := s.db.WithContext(ctx).
		Where("expires_at IS NOT NULL AND expires_at <= NOW() AND payment_status = ?", model.PaymentStatusPending).
		Order("expires_at ASC").
		Limit(limit).
		Find(&orders).Error
	if err != nil {
		return nil, err
	}
	return orders, nil
}
