package sql

import (
	"context"
	"tercul/internal/domain"
	"tercul/internal/platform/config"

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

type translationRepository struct {
	domain.BaseRepository[domain.Translation]
	db     *gorm.DB
	tracer trace.Tracer
}

// NewTranslationRepository creates a new TranslationRepository.
func NewTranslationRepository(db *gorm.DB, cfg *config.Config) domain.TranslationRepository {
	return &translationRepository{
		BaseRepository: NewBaseRepositoryImpl[domain.Translation](db, cfg),
		db:             db,
		tracer:         otel.Tracer("translation.repository"),
	}
}

// ListByWorkID finds translations by work ID
func (r *translationRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.Translation, error) {
	ctx, span := r.tracer.Start(ctx, "ListByWorkID")
	defer span.End()
	var translations []domain.Translation
	if err := r.db.WithContext(ctx).Where("translatable_id = ? AND translatable_type = ?", workID, "works").Find(&translations).Error; err != nil {
		return nil, err
	}
	return translations, nil
}

// ListByWorkIDPaginated finds translations by work ID with pagination and optional language filtering.
func (r *translationRepository) ListByWorkIDPaginated(ctx context.Context, workID uint, language *string, page, pageSize int) (*domain.PaginatedResult[domain.Translation], error) {
	ctx, span := r.tracer.Start(ctx, "ListByWorkIDPaginated")
	defer span.End()

	if page < 1 {
		page = 1
	}
	if pageSize < 1 {
		pageSize = 20 // Default page size
	}

	var translations []domain.Translation
	var totalCount int64

	query := r.db.WithContext(ctx).Model(&domain.Translation{}).Where("translatable_id = ? AND translatable_type = ?", workID, "works")

	if language != nil {
		query = query.Where("language = ?", *language)
	}

	// Get total count
	if err := query.Count(&totalCount).Error; err != nil {
		return nil, err
	}

	// Calculate offset
	offset := (page - 1) * pageSize

	// Get paginated data
	if err := query.Offset(offset).Limit(pageSize).Find(&translations).Error; err != nil {
		return nil, err
	}

	// Calculate total pages
	totalPages := 0
	if pageSize > 0 {
		totalPages = int(totalCount) / pageSize
		if int(totalCount)%pageSize > 0 {
			totalPages++
		}
	}

	hasNext := page < totalPages
	hasPrev := page > 1

	return &domain.PaginatedResult[domain.Translation]{
		Items:      translations,
		TotalCount: totalCount,
		Page:       page,
		PageSize:   pageSize,
		TotalPages: totalPages,
		HasNext:    hasNext,
		HasPrev:    hasPrev,
	}, nil
}

// Upsert creates a new translation or updates an existing one based on the unique
// composite key of (translatable_id, translatable_type, language).
func (r *translationRepository) Upsert(ctx context.Context, translation *domain.Translation) error {
	ctx, span := r.tracer.Start(ctx, "Upsert")
	defer span.End()

	// The unique key for a translation is (TranslatableID, TranslatableType, Language).
	// If a translation with this combination exists, we update it. Otherwise, we create it.
	return r.db.WithContext(ctx).Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "translatable_id"}, {Name: "translatable_type"}, {Name: "language"}},
		DoUpdates: clause.AssignmentColumns([]string{"title", "content", "description", "status", "translator_id"}),
	}).Create(translation).Error
}

// ListByEntity finds translations by entity type and ID
func (r *translationRepository) ListByEntity(ctx context.Context, entityType string, entityID uint) ([]domain.Translation, error) {
	ctx, span := r.tracer.Start(ctx, "ListByEntity")
	defer span.End()
	var translations []domain.Translation
	if err := r.db.WithContext(ctx).Where("translatable_id = ? AND translatable_type = ?", entityID, entityType).Find(&translations).Error; err != nil {
		return nil, err
	}
	return translations, nil
}

// ListByTranslatorID finds translations by translator ID
func (r *translationRepository) ListByTranslatorID(ctx context.Context, translatorID uint) ([]domain.Translation, error) {
	ctx, span := r.tracer.Start(ctx, "ListByTranslatorID")
	defer span.End()
	var translations []domain.Translation
	if err := r.db.WithContext(ctx).Where("translator_id = ?", translatorID).Find(&translations).Error; err != nil {
		return nil, err
	}
	return translations, nil
}

// ListByStatus finds translations by status
func (r *translationRepository) ListByStatus(ctx context.Context, status domain.TranslationStatus) ([]domain.Translation, error) {
	ctx, span := r.tracer.Start(ctx, "ListByStatus")
	defer span.End()
	var translations []domain.Translation
	if err := r.db.WithContext(ctx).Where("status = ?", status).Find(&translations).Error; err != nil {
		return nil, err
	}
	return translations, nil
}
