package work

import (
	"context"
	"errors"
	"tercul/internal/domain"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/trace"
)

// WorkQueries contains the query handlers for the work aggregate.
type WorkQueries struct {
	repo   domain.WorkRepository
	tracer trace.Tracer
}

// NewWorkQueries creates a new WorkQueries handler.
func NewWorkQueries(repo domain.WorkRepository) *WorkQueries {
	return &WorkQueries{
		repo:   repo,
		tracer: otel.Tracer("work.queries"),
	}
}

// GetWorkByID retrieves a work by ID.
func (q *WorkQueries) GetWorkByID(ctx context.Context, id uint) (*WorkDTO, error) {
	ctx, span := q.tracer.Start(ctx, "GetWorkByID")
	defer span.End()
	if id == 0 {
		return nil, errors.New("invalid work ID")
	}

	work, err := q.repo.GetByID(ctx, id)
	if err != nil {
		return nil, err
	}
	if work == nil {
		return nil, nil
	}

	return &WorkDTO{
		ID:       work.ID,
		Title:    work.Title,
		Language: work.Language,
	}, nil
}

// ListWorks returns a paginated list of works.
func (q *WorkQueries) ListWorks(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[WorkDTO], error) {
	ctx, span := q.tracer.Start(ctx, "ListWorks")
	defer span.End()

	paginatedWorks, err := q.repo.List(ctx, page, pageSize)
	if err != nil {
		return nil, err
	}

	workDTOs := make([]WorkDTO, len(paginatedWorks.Items))
	for i, work := range paginatedWorks.Items {
		workDTOs[i] = WorkDTO{
			ID:       work.ID,
			Title:    work.Title,
			Language: work.Language,
		}
	}

	return &domain.PaginatedResult[WorkDTO]{
		Items:      workDTOs,
		TotalCount: paginatedWorks.TotalCount,
		Page:       paginatedWorks.Page,
		PageSize:   paginatedWorks.PageSize,
		TotalPages: paginatedWorks.TotalPages,
	}, nil
}

// GetWorkWithTranslations retrieves a work with its translations.
func (q *WorkQueries) GetWorkWithTranslations(ctx context.Context, id uint) (*domain.Work, error) {
	ctx, span := q.tracer.Start(ctx, "GetWorkWithTranslations")
	defer span.End()
	if id == 0 {
		return nil, errors.New("invalid work ID")
	}
	return q.repo.GetWithTranslations(ctx, id)
}

// FindWorksByTitle finds works by title.
func (q *WorkQueries) FindWorksByTitle(ctx context.Context, title string) ([]domain.Work, error) {
	ctx, span := q.tracer.Start(ctx, "FindWorksByTitle")
	defer span.End()
	if title == "" {
		return nil, errors.New("title cannot be empty")
	}
	return q.repo.FindByTitle(ctx, title)
}

// FindWorksByAuthor finds works by author ID.
func (q *WorkQueries) FindWorksByAuthor(ctx context.Context, authorID uint) ([]domain.Work, error) {
	ctx, span := q.tracer.Start(ctx, "FindWorksByAuthor")
	defer span.End()
	if authorID == 0 {
		return nil, errors.New("invalid author ID")
	}
	return q.repo.FindByAuthor(ctx, authorID)
}

// FindWorksByCategory finds works by category ID.
func (q *WorkQueries) FindWorksByCategory(ctx context.Context, categoryID uint) ([]domain.Work, error) {
	ctx, span := q.tracer.Start(ctx, "FindWorksByCategory")
	defer span.End()
	if categoryID == 0 {
		return nil, errors.New("invalid category ID")
	}
	return q.repo.FindByCategory(ctx, categoryID)
}

// FindWorksByLanguage finds works by language.
func (q *WorkQueries) FindWorksByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
	ctx, span := q.tracer.Start(ctx, "FindWorksByLanguage")
	defer span.End()
	if language == "" {
		return nil, errors.New("language cannot be empty")
	}
	return q.repo.FindByLanguage(ctx, language, page, pageSize)
}

// ListByCollectionID finds works by collection ID.
func (q *WorkQueries) ListByCollectionID(ctx context.Context, collectionID uint) ([]domain.Work, error) {
	ctx, span := q.tracer.Start(ctx, "ListByCollectionID")
	defer span.End()
	if collectionID == 0 {
		return nil, errors.New("invalid collection ID")
	}
	return q.repo.ListByCollectionID(ctx, collectionID)
}
