package processor

import (
	"apskel-pos-be/internal/appcontext"
	"apskel-pos-be/internal/entities"
	"apskel-pos-be/internal/mappers"
	"apskel-pos-be/internal/models"
	"context"
	"time"

	"github.com/google/uuid"
)

type IngredientProcessorImpl struct {
	ingredientRepo IngredientRepository
	unitRepo       UnitRepository
}

func NewIngredientProcessor(ingredientRepo IngredientRepository, unitRepo UnitRepository) *IngredientProcessorImpl {
	return &IngredientProcessorImpl{
		ingredientRepo: ingredientRepo,
		unitRepo:       unitRepo,
	}
}

func (p *IngredientProcessorImpl) CreateIngredient(ctx context.Context, req *models.CreateIngredientRequest) (*models.IngredientResponse, error) {
	_, err := p.unitRepo.GetByID(ctx, req.UnitID, req.OrganizationID)
	if err != nil {
		return nil, err
	}

	ingredient := &entities.Ingredient{
		ID:             uuid.New(),
		OrganizationID: req.OrganizationID,
		OutletID:       req.OutletID,
		Name:           req.Name,
		UnitID:         req.UnitID,
		Cost:           req.Cost,
		Stock:          req.Stock,
		IsSemiFinished: req.IsSemiFinished,
		IsActive:       req.IsActive,
		Metadata:       req.Metadata,
		CreatedAt:      time.Now(),
		UpdatedAt:      time.Now(),
	}

	err = p.ingredientRepo.Create(ctx, ingredient)
	if err != nil {
		return nil, err
	}

	ingredientWithUnit, err := p.ingredientRepo.GetByID(ctx, ingredient.ID, req.OrganizationID)
	if err != nil {
		return nil, err
	}

	ingredientModel := mappers.MapIngredientEntityToModel(ingredientWithUnit)
	response := &models.IngredientResponse{
		ID:             ingredientModel.ID,
		OrganizationID: ingredientModel.OrganizationID,
		OutletID:       ingredientModel.OutletID,
		Name:           ingredientModel.Name,
		UnitID:         ingredientModel.UnitID,
		Cost:           ingredientModel.Cost,
		Stock:          ingredientModel.Stock,
		IsSemiFinished: ingredientModel.IsSemiFinished,
		IsActive:       ingredientModel.IsActive,
		Metadata:       ingredientModel.Metadata,
		CreatedAt:      ingredientModel.CreatedAt,
		UpdatedAt:      ingredientModel.UpdatedAt,
		Unit:           ingredientModel.Unit,
	}

	return response, nil
}

func (p *IngredientProcessorImpl) GetIngredientByID(ctx context.Context, id uuid.UUID) (*models.IngredientResponse, error) {
	contextInfo := appcontext.FromGinContext(ctx)

	// For now, we'll need to get organizationID from context or request
	// This is a limitation of the current interface design
	organizationID := contextInfo.OrganizationID // This should come from context

	ingredient, err := p.ingredientRepo.GetByID(ctx, id, organizationID)
	if err != nil {
		return nil, err
	}

	ingredientModel := mappers.MapIngredientEntityToModel(ingredient)
	response := &models.IngredientResponse{
		ID:             ingredientModel.ID,
		OrganizationID: ingredientModel.OrganizationID,
		OutletID:       ingredientModel.OutletID,
		Name:           ingredientModel.Name,
		UnitID:         ingredientModel.UnitID,
		Cost:           ingredientModel.Cost,
		Stock:          ingredientModel.Stock,
		IsSemiFinished: ingredientModel.IsSemiFinished,
		IsActive:       ingredientModel.IsActive,
		Metadata:       ingredientModel.Metadata,
		CreatedAt:      ingredientModel.CreatedAt,
		UpdatedAt:      ingredientModel.UpdatedAt,
		Unit:           ingredientModel.Unit,
	}

	return response, nil
}

