// Package service provides business logic / use cases for the application.
// Services orchestrate domain operations using port interfaces.
package service

import (
	"context"
	"errors"

	"github.com/google/uuid"

	"{{GO_MODULE}}/pkg/logging"
	"{{GO_MODULE}}/services/{{COMPONENT_NAME}}/internal/domain"
	"{{GO_MODULE}}/services/{{COMPONENT_NAME}}/internal/port"
)

// ExampleService handles example-related business logic.
type ExampleService struct {
	repo   port.ExampleRepository
	logger *logging.Logger
}

// NewExampleService creates a new example service.
func NewExampleService(repo port.ExampleRepository, logger *logging.Logger) *ExampleService {
	return &ExampleService{
		repo:   repo,
		logger: logger.WithService("ExampleService"),
	}
}

// List returns all examples.
func (s *ExampleService) List(ctx context.Context) ([]domain.Example, error) {
	return s.repo.List(ctx)
}

// Get returns an example by ID.
// Returns domain.ErrExampleNotFound if not found.
func (s *ExampleService) Get(ctx context.Context, id domain.ExampleID) (*domain.Example, error) {
	return s.repo.Get(ctx, id)
}

// CreateInput contains the data needed to create an example.
type CreateInput struct {
	Name        string
	Description string
}

// Create creates a new example with duplicate detection.
// Returns domain.ErrDuplicateExample if name already exists.
// Returns domain.ErrInvalidExampleName if name is invalid.
func (s *ExampleService) Create(ctx context.Context, input CreateInput) (*domain.Example, error) {
	// Check for duplicates
	exists, err := s.repo.ExistsByName(ctx, input.Name)
	if err != nil {
		return nil, err
	}
	if exists {
		return nil, domain.ErrDuplicateExample
	}

	// Generate new ID
	id := domain.ExampleID(uuid.New().String())

	// Create domain entity (validates name)
	example, err := domain.NewExample(id, input.Name, input.Description)
	if err != nil {
		return nil, err
	}

	// Persist
	if err := s.repo.Create(ctx, example); err != nil {
		return nil, err
	}

	s.logger.Info("example created", "id", id, "name", input.Name)
	return example, nil
}

// UpdateInput contains the data needed to update an example.
type UpdateInput struct {
	Name        string
	Description string
}

// Update modifies an existing example.
// Returns domain.ErrExampleNotFound if not found.
// Returns domain.ErrDuplicateExample if new name conflicts with another example.
// Returns domain.ErrInvalidExampleName if name is invalid.
func (s *ExampleService) Update(ctx context.Context, id domain.ExampleID, input UpdateInput) (*domain.Example, error) {
	// Fetch existing
	example, err := s.repo.Get(ctx, id)
	if err != nil {
		return nil, err
	}

	// Check for name conflicts (only if name changed)
	if example.Name != input.Name {
		exists, err := s.repo.ExistsByName(ctx, input.Name)
		if err != nil {
			return nil, err
		}
		if exists {
			return nil, domain.ErrDuplicateExample
		}
	}

	// Update domain entity (validates name)
	if err := example.Update(input.Name, input.Description); err != nil {
		return nil, err
	}

	// Persist
	if err := s.repo.Update(ctx, example); err != nil {
		return nil, err
	}

	s.logger.Info("example updated", "id", id, "name", input.Name)
	return example, nil
}

// Delete removes an example by ID.
// Returns domain.ErrExampleNotFound if not found.
func (s *ExampleService) Delete(ctx context.Context, id domain.ExampleID) error {
	// Verify exists before delete
	if _, err := s.repo.Get(ctx, id); err != nil {
		if errors.Is(err, domain.ErrExampleNotFound) {
			return domain.ErrExampleNotFound
		}
		return err
	}

	if err := s.repo.Delete(ctx, id); err != nil {
		return err
	}

	s.logger.Info("example deleted", "id", id)
	return nil
}
