package service

import (
	"apskel-pos-be/internal/appcontext"
	"apskel-pos-be/internal/contract"
	"apskel-pos-be/internal/mappers"
	"apskel-pos-be/internal/models"
	"apskel-pos-be/internal/processor"
	"apskel-pos-be/internal/repository"
	"context"
	"fmt"

	"github.com/google/uuid"
)

type OrderIngredientTransactionService struct {
	processor processor.OrderIngredientTransactionProcessor
	txManager *repository.TxManager
}

func NewOrderIngredientTransactionService(processor processor.OrderIngredientTransactionProcessor, txManager *repository.TxManager) *OrderIngredientTransactionService {
	return &OrderIngredientTransactionService{
		processor: processor,
		txManager: txManager,
	}
}

func (s *OrderIngredientTransactionService) CreateOrderIngredientTransaction(ctx context.Context, req *contract.CreateOrderIngredientTransactionRequest) (*contract.OrderIngredientTransactionResponse, error) {
	// Get organization and outlet from context
	appCtx := appcontext.FromGinContext(ctx)
	organizationID := appCtx.OrganizationID
	outletID := appCtx.OutletID
	createdBy := appCtx.UserID

	// Convert contract to model
	modelReq := mappers.ContractToModelCreateOrderIngredientTransactionRequest(req)

	// Create transaction
	response, err := s.processor.CreateOrderIngredientTransaction(ctx, modelReq, organizationID, outletID, createdBy)
	if err != nil {
		return nil, fmt.Errorf("failed to create order ingredient transaction: %w", err)
	}

	// Convert model to contract
	contractResp := mappers.ModelToContractOrderIngredientTransactionResponse(response)
	return contractResp, nil
}

func (s *OrderIngredientTransactionService) GetOrderIngredientTransactionByID(ctx context.Context, id uuid.UUID) (*contract.OrderIngredientTransactionResponse, error) {
	// Get organization from context
	appCtx := appcontext.FromGinContext(ctx)
	organizationID := appCtx.OrganizationID

	// Get transaction
	response, err := s.processor.GetOrderIngredientTransactionByID(ctx, id, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get order ingredient transaction: %w", err)
	}

	// Convert model to contract
	contractResp := mappers.ModelToContractOrderIngredientTransactionResponse(response)
	return contractResp, nil
}

func (s *OrderIngredientTransactionService) UpdateOrderIngredientTransaction(ctx context.Context, id uuid.UUID, req *contract.UpdateOrderIngredientTransactionRequest) (*contract.OrderIngredientTransactionResponse, error) {
	// Get organization from context
	appCtx := appcontext.FromGinContext(ctx)
	organizationID := appCtx.OrganizationID

	// Convert contract to model
	modelReq := mappers.ContractToModelUpdateOrderIngredientTransactionRequest(req)

	// Update transaction
	response, err := s.processor.UpdateOrderIngredientTransaction(ctx, id, modelReq, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to update order ingredient transaction: %w", err)
	}

	// Convert model to contract
	contractResp := mappers.ModelToContractOrderIngredientTransactionResponse(response)
	return contractResp, nil
}

func (s *OrderIngredientTransactionService) DeleteOrderIngredientTransaction(ctx context.Context, id uuid.UUID) error {
	// Get organization from context
	appCtx := appcontext.FromGinContext(ctx)
	organizationID := appCtx.OrganizationID

	// Delete transaction
	if err := s.processor.DeleteOrderIngredientTransaction(ctx, id, organizationID); err != nil {
		return fmt.Errorf("failed to delete order ingredient transaction: %w", err)
	}

	return nil
}

func (s *OrderIngredientTransactionService) ListOrderIngredientTransactions(ctx context.Context, req *contract.ListOrderIngredientTransactionsRequest) ([]*contract.OrderIngredientTransactionResponse, int64, error) {
	// Get organization from context
	appCtx := appcontext.FromGinContext(ctx)
	organizationID := appCtx.OrganizationID

	// Convert contract to model
	modelReq := mappers.ContractToModelListOrderIngredientTransactionsRequest(req)

	// List transactions
	responses, total, err := s.processor.ListOrderIngredientTransactions(ctx, modelReq, organizationID)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to list order ingredient transactions: %w", err)
	}

	// Convert models to contracts
	contractResponses := mappers.ModelToContractOrderIngredientTransactionResponses(responses)
	return contractResponses, total, nil
}

