package processor

import (
	"context"
	"fmt"

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

	"github.com/google/uuid"
)

type ProductVariantProcessor interface {
	CreateProductVariant(ctx context.Context, req *models.CreateProductVariantRequest) (*models.ProductVariantResponse, error)
	UpdateProductVariant(ctx context.Context, id uuid.UUID, req *models.UpdateProductVariantRequest) (*models.ProductVariantResponse, error)
	DeleteProductVariant(ctx context.Context, id uuid.UUID) error
	GetProductVariantByID(ctx context.Context, id uuid.UUID) (*models.ProductVariantResponse, error)
	GetProductVariantsByProductID(ctx context.Context, productID uuid.UUID) ([]models.ProductVariantResponse, error)
}

type ProductVariantProcessorImpl struct {
	productVariantRepo repository.ProductVariantRepository
	productRepo        ProductRepository
}

func NewProductVariantProcessorImpl(
	productVariantRepo repository.ProductVariantRepository,
	productRepo ProductRepository,
) *ProductVariantProcessorImpl {
	return &ProductVariantProcessorImpl{
		productVariantRepo: productVariantRepo,
		productRepo:        productRepo,
	}
}

func (p *ProductVariantProcessorImpl) CreateProductVariant(ctx context.Context, req *models.CreateProductVariantRequest) (*models.ProductVariantResponse, error) {
	// Validate product exists
	_, err := p.productRepo.GetByID(ctx, req.ProductID)
	if err != nil {
		return nil, fmt.Errorf("invalid product: %w", err)
	}

	// Check name uniqueness within the same product
	exists, err := p.productVariantRepo.ExistsByName(ctx, req.ProductID, req.Name, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to check variant name uniqueness: %w", err)
	}
	if exists {
		return nil, fmt.Errorf("variant with name '%s' already exists for this product", req.Name)
	}

	// Map request to entity
	variantEntity := mappers.CreateProductVariantRequestToEntity(req)

	// Create variant
	if err := p.productVariantRepo.Create(ctx, variantEntity); err != nil {
		return nil, fmt.Errorf("failed to create product variant: %w", err)
	}

	// Map entity to response model
	response := mappers.ProductVariantEntityToResponse(variantEntity)
	return response, nil
}

func (p *ProductVariantProcessorImpl) UpdateProductVariant(ctx context.Context, id uuid.UUID, req *models.UpdateProductVariantRequest) (*models.ProductVariantResponse, error) {
	// Get existing variant
	existingVariant, err := p.productVariantRepo.GetByID(ctx, id)
	if err != nil {
		return nil, fmt.Errorf("product variant not found: %w", err)
	}

	// Check name uniqueness if being updated
	if req.Name != nil && *req.Name != existingVariant.Name {
		exists, err := p.productVariantRepo.ExistsByName(ctx, existingVariant.ProductID, *req.Name, &id)
		if err != nil {
			return nil, fmt.Errorf("failed to check variant name uniqueness: %w", err)
		}
		if exists {
			return nil, fmt.Errorf("variant with name '%s' already exists for this product", *req.Name)
		}
	}

	// Apply updates to entity
	mappers.UpdateProductVariantEntityFromRequest(existingVariant, req)

	// Update variant
	if err := p.productVariantRepo.Update(ctx, existingVariant); err != nil {
		return nil, fmt.Errorf("failed to update product variant: %w", err)
	}

	// Map entity to response model
	response := mappers.ProductVariantEntityToResponse(existingVariant)
	return response, nil
}

func (p *ProductVariantProcessorImpl) DeleteProductVariant(ctx context.Context, id uuid.UUID) error {
	// Check if variant exists
	_, err := p.productVariantRepo.GetByID(ctx, id)
	if err != nil {
		return fmt.Errorf("product variant not found: %w", err)
	}

	// TODO: Check if variant is used in any order items before deletion
	// This would require checking the order_items table

	// Delete variant
	if err := p.productVariantRepo.Delete(ctx, id); err != nil {
		return fmt.Errorf("failed to delete product variant: %w", err)
	}

	return nil
}

func (p *ProductVariantProcessorImpl) GetProductVariantByID(ctx context.Context, id uuid.UUID) (*models.ProductVariantResponse, error) {
	variant, err := p.productVariantRepo.GetByID(ctx, id)
	if err != nil {
		return nil, fmt.Errorf("product variant not found: %w", err)
	}

	response := mappers.ProductVariantEntityToResponse(variant)
	return response, nil
}

func (p *ProductVariantProcessorImpl) GetProductVariantsByProductID(ctx context.Context, productID uuid.UUID) ([]models.ProductVariantResponse, error) {
	variants, err := p.productVariantRepo.GetByProductID(ctx, productID)
	if err != nil {
		return nil, fmt.Errorf("failed to get product variants: %w", err)
	}

	responses := make([]models.ProductVariantResponse, len(variants))
	for i, variant := range variants {
		response := mappers.ProductVariantEntityToResponse(variant)
		responses[i] = *response
	}

	return responses, nil
}
