package processor

import (
	"apskel-pos-be/internal/constants"
	"context"
	"fmt"
	"time"

	"apskel-pos-be/internal/entities"
	"apskel-pos-be/internal/models"
	"apskel-pos-be/internal/repository"

	"github.com/google/uuid"
)

type ProductRecipeProcessor interface {
	Create(ctx context.Context, req *models.CreateProductRecipeRequest, organizationID uuid.UUID) (*models.ProductRecipeResponse, error)
	GetByID(ctx context.Context, id uuid.UUID, organizationID uuid.UUID) (*models.ProductRecipeResponse, error)
	GetByProductID(ctx context.Context, productID uuid.UUID, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error)
	GetByProductAndVariantID(ctx context.Context, productID uuid.UUID, variantID *uuid.UUID, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error)
	GetByIngredientID(ctx context.Context, ingredientID uuid.UUID, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error)
	Update(ctx context.Context, id uuid.UUID, req *models.UpdateProductRecipeRequest, organizationID uuid.UUID) (*models.ProductRecipeResponse, error)
	Delete(ctx context.Context, id uuid.UUID, organizationID uuid.UUID) error
	BulkCreate(ctx context.Context, recipes []models.CreateProductRecipeRequest, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error)
}

type ProductRecipeProcessorImpl struct {
	productRecipeRepo *repository.ProductRecipeRepository
	productRepo       ProductRepository
	ingredientRepo    IngredientRepository
}

func NewProductRecipeProcessor(productRecipeRepo *repository.ProductRecipeRepository, productRepo ProductRepository, ingredientRepo IngredientRepository) *ProductRecipeProcessorImpl {
	return &ProductRecipeProcessorImpl{
		productRecipeRepo: productRecipeRepo,
		productRepo:       productRepo,
		ingredientRepo:    ingredientRepo,
	}
}

func (p *ProductRecipeProcessorImpl) Create(ctx context.Context, req *models.CreateProductRecipeRequest, organizationID uuid.UUID) (*models.ProductRecipeResponse, error) {
	_, err := p.productRepo.GetByID(ctx, req.ProductID)
	if err != nil {
		return nil, fmt.Errorf("invalid product: %w", err)
	}

	_, err = p.ingredientRepo.GetByID(ctx, req.IngredientID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("invalid ingredient: %w", err)
	}

	entity := &entities.ProductRecipe{
		ID:             uuid.New(),
		OrganizationID: organizationID,
		OutletID:       req.OutletID,
		ProductID:      req.ProductID,
		VariantID:      req.VariantID,
		IngredientID:   req.IngredientID,
		Quantity:       req.Quantity,
		CreatedAt:      time.Now().UTC(),
		UpdatedAt:      time.Now().UTC(),
	}

	if err := p.productRecipeRepo.Create(ctx, entity); err != nil {
		return nil, fmt.Errorf("failed to create product recipe: %w", err)
	}

	createdEntity, err := p.productRecipeRepo.GetByID(ctx, entity.ID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get created product recipe: %w", err)
	}

	return p.entityToResponse(createdEntity), nil
}

func (p *ProductRecipeProcessorImpl) GetByID(ctx context.Context, id uuid.UUID, organizationID uuid.UUID) (*models.ProductRecipeResponse, error) {
	entity, err := p.productRecipeRepo.GetByID(ctx, id, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get product recipe: %w", err)
	}

	return p.entityToResponse(entity), nil
}

func (p *ProductRecipeProcessorImpl) GetByProductID(ctx context.Context, productID uuid.UUID, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error) {
	entities, err := p.productRecipeRepo.GetByProductID(ctx, productID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get product recipes by product ID: %w", err)
	}

	responses := make([]*models.ProductRecipeResponse, len(entities))
	for i, entity := range entities {
		responses[i] = p.entityToResponse(entity)
	}

	return responses, nil
}

func (p *ProductRecipeProcessorImpl) GetByProductAndVariantID(ctx context.Context, productID uuid.UUID, variantID *uuid.UUID, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error) {
	entities, err := p.productRecipeRepo.GetByProductAndVariantID(ctx, productID, variantID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get product recipes by product and variant ID: %w", err)
	}

	responses := make([]*models.ProductRecipeResponse, len(entities))
	for i, entity := range entities {
		responses[i] = p.entityToResponse(entity)
	}

	return responses, nil
}

func (p *ProductRecipeProcessorImpl) GetByIngredientID(ctx context.Context, ingredientID uuid.UUID, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error) {
	entities, err := p.productRecipeRepo.GetByIngredientID(ctx, ingredientID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get product recipes by ingredient ID: %w", err)
	}

	responses := make([]*models.ProductRecipeResponse, len(entities))
	for i, entity := range entities {
		responses[i] = p.entityToResponse(entity)
	}

	return responses, nil
}