func (s *OrderIngredientTransactionService) GetOrderIngredientTransactionsByOrder(ctx context.Context, orderID uuid.UUID) ([]*contract.OrderIngredientTransactionResponse, error) {
	// Get organization from context
	appCtx := appcontext.FromGinContext(ctx)
	organizationID := appCtx.OrganizationID

	// Get transactions by order
	responses, err := s.processor.GetOrderIngredientTransactionsByOrder(ctx, orderID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get order ingredient transactions by order: %w", err)
	}

	// Convert models to contracts
	contractResponses := mappers.ModelToContractOrderIngredientTransactionResponses(responses)
	return contractResponses, nil
}

func (s *OrderIngredientTransactionService) GetOrderIngredientTransactionsByOrderItem(ctx context.Context, orderItemID uuid.UUID) ([]*contract.OrderIngredientTransactionResponse, error) {
	// Get organization from context
	appCtx := appcontext.FromGinContext(ctx)
	organizationID := appCtx.OrganizationID

	// Get transactions by order item
	responses, err := s.processor.GetOrderIngredientTransactionsByOrderItem(ctx, orderItemID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get order ingredient transactions by order item: %w", err)
	}

	// Convert models to contracts
	contractResponses := mappers.ModelToContractOrderIngredientTransactionResponses(responses)
	return contractResponses, nil
}

func (s *OrderIngredientTransactionService) GetOrderIngredientTransactionsByIngredient(ctx context.Context, ingredientID uuid.UUID) ([]*contract.OrderIngredientTransactionResponse, error) {
	// Get organization from context
	appCtx := appcontext.FromGinContext(ctx)
	organizationID := appCtx.OrganizationID

	// Get transactions by ingredient
	responses, err := s.processor.GetOrderIngredientTransactionsByIngredient(ctx, ingredientID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get order ingredient transactions by ingredient: %w", err)
	}

	// Convert models to contracts
	contractResponses := mappers.ModelToContractOrderIngredientTransactionResponses(responses)
	return contractResponses, nil
}

func (s *OrderIngredientTransactionService) GetOrderIngredientTransactionSummary(ctx context.Context, req *contract.ListOrderIngredientTransactionsRequest) ([]*contract.OrderIngredientTransactionSummary, error) {
	// Get organization from context
	appCtx := appcontext.FromGinContext(ctx)
	organizationID := appCtx.OrganizationID

	// Convert contract to model
	modelReq := mappers.ContractToModelListOrderIngredientTransactionsRequest(req)

	// Get summary
	summaries, err := s.processor.GetOrderIngredientTransactionSummary(ctx, modelReq, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to get order ingredient transaction summary: %w", err)
	}

	// Convert models to contracts
	contractSummaries := mappers.ModelToContractOrderIngredientTransactionSummaries(summaries)
	return contractSummaries, nil
}

func (s *OrderIngredientTransactionService) BulkCreateOrderIngredientTransactions(ctx context.Context, transactions []*contract.CreateOrderIngredientTransactionRequest) ([]*contract.OrderIngredientTransactionResponse, error) {
	// Get organization and outlet from context
	appCtx := appcontext.FromGinContext(ctx)
	organizationID := appCtx.OrganizationID
	outletID := appCtx.OutletID
	createdBy := appCtx.UserID

	// Convert contracts to models
	modelReqs := make([]*models.CreateOrderIngredientTransactionRequest, len(transactions))
	for i, req := range transactions {
		modelReqs[i] = mappers.ContractToModelCreateOrderIngredientTransactionRequest(req)
	}

	// Bulk create transactions
	responses, err := s.processor.BulkCreateOrderIngredientTransactions(ctx, modelReqs, organizationID, outletID, createdBy)
	if err != nil {
		return nil, fmt.Errorf("failed to bulk create order ingredient transactions: %w", err)
	}

	// Convert models to contracts
	contractResponses := mappers.ModelToContractOrderIngredientTransactionResponses(responses)
	return contractResponses, nil
}
