package postgres

import (
	"context"
	"fmt"
	"time"

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

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

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

func (s *AssetAllocationRecordStore) Create(ctx context.Context, record *model.AssetAllocationRecord) error {
	return s.db.WithContext(ctx).Create(record).Error
}

func (s *AssetAllocationRecordStore) BatchCreate(ctx context.Context, records []*model.AssetAllocationRecord) error {
	if len(records) == 0 {
		return nil
	}
	return s.db.WithContext(ctx).CreateInBatches(records, 100).Error
}

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

func (s *AssetAllocationRecordStore) GetByAllocationNo(ctx context.Context, allocationNo string) (*model.AssetAllocationRecord, error) {
	var record model.AssetAllocationRecord
	if err := s.db.WithContext(ctx).Where("allocation_no = ?", allocationNo).First(&record).Error; err != nil {
		return nil, err
	}
	return &record, nil
}

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

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

	if allocationType, ok := filters["allocation_type"].(string); ok && allocationType != "" {
		query = query.Where("allocation_type = ?", allocationType)
	}
	if assetType, ok := filters["asset_type"].(string); ok && assetType != "" {
		query = query.Where("asset_type = ?", assetType)
	}
	if assetIdentifier, ok := filters["asset_identifier"].(string); ok && assetIdentifier != "" {
		query = query.Where("asset_identifier LIKE ?", "%"+assetIdentifier+"%")
	}
	if allocationNo, ok := filters["allocation_no"].(string); ok && allocationNo != "" {
		query = query.Where("allocation_no = ?", allocationNo)
	}
	if fromShopID, ok := filters["from_shop_id"].(uint); ok && fromShopID > 0 {
		query = query.Where("from_owner_type = ? AND from_owner_id = ?", constants.OwnerTypeShop, fromShopID)
	}
	if toShopID, ok := filters["to_shop_id"].(uint); ok && toShopID > 0 {
		query = query.Where("to_owner_type = ? AND to_owner_id = ?", constants.OwnerTypeShop, toShopID)
	}
	if operatorID, ok := filters["operator_id"].(uint); ok && operatorID > 0 {
		query = query.Where("operator_id = ?", operatorID)
	}
	if createdAtStart, ok := filters["created_at_start"].(time.Time); ok {
		query = query.Where("created_at >= ?", createdAtStart)
	}
	if createdAtEnd, ok := filters["created_at_end"].(time.Time); ok {
		query = query.Where("created_at <= ?", createdAtEnd)
	}
	if relatedShopIDs, ok := filters["related_shop_ids"].([]uint); ok && len(relatedShopIDs) > 0 {
		query = query.Where(
			"(from_owner_type = ? AND from_owner_id IN ?) OR (to_owner_type = ? AND to_owner_id IN ?)",
			constants.OwnerTypeShop, relatedShopIDs,
			constants.OwnerTypeShop, relatedShopIDs,
		)
	}

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

	if opts == nil {
		opts = &store.QueryOptions{
			Page:     1,
			PageSize: constants.DefaultPageSize,
		}
	}
	offset := (opts.Page - 1) * opts.PageSize
	query = query.Offset(offset).Limit(opts.PageSize)

	if opts.OrderBy != "" {
		query = query.Order(opts.OrderBy)
	} else {
		query = query.Order("created_at DESC")
	}

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

	return records, total, nil
}

func (s *AssetAllocationRecordStore) GenerateAllocationNo(ctx context.Context, allocationType string) string {
	prefix := "AL"
	if allocationType == constants.AssetAllocationTypeRecall {
		prefix = "RC"
	}
	return fmt.Sprintf("%s%s%d", prefix, time.Now().Format("20060102150405"), time.Now().UnixNano()%10000)
}
