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/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
	if err := s.db.WithContext(ctx).First(&order, id).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
	if err := s.db.WithContext(ctx).First(&order, id).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
	if err := s.db.WithContext(ctx).Where("order_no = ?", orderNo).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{})

	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["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["iot_card_id"]; ok {
		query = query.Where("iot_card_id = ?", v)
	}
	if v, ok := filters["device_id"]; ok {
		query = query.Where("device_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 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) error {
	updates := map[string]any{
		"payment_status": status,
	}
	if paidAt != nil {
		updates["paid_at"] = paidAt
	}
	return s.db.WithContext(ctx).Model(&model.Order{}).Where("id = ?", id).Updates(updates).Error
}

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