package service

import (
	"context"
	"time"

	"erp-mvp/core-service/internal/models"
	"erp-mvp/core-service/internal/repository"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"
)

type OperationsService interface {
	PlaceItem(ctx context.Context, orgID uuid.UUID, req *models.PlaceItemRequest) (*models.ItemPlacement, error)
	MoveItem(ctx context.Context, placementID uuid.UUID, newLocationID uuid.UUID, orgID uuid.UUID) error
	GetItemPlacements(ctx context.Context, itemID uuid.UUID, orgID uuid.UUID) ([]*models.ItemPlacement, error)
	GetLocationPlacements(ctx context.Context, locationID uuid.UUID, orgID uuid.UUID) ([]*models.ItemPlacement, error)
	UpdateQuantity(ctx context.Context, placementID uuid.UUID, quantity int, orgID uuid.UUID) error
	DeletePlacement(ctx context.Context, placementID uuid.UUID, orgID uuid.UUID) error
	Search(ctx context.Context, orgID uuid.UUID, req *models.SearchRequest) (*models.SearchResponse, error)
}

type operationsService struct {
	operationsRepo repository.OperationsRepository
	itemRepo       repository.ItemRepository
	locationRepo   repository.LocationRepository
	logger         *logrus.Logger
}

func NewOperationsService(operationsRepo repository.OperationsRepository, itemRepo repository.ItemRepository, locationRepo repository.LocationRepository) OperationsService {
	return &operationsService{
		operationsRepo: operationsRepo,
		itemRepo:       itemRepo,
		locationRepo:   locationRepo,
		logger:         logrus.New(),
	}
}

func (s *operationsService) PlaceItem(ctx context.Context, orgID uuid.UUID, req *models.PlaceItemRequest) (*models.ItemPlacement, error) {
	s.logger.Info("Placing item: ", req.ItemID, " in location: ", req.LocationID, " for organization: ", orgID)

	// Проверяем, что товар существует и принадлежит организации
	_, err := s.itemRepo.GetByID(ctx, req.ItemID, orgID)
	if err != nil {
		s.logger.Error("Item not found or not accessible: ", err)
		return nil, err
	}

	// Проверяем, что место хранения существует и принадлежит организации
	_, err = s.locationRepo.GetByID(ctx, req.LocationID, orgID)
	if err != nil {
		s.logger.Error("Location not found or not accessible: ", err)
		return nil, err
	}

	placement := &models.ItemPlacement{
		ID:             uuid.New(),
		OrganizationID: orgID,
		ItemID:         req.ItemID,
		LocationID:     req.LocationID,
		Quantity:       req.Quantity,
		CreatedAt:      time.Now(),
	}

	if err := s.operationsRepo.PlaceItem(ctx, placement); err != nil {
		s.logger.Error("Failed to place item: ", err)
		return nil, err
	}

	s.logger.Info("Item placed successfully: ", placement.ID)
	return placement, nil
}

func (s *operationsService) MoveItem(ctx context.Context, placementID uuid.UUID, newLocationID uuid.UUID, orgID uuid.UUID) error {
	s.logger.Info("Moving item placement: ", placementID, " to location: ", newLocationID, " for organization: ", orgID)

	// Проверяем, что размещение существует и принадлежит организации
	placement, err := s.operationsRepo.GetByID(ctx, placementID, orgID)
	if err != nil {
		s.logger.Error("Item placement not found or not accessible: ", err)
		return err
	}

	// Проверяем, что новое место хранения существует и принадлежит организации
	_, err = s.locationRepo.GetByID(ctx, newLocationID, orgID)
	if err != nil {
		s.logger.Error("New location not found or not accessible: ", err)
		return err
	}

	if err := s.operationsRepo.MoveItem(ctx, placementID, newLocationID, orgID); err != nil {
		s.logger.Error("Failed to move item: ", err)
		return err
	}

	s.logger.Info("Item moved successfully from location: ", placement.LocationID, " to: ", newLocationID)
	return nil
}

func (s *operationsService) GetItemPlacements(ctx context.Context, itemID uuid.UUID, orgID uuid.UUID) ([]*models.ItemPlacement, error) {
	s.logger.Info("Getting placements for item: ", itemID, " in organization: ", orgID)

	// Проверяем, что товар существует и принадлежит организации
	_, err := s.itemRepo.GetByID(ctx, itemID, orgID)
	if err != nil {
		s.logger.Error("Item not found or not accessible: ", err)
		return nil, err
	}

	placements, err := s.operationsRepo.GetByItem(ctx, itemID, orgID)
	if err != nil {
		s.logger.Error("Failed to get item placements: ", err)
		return nil, err
	}

	return placements, nil
}

func (s *operationsService) GetLocationPlacements(ctx context.Context, locationID uuid.UUID, orgID uuid.UUID) ([]*models.ItemPlacement, error) {
	s.logger.Info("Getting placements for location: ", locationID, " in organization: ", orgID)

	// Проверяем, что место хранения существует и принадлежит организации
	_, err := s.locationRepo.GetByID(ctx, locationID, orgID)
	if err != nil {
		s.logger.Error("Location not found or not accessible: ", err)
		return nil, err
	}

	placements, err := s.operationsRepo.GetByLocation(ctx, locationID, orgID)
	if err != nil {
		s.logger.Error("Failed to get location placements: ", err)
		return nil, err
	}

	return placements, nil
}

func (s *operationsService) UpdateQuantity(ctx context.Context, placementID uuid.UUID, quantity int, orgID uuid.UUID) error {
	s.logger.Info("Updating quantity for placement: ", placementID, " to: ", quantity, " in organization: ", orgID)

	// Проверяем, что размещение существует и принадлежит организации
	_, err := s.operationsRepo.GetByID(ctx, placementID, orgID)
	if err != nil {
		s.logger.Error("Item placement not found or not accessible: ", err)
		return err
	}

	if err := s.operationsRepo.UpdateQuantity(ctx, placementID, quantity, orgID); err != nil {
		s.logger.Error("Failed to update quantity: ", err)
		return err
	}

	s.logger.Info("Quantity updated successfully for placement: ", placementID)
	return nil
}

func (s *operationsService) DeletePlacement(ctx context.Context, placementID uuid.UUID, orgID uuid.UUID) error {
	s.logger.Info("Deleting placement: ", placementID, " for organization: ", orgID)

	// Проверяем, что размещение существует и принадлежит организации
	_, err := s.operationsRepo.GetByID(ctx, placementID, orgID)
	if err != nil {
		s.logger.Error("Item placement not found or not accessible: ", err)
		return err
	}

	if err := s.operationsRepo.Delete(ctx, placementID, orgID); err != nil {
		s.logger.Error("Failed to delete placement: ", err)
		return err
	}

	s.logger.Info("Placement deleted successfully: ", placementID)
	return nil
}

func (s *operationsService) Search(ctx context.Context, orgID uuid.UUID, req *models.SearchRequest) (*models.SearchResponse, error) {
	s.logger.Info("Searching items with locations for organization: ", orgID, " query: ", req.Query)

	results, err := s.operationsRepo.Search(ctx, orgID, req.Query, req.Category, req.Address)
	if err != nil {
		s.logger.Error("Failed to search items with locations: ", err)
		return nil, err
	}

	response := &models.SearchResponse{
		Items:      results,
		TotalCount: len(results),
	}

	return response, nil
}
