package postgres

import (
	"context"

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

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

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

func (s *RechargeOrderStore) Create(ctx context.Context, order *model.RechargeOrder) error {
	return s.db.WithContext(ctx).Create(order).Error
}

func (s *RechargeOrderStore) CreateWithTx(ctx context.Context, tx *gorm.DB, order *model.RechargeOrder) error {
	return tx.WithContext(ctx).Create(order).Error
}

func (s *RechargeOrderStore) GetByRechargeOrderNo(ctx context.Context, orderNo string) (*model.RechargeOrder, error) {
	var order model.RechargeOrder
	err := s.db.WithContext(ctx).
		Where("recharge_order_no = ?", orderNo).
		First(&order).Error
	if err != nil {
		return nil, err
	}
	return &order, nil
}

func (s *RechargeOrderStore) GetByID(ctx context.Context, id uint) (*model.RechargeOrder, error) {
	var order model.RechargeOrder
	err := s.db.WithContext(ctx).First(&order, id).Error
	if err != nil {
		return nil, err
	}
	return &order, nil
}

func (s *RechargeOrderStore) UpdateStatus(ctx context.Context, id uint, status int) error {
	return s.db.WithContext(ctx).
		Model(&model.RechargeOrder{}).
		Where("id = ?", id).
		Update("status", status).Error
}

func (s *RechargeOrderStore) UpdateStatusWithTx(ctx context.Context, tx *gorm.DB, id uint, status int) error {
	return tx.WithContext(ctx).
		Model(&model.RechargeOrder{}).
		Where("id = ?", id).
		Update("status", status).Error
}

func (s *RechargeOrderStore) UpdateStatusWithOptimisticLock(ctx context.Context, id uint, oldStatus *int, newStatus int, paidAt interface{}) error {
	return s.UpdateStatusWithOptimisticLockDB(ctx, s.db, id, oldStatus, newStatus, paidAt)
}

func (s *RechargeOrderStore) UpdateStatusWithOptimisticLockDB(ctx context.Context, db *gorm.DB, id uint, oldStatus *int, newStatus int, paidAt interface{}) error {
	updates := map[string]interface{}{
		"status": newStatus,
	}
	if paidAt != nil {
		updates["paid_at"] = paidAt
	}

	query := db.WithContext(ctx).Model(&model.RechargeOrder{}).Where("id = ?", id)

	if oldStatus != nil {
		query = query.Where("status = ?", *oldStatus)
	}

	result := query.Updates(updates)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return gorm.ErrRecordNotFound
	}
	return nil
}

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

func (s *RechargeOrderStore) ListByCard(ctx context.Context, iotCardID uint, generation int, offset, limit int) ([]*model.RechargeOrder, error) {
	var orders []*model.RechargeOrder
	query := s.db.WithContext(ctx).
		Where("iot_card_id = ? AND generation = ?", iotCardID, generation)

	if err := query.Order("created_at DESC").Offset(offset).Limit(limit).Find(&orders).Error; err != nil {
		return nil, err
	}
	return orders, nil
}

func (s *RechargeOrderStore) ListByDevice(ctx context.Context, deviceID uint, generation int, offset, limit int) ([]*model.RechargeOrder, error) {
	var orders []*model.RechargeOrder
	query := s.db.WithContext(ctx).
		Where("device_id = ? AND generation = ?", deviceID, generation)

	if err := query.Order("created_at DESC").Offset(offset).Limit(limit).Find(&orders).Error; err != nil {
		return nil, err
	}
	return orders, nil
}

func (s *RechargeOrderStore) ListByUserID(ctx context.Context, userID uint, offset, limit int) ([]*model.RechargeOrder, error) {
	var orders []*model.RechargeOrder
	err := s.db.WithContext(ctx).
		Where("user_id = ?", userID).
		Order("created_at DESC").
		Offset(offset).
		Limit(limit).
		Find(&orders).Error
	if err != nil {
		return nil, err
	}
	return orders, nil
}

type ListRechargeOrderParams struct {
	Page         int
	PageSize     int
	UserID       *uint
	IotCardID    *uint
	DeviceID     *uint
	Status       *int
	StartTime    interface{}
	EndTime      interface{}
	ResourceType *string
	ResourceID   *uint
}

func (s *RechargeOrderStore) List(ctx context.Context, params *ListRechargeOrderParams) ([]*model.RechargeOrder, int64, error) {
	var orders []*model.RechargeOrder
	var total int64

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

	if params.UserID != nil {
		query = query.Where("user_id = ?", *params.UserID)
	}
	if params.IotCardID != nil {
		query = query.Where("iot_card_id = ?", *params.IotCardID)
	}
	if params.DeviceID != nil {
		query = query.Where("device_id = ?", *params.DeviceID)
	}
	if params.Status != nil {
		query = query.Where("status = ?", *params.Status)
	}
	if params.ResourceType != nil && params.ResourceID != nil {
		query = query.Where("resource_type = ? AND resource_id = ?", *params.ResourceType, *params.ResourceID)
	}
	if params.StartTime != nil {
		query = query.Where("created_at >= ?", params.StartTime)
	}
	if params.EndTime != nil {
		query = query.Where("created_at <= ?", params.EndTime)
	}

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

	page := params.Page
	if page < 1 {
		page = 1
	}
	pageSize := params.PageSize
	if pageSize < 1 {
		pageSize = 20
	}

	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
}
