package repository

import (
	"apskel-pos-be/internal/entities"
	"context"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

type OrderIngredientTransactionRepository interface {
	Create(ctx context.Context, transaction *entities.OrderIngredientTransaction) error
	GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.OrderIngredientTransaction, error)
	Update(ctx context.Context, transaction *entities.OrderIngredientTransaction) error
	Delete(ctx context.Context, id, organizationID uuid.UUID) error
	List(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*entities.OrderIngredientTransaction, int64, error)
	GetByOrderID(ctx context.Context, orderID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error)
	GetByOrderItemID(ctx context.Context, orderItemID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error)
	GetByIngredientID(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error)
	GetSummary(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}) ([]*entities.OrderIngredientTransaction, error)
	BulkCreate(ctx context.Context, transactions []*entities.OrderIngredientTransaction) error
}

type OrderIngredientTransactionRepositoryImpl struct {
	db *gorm.DB
}

func NewOrderIngredientTransactionRepositoryImpl(db *gorm.DB) OrderIngredientTransactionRepository {
	return &OrderIngredientTransactionRepositoryImpl{db: db}
}

func (r *OrderIngredientTransactionRepositoryImpl) Create(ctx context.Context, transaction *entities.OrderIngredientTransaction) error {
	return r.db.WithContext(ctx).Create(transaction).Error
}

func (r *OrderIngredientTransactionRepositoryImpl) GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.OrderIngredientTransaction, error) {
	var transaction entities.OrderIngredientTransaction
	err := r.db.WithContext(ctx).
		Where("id = ? AND organization_id = ?", id, organizationID).
		Preload("Organization").
		Preload("Outlet").
		Preload("Order").
		Preload("OrderItem").
		Preload("Product").
		Preload("ProductVariant").
		Preload("Ingredient").
		Preload("CreatedByUser").
		First(&transaction).Error
	if err != nil {
		return nil, err
	}
	return &transaction, nil
}

func (r *OrderIngredientTransactionRepositoryImpl) Update(ctx context.Context, transaction *entities.OrderIngredientTransaction) error {
	return r.db.WithContext(ctx).Save(transaction).Error
}

func (r *OrderIngredientTransactionRepositoryImpl) Delete(ctx context.Context, id, organizationID uuid.UUID) error {
	return r.db.WithContext(ctx).
		Where("id = ? AND organization_id = ?", id, organizationID).
		Delete(&entities.OrderIngredientTransaction{}).Error
}

func (r *OrderIngredientTransactionRepositoryImpl) List(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*entities.OrderIngredientTransaction, int64, error) {
	var transactions []*entities.OrderIngredientTransaction
	var total int64

	query := r.db.WithContext(ctx).Model(&entities.OrderIngredientTransaction{}).
		Where("organization_id = ?", organizationID)

	// Apply filters
	for key, value := range filters {
		switch key {
		case "order_id":
			if orderID, ok := value.(uuid.UUID); ok {
				query = query.Where("order_id = ?", orderID)
			}
		case "order_item_id":
			if orderItemID, ok := value.(uuid.UUID); ok {
				query = query.Where("order_item_id = ?", orderItemID)
			}
		case "product_id":
			if productID, ok := value.(uuid.UUID); ok {
				query = query.Where("product_id = ?", productID)
			}
		case "product_variant_id":
			if productVariantID, ok := value.(uuid.UUID); ok {
				query = query.Where("product_variant_id = ?", productVariantID)
			}
		case "ingredient_id":
			if ingredientID, ok := value.(uuid.UUID); ok {
				query = query.Where("ingredient_id = ?", ingredientID)
			}
		case "start_date":
			if startDate, ok := value.(string); ok {
				query = query.Where("transaction_date >= ?", startDate)
			}
		case "end_date":
			if endDate, ok := value.(string); ok {
				query = query.Where("transaction_date <= ?", endDate)
			}
		}
	}

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

	// Apply pagination and get results
	offset := (page - 1) * limit
	err := query.
		Preload("Organization").
		Preload("Outlet").
		Preload("Order").
		Preload("OrderItem").
		Preload("Product").
		Preload("ProductVariant").
		Preload("Ingredient").
		Preload("CreatedByUser").
		Order("created_at DESC").
		Offset(offset).
		Limit(limit).
		Find(&transactions).Error

	return transactions, total, err
}

