package repository

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

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

type IngredientUnitConverterRepository interface {
	Create(ctx context.Context, converter *entities.IngredientUnitConverter) error
	GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.IngredientUnitConverter, error)
	GetByIDAndOrganizationID(ctx context.Context, id, organizationID uuid.UUID) (*entities.IngredientUnitConverter, error)
	Update(ctx context.Context, converter *entities.IngredientUnitConverter) 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.IngredientUnitConverter, int, error)
	GetByIngredientAndUnits(ctx context.Context, ingredientID, fromUnitID, toUnitID, organizationID uuid.UUID) (*entities.IngredientUnitConverter, error)
	GetConvertersForIngredient(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*entities.IngredientUnitConverter, error)
	GetActiveConverters(ctx context.Context, organizationID uuid.UUID) ([]*entities.IngredientUnitConverter, error)
	ConvertQuantity(ctx context.Context, ingredientID, fromUnitID, toUnitID, organizationID uuid.UUID, quantity float64) (float64, error)
}

type IngredientUnitConverterRepositoryImpl struct {
	db *gorm.DB
}

func NewIngredientUnitConverterRepositoryImpl(db *gorm.DB) IngredientUnitConverterRepository {
	return &IngredientUnitConverterRepositoryImpl{
		db: db,
	}
}

func (r *IngredientUnitConverterRepositoryImpl) Create(ctx context.Context, converter *entities.IngredientUnitConverter) error {
	return r.db.WithContext(ctx).Create(converter).Error
}

func (r *IngredientUnitConverterRepositoryImpl) GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.IngredientUnitConverter, error) {
	var converter entities.IngredientUnitConverter
	err := r.db.WithContext(ctx).
		Where("id = ? AND organization_id = ?", id, organizationID).
		Preload("Ingredient").
		Preload("FromUnit").
		Preload("ToUnit").
		Preload("CreatedByUser").
		Preload("UpdatedByUser").
		First(&converter).Error
	if err != nil {
		return nil, err
	}
	return &converter, nil
}

func (r *IngredientUnitConverterRepositoryImpl) GetByIDAndOrganizationID(ctx context.Context, id, organizationID uuid.UUID) (*entities.IngredientUnitConverter, error) {
	return r.GetByID(ctx, id, organizationID)
}

func (r *IngredientUnitConverterRepositoryImpl) Update(ctx context.Context, converter *entities.IngredientUnitConverter) error {
	return r.db.WithContext(ctx).Save(converter).Error
}

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

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

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

	// Apply filters
	if ingredientID, ok := filters["ingredient_id"].(uuid.UUID); ok {
		query = query.Where("ingredient_id = ?", ingredientID)
	}
	if fromUnitID, ok := filters["from_unit_id"].(uuid.UUID); ok {
		query = query.Where("from_unit_id = ?", fromUnitID)
	}
	if toUnitID, ok := filters["to_unit_id"].(uuid.UUID); ok {
		query = query.Where("to_unit_id = ?", toUnitID)
	}
	if isActive, ok := filters["is_active"].(bool); ok {
		query = query.Where("is_active = ?", isActive)
	}
	if search, ok := filters["search"].(string); ok && search != "" {
		query = query.Joins("LEFT JOIN ingredients ON ingredient_unit_converters.ingredient_id = ingredients.id").
			Joins("LEFT JOIN units AS from_units ON ingredient_unit_converters.from_unit_id = from_units.id").
			Joins("LEFT JOIN units AS to_units ON ingredient_unit_converters.to_unit_id = to_units.id").
			Where("ingredients.name ILIKE ? OR from_units.name ILIKE ? OR to_units.name ILIKE ?",
				"%"+search+"%", "%"+search+"%", "%"+search+"%")
	}

	// 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("Ingredient").
		Preload("FromUnit").
		Preload("ToUnit").
		Preload("CreatedByUser").
		Preload("UpdatedByUser").
		Order("created_at DESC").
		Offset(offset).
		Limit(limit).
		Find(&converters).Error

	return converters, int(total), err
}

func (r *IngredientUnitConverterRepositoryImpl) GetByIngredientAndUnits(ctx context.Context, ingredientID, fromUnitID, toUnitID, organizationID uuid.UUID) (*entities.IngredientUnitConverter, error) {
	var converter entities.IngredientUnitConverter
	err := r.db.WithContext(ctx).
		Where("ingredient_id = ? AND from_unit_id = ? AND to_unit_id = ? AND organization_id = ? AND is_active = ?",
			ingredientID, fromUnitID, toUnitID, organizationID, true).
		Preload("Ingredient").
		Preload("FromUnit").
		Preload("ToUnit").
		First(&converter).Error
	if err != nil {
		return nil, err
	}
	return &converter, nil
}

func (r *IngredientUnitConverterRepositoryImpl) GetConvertersForIngredient(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*entities.IngredientUnitConverter, error) {
	var converters []*entities.IngredientUnitConverter
	err := r.db.WithContext(ctx).
		Where("ingredient_id = ? AND organization_id = ? AND is_active = ?", ingredientID, organizationID, true).
		Preload("FromUnit").
		Preload("ToUnit").
		Find(&converters).Error
	return converters, err
}

func (r *IngredientUnitConverterRepositoryImpl) GetActiveConverters(ctx context.Context, organizationID uuid.UUID) ([]*entities.IngredientUnitConverter, error) {
	var converters []*entities.IngredientUnitConverter
	err := r.db.WithContext(ctx).
		Where("organization_id = ? AND is_active = ?", organizationID, true).
		Preload("Ingredient").
		Preload("FromUnit").
		Preload("ToUnit").
		Find(&converters).Error
	return converters, err
}

func (r *IngredientUnitConverterRepositoryImpl) ConvertQuantity(ctx context.Context, ingredientID, fromUnitID, toUnitID, organizationID uuid.UUID, quantity float64) (float64, error) {
	// If from and to units are the same, return the same quantity
	if fromUnitID == toUnitID {
		return quantity, nil
	}

	// Try to find direct converter
	converter, err := r.GetByIngredientAndUnits(ctx, ingredientID, fromUnitID, toUnitID, organizationID)
	if err == nil {
		return quantity * converter.ConversionFactor, nil
	}

	// If direct converter not found, try to find reverse converter
	reverseConverter, err := r.GetByIngredientAndUnits(ctx, ingredientID, toUnitID, fromUnitID, organizationID)
	if err == nil {
		return quantity / reverseConverter.ConversionFactor, nil
	}

	// If no converter found, return error
	return 0, fmt.Errorf("no conversion found between units %s and %s for ingredient %s", fromUnitID, toUnitID, ingredientID)
}