func (p *ProductRecipeProcessorImpl) Update(ctx context.Context, id uuid.UUID, req *models.UpdateProductRecipeRequest, organizationID uuid.UUID) (*models.ProductRecipeResponse, error) {
	// Get existing entity
	existingEntity, err := p.productRecipeRepo.GetByID(ctx, id, organizationID)
	if err != nil {
		return nil, fmt.Errorf("product recipe not found: %w", err)
	}

	// Update fields
	existingEntity.OutletID = req.OutletID
	existingEntity.VariantID = req.VariantID
	existingEntity.Quantity = req.Quantity
	existingEntity.UpdatedAt = time.Now().UTC()

	if err := p.productRecipeRepo.Update(ctx, existingEntity); err != nil {
		return nil, fmt.Errorf("failed to update product recipe: %w", err)
	}

	// Get the updated entity with relations
	updatedEntity, err := p.productRecipeRepo.GetByID(ctx, id, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get updated product recipe: %w", err)
	}

	return p.entityToResponse(updatedEntity), nil
}

func (p *ProductRecipeProcessorImpl) Delete(ctx context.Context, id uuid.UUID, organizationID uuid.UUID) error {
	if err := p.productRecipeRepo.Delete(ctx, id, organizationID); err != nil {
		return fmt.Errorf("failed to delete product recipe: %w", err)
	}

	return nil
}

func (p *ProductRecipeProcessorImpl) BulkCreate(ctx context.Context, recipes []models.CreateProductRecipeRequest, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error) {
	responses := make([]*models.ProductRecipeResponse, 0, len(recipes))

	for _, recipe := range recipes {
		response, err := p.Create(ctx, &recipe, organizationID)
		if err != nil {
			return nil, fmt.Errorf("failed to create recipe for product %s and ingredient %s: %w", recipe.ProductID, recipe.IngredientID, err)
		}
		responses = append(responses, response)
	}

	return responses, nil
}

func (p *ProductRecipeProcessorImpl) entityToResponse(entity *entities.ProductRecipe) *models.ProductRecipeResponse {
	response := &models.ProductRecipeResponse{
		ID:             entity.ID,
		OrganizationID: entity.OrganizationID,
		OutletID:       entity.OutletID,
		ProductID:      entity.ProductID,
		VariantID:      entity.VariantID,
		IngredientID:   entity.IngredientID,
		Quantity:       entity.Quantity,
		CreatedAt:      entity.CreatedAt,
		UpdatedAt:      entity.UpdatedAt,
	}

	if entity.Product != nil {
		response.Product = &models.Product{
			ID:             entity.Product.ID,
			OrganizationID: entity.Product.OrganizationID,
			CategoryID:     entity.Product.CategoryID,
			SKU:            entity.Product.SKU,
			Name:           entity.Product.Name,
			Description:    entity.Product.Description,
			Price:          entity.Product.Price,
			Cost:           entity.Product.Cost,
			BusinessType:   constants.BusinessType(entity.Product.BusinessType),
			ImageURL:       entity.Product.ImageURL,
			PrinterType:    entity.Product.PrinterType,
			UnitID:         entity.Product.UnitID,
			HasIngredients: entity.Product.HasIngredients,
			Metadata:       entity.Product.Metadata,
			IsActive:       entity.Product.IsActive,
			CreatedAt:      entity.Product.CreatedAt,
			UpdatedAt:      entity.Product.UpdatedAt,
		}
	}

	if entity.ProductVariant != nil {
		response.ProductVariant = &models.ProductVariant{
			ID:            entity.ProductVariant.ID,
			ProductID:     entity.ProductVariant.ProductID,
			Name:          entity.ProductVariant.Name,
			PriceModifier: entity.ProductVariant.PriceModifier,
			Cost:          entity.ProductVariant.Cost,
			Metadata:      entity.ProductVariant.Metadata,
			CreatedAt:     entity.ProductVariant.CreatedAt,
			UpdatedAt:     entity.ProductVariant.UpdatedAt,
		}
	}

	if entity.Ingredient != nil {
		response.Ingredient = &models.Ingredient{
			ID:             entity.Ingredient.ID,
			OrganizationID: entity.Ingredient.OrganizationID,
			OutletID:       entity.Ingredient.OutletID,
			Name:           entity.Ingredient.Name,
			UnitID:         entity.Ingredient.UnitID,
			Cost:           entity.Ingredient.Cost,
			Stock:          entity.Ingredient.Stock,
			IsSemiFinished: entity.Ingredient.IsSemiFinished,
			IsActive:       entity.Ingredient.IsActive,
			Metadata:       entity.Ingredient.Metadata,
			CreatedAt:      entity.Ingredient.CreatedAt,
			UpdatedAt:      entity.Ingredient.UpdatedAt,
		}
	}

	return response
}