func (r *OrderIngredientTransactionRepositoryImpl) GetByOrderID(ctx context.Context, orderID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error) {
	var transactions []*entities.OrderIngredientTransaction
	err := r.db.WithContext(ctx).
		Where("order_id = ? AND organization_id = ?", orderID, organizationID).
		Preload("Organization").
		Preload("Outlet").
		Preload("Order").
		Preload("OrderItem").
		Preload("Product").
		Preload("ProductVariant").
		Preload("Ingredient").
		Preload("CreatedByUser").
		Order("created_at ASC").
		Find(&transactions).Error
	return transactions, err
}

func (r *OrderIngredientTransactionRepositoryImpl) GetByOrderItemID(ctx context.Context, orderItemID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error) {
	var transactions []*entities.OrderIngredientTransaction
	err := r.db.WithContext(ctx).
		Where("order_item_id = ? AND organization_id = ?", orderItemID, organizationID).
		Preload("Organization").
		Preload("Outlet").
		Preload("Order").
		Preload("OrderItem").
		Preload("Product").
		Preload("ProductVariant").
		Preload("Ingredient").
		Preload("CreatedByUser").
		Order("created_at ASC").
		Find(&transactions).Error
	return transactions, err
}

func (r *OrderIngredientTransactionRepositoryImpl) GetByIngredientID(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error) {
	var transactions []*entities.OrderIngredientTransaction
	err := r.db.WithContext(ctx).
		Where("ingredient_id = ? AND organization_id = ?", ingredientID, organizationID).
		Preload("Organization").
		Preload("Outlet").
		Preload("Order").
		Preload("OrderItem").
		Preload("Product").
		Preload("ProductVariant").
		Preload("Ingredient").
		Preload("CreatedByUser").
		Order("created_at DESC").
		Find(&transactions).Error
	return transactions, err
}

func (r *OrderIngredientTransactionRepositoryImpl) GetSummary(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}) ([]*entities.OrderIngredientTransaction, error) {
	var transactions []*entities.OrderIngredientTransaction

	query := r.db.WithContext(ctx).Model(&entities.OrderIngredientTransaction{}).
		Where("organization_id = ?", organizationID)

	// Apply filters
	for key, value := range filters {
		switch key {
		case "order_id":
			if orderID, ok := value.(uuid.UUID); ok {
				query = query.Where("order_id = ?", orderID)
			}
		case "order_item_id":
			if orderItemID, ok := value.(uuid.UUID); ok {
				query = query.Where("order_item_id = ?", orderItemID)
			}
		case "product_id":
			if productID, ok := value.(uuid.UUID); ok {
				query = query.Where("product_id = ?", productID)
			}
		case "product_variant_id":
			if productVariantID, ok := value.(uuid.UUID); ok {
				query = query.Where("product_variant_id = ?", productVariantID)
			}
		case "ingredient_id":
			if ingredientID, ok := value.(uuid.UUID); ok {
				query = query.Where("ingredient_id = ?", ingredientID)
			}
		case "start_date":
			if startDate, ok := value.(string); ok {
				query = query.Where("transaction_date >= ?", startDate)
			}
		case "end_date":
			if endDate, ok := value.(string); ok {
				query = query.Where("transaction_date <= ?", endDate)
			}
		}
	}

	err := query.
		Preload("Ingredient").
		Order("ingredient_id, created_at ASC").
		Find(&transactions).Error

	return transactions, err
}

func (r *OrderIngredientTransactionRepositoryImpl) BulkCreate(ctx context.Context, transactions []*entities.OrderIngredientTransaction) error {
	if len(transactions) == 0 {
		return nil
	}
	return r.db.WithContext(ctx).CreateInBatches(transactions, 100).Error
}