func (p *IngredientProcessorImpl) ListIngredients(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, page, limit int, search string) (*models.PaginatedResponse[models.IngredientResponse], error) {
	// Set default values
	if page < 1 {
		page = 1
	}
	if limit < 1 {
		limit = 10
	}
	if limit > 100 {
		limit = 100
	}

	ingredients, total, err := p.ingredientRepo.GetAll(ctx, organizationID, outletID, page, limit, search, nil)
	if err != nil {
		return nil, err
	}

	// Map to response models
	ingredientModels := mappers.MapIngredientEntitiesToModels(ingredients)
	ingredientResponses := make([]models.IngredientResponse, len(ingredientModels))

	for i, ingredientModel := range ingredientModels {
		ingredientResponses[i] = models.IngredientResponse{
			ID:             ingredientModel.ID,
			OrganizationID: ingredientModel.OrganizationID,
			OutletID:       ingredientModel.OutletID,
			Name:           ingredientModel.Name,
			UnitID:         ingredientModel.UnitID,
			Cost:           ingredientModel.Cost,
			Stock:          ingredientModel.Stock,
			IsSemiFinished: ingredientModel.IsSemiFinished,
			IsActive:       ingredientModel.IsActive,
			Metadata:       ingredientModel.Metadata,
			CreatedAt:      ingredientModel.CreatedAt,
			UpdatedAt:      ingredientModel.UpdatedAt,
			Unit:           ingredientModel.Unit,
		}
	}

	// Create paginated response
	paginatedResponse := &models.PaginatedResponse[models.IngredientResponse]{
		Data: ingredientResponses,
		Pagination: models.Pagination{
			Page:       page,
			Limit:      limit,
			Total:      int64(total),
			TotalPages: (total + limit - 1) / limit,
		},
	}

	return paginatedResponse, nil
}

func (p *IngredientProcessorImpl) UpdateIngredient(ctx context.Context, id uuid.UUID, req *models.UpdateIngredientRequest) (*models.IngredientResponse, error) {
	contextInfo := appcontext.FromGinContext(ctx)
	// For now, we'll need to get organizationID from context or request
	// This is a limitation of the current interface design
	organizationID := contextInfo.OrganizationID // This should come from context

	// Get existing ingredient
	existingIngredient, err := p.ingredientRepo.GetByID(ctx, id, organizationID)
	if err != nil {
		return nil, err
	}

	// Validate unit exists if changed
	if req.UnitID != existingIngredient.UnitID {
		_, err := p.unitRepo.GetByID(ctx, req.UnitID, organizationID)
		if err != nil {
			return nil, err
		}
	}

	// Update fields
	existingIngredient.OutletID = req.OutletID
	existingIngredient.Name = req.Name
	existingIngredient.UnitID = req.UnitID
	existingIngredient.Cost = req.Cost
	existingIngredient.Stock = req.Stock
	existingIngredient.IsSemiFinished = req.IsSemiFinished
	existingIngredient.IsActive = req.IsActive
	existingIngredient.Metadata = req.Metadata
	existingIngredient.UpdatedAt = time.Now()

	// Save to database
	err = p.ingredientRepo.Update(ctx, existingIngredient)
	if err != nil {
		return nil, err
	}

	// Get with relations
	ingredientWithUnit, err := p.ingredientRepo.GetByID(ctx, id, organizationID)
	if err != nil {
		return nil, err
	}

	// Map to response
	ingredientModel := mappers.MapIngredientEntityToModel(ingredientWithUnit)
	response := &models.IngredientResponse{
		ID:             ingredientModel.ID,
		OrganizationID: ingredientModel.OrganizationID,
		OutletID:       ingredientModel.OutletID,
		Name:           ingredientModel.Name,
		UnitID:         ingredientModel.UnitID,
		Cost:           ingredientModel.Cost,
		Stock:          ingredientModel.Stock,
		IsSemiFinished: ingredientModel.IsSemiFinished,
		IsActive:       ingredientModel.IsActive,
		Metadata:       ingredientModel.Metadata,
		CreatedAt:      ingredientModel.CreatedAt,
		UpdatedAt:      ingredientModel.UpdatedAt,
		Unit:           ingredientModel.Unit,
	}

	return response, nil
}

func (p *IngredientProcessorImpl) DeleteIngredient(ctx context.Context, id uuid.UUID) error {
	contextInfo := appcontext.FromGinContext(ctx)
	organizationID := contextInfo.OrganizationID

	err := p.ingredientRepo.Delete(ctx, id, organizationID)
	if err != nil {
		return err
	}

	return nil
}
