package translation

import (
	"context"
	"fmt"
	"tercul/internal/app/authz"
	"tercul/internal/domain"
	platform_auth "tercul/internal/platform/auth"

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

// TranslationCommands contains the command handlers for the translation aggregate.
type TranslationCommands struct {
	repo     domain.TranslationRepository
	authzSvc *authz.Service
	tracer   trace.Tracer
}

// NewTranslationCommands creates a new TranslationCommands handler.
func NewTranslationCommands(repo domain.TranslationRepository, authzSvc *authz.Service) *TranslationCommands {
	return &TranslationCommands{
		repo:     repo,
		authzSvc: authzSvc,
		tracer:   otel.Tracer("translation.commands"),
	}
}

// CreateOrUpdateTranslationInput represents the input for creating or updating a translation.
type CreateOrUpdateTranslationInput struct {
	Title              string
	Content            string
	Description        string
	Language           string
	Status             domain.TranslationStatus
	TranslatableID     uint
	TranslatableType   string
	TranslatorID       *uint
	IsOriginalLanguage bool
}

// CreateOrUpdateTranslation creates a new translation or updates an existing one.
func (c *TranslationCommands) CreateOrUpdateTranslation(ctx context.Context, input CreateOrUpdateTranslationInput) (*domain.Translation, error) {
	ctx, span := c.tracer.Start(ctx, "CreateOrUpdateTranslation")
	defer span.End()

	// Validate input first
	if input.Language == "" {
		return nil, fmt.Errorf("%w: language cannot be empty", domain.ErrValidation)
	}
	if input.TranslatableID == 0 {
		return nil, fmt.Errorf("%w: translatable ID cannot be zero", domain.ErrValidation)
	}
	if input.TranslatableType == "" {
		return nil, fmt.Errorf("%w: translatable type cannot be empty", domain.ErrValidation)
	}

	userID, ok := platform_auth.GetUserIDFromContext(ctx)
	if !ok {
		return nil, domain.ErrUnauthorized
	}

	// Authorize that the user can edit the parent entity.
	can, err := c.authzSvc.CanEditEntity(ctx, userID, input.TranslatableType, input.TranslatableID)
	if err != nil {
		return nil, fmt.Errorf("authorization check failed: %w", err)
	}
	if !can {
		return nil, domain.ErrForbidden
	}

	var translatorID uint
	if input.TranslatorID != nil {
		translatorID = *input.TranslatorID
	} else {
		translatorID = userID
	}

	translation := &domain.Translation{
		Title:              input.Title,
		Content:            input.Content,
		Description:        input.Description,
		Language:           input.Language,
		Status:             input.Status,
		TranslatableID:     input.TranslatableID,
		TranslatableType:   input.TranslatableType,
		TranslatorID:       &translatorID,
		IsOriginalLanguage: input.IsOriginalLanguage,
	}

	if err := c.repo.Upsert(ctx, translation); err != nil {
		return nil, fmt.Errorf("failed to upsert translation: %w", err)
	}

	return translation, nil
}

// DeleteTranslation deletes a translation by ID.
func (c *TranslationCommands) DeleteTranslation(ctx context.Context, id uint) error {
	ctx, span := c.tracer.Start(ctx, "DeleteTranslation")
	defer span.End()

	userID, ok := platform_auth.GetUserIDFromContext(ctx)
	if !ok {
		return domain.ErrUnauthorized
	}

	can, err := c.authzSvc.CanDeleteTranslation(ctx, userID, id)
	if err != nil {
		return err
	}
	if !can {
		return domain.ErrForbidden
	}

	return c.repo.Delete(ctx, id)
}
