package page

import (
	"context"
	"fmt"

	"github.com/gocql/gocql"
	"go.uber.org/zap"

	domainpage "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/page"
	pageusecase "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/page"
)

// SyncPagesService handles page synchronization operations
type SyncPagesService interface {
	SyncPages(ctx context.Context, tenantID, siteID gocql.UUID, input *pageusecase.SyncPagesInput) (*pageusecase.SyncPagesOutput, error)
}

type syncPagesService struct {
	// Focused usecases
	validateSiteUC *pageusecase.ValidateSiteUseCase
	ensureIndexUC  *pageusecase.EnsureSearchIndexUseCase
	createPageUC   *pageusecase.CreatePageEntityUseCase
	upsertPageUC   *pageusecase.UpsertPageUseCase
	indexPageUC    *pageusecase.IndexPageToSearchUseCase
	updateUsageUC  *pageusecase.UpdateSiteUsageUseCase

	logger *zap.Logger
}

// NewSyncPagesService creates a new SyncPagesService
func NewSyncPagesService(
	validateSiteUC *pageusecase.ValidateSiteUseCase,
	ensureIndexUC *pageusecase.EnsureSearchIndexUseCase,
	createPageUC *pageusecase.CreatePageEntityUseCase,
	upsertPageUC *pageusecase.UpsertPageUseCase,
	indexPageUC *pageusecase.IndexPageToSearchUseCase,
	updateUsageUC *pageusecase.UpdateSiteUsageUseCase,
	logger *zap.Logger,
) SyncPagesService {
	return &syncPagesService{
		validateSiteUC: validateSiteUC,
		ensureIndexUC:  ensureIndexUC,
		createPageUC:   createPageUC,
		upsertPageUC:   upsertPageUC,
		indexPageUC:    indexPageUC,
		updateUsageUC:  updateUsageUC,
		logger:         logger.Named("sync-pages-service"),
	}
}

// SyncPages orchestrates the page synchronization workflow
func (s *syncPagesService) SyncPages(ctx context.Context, tenantID, siteID gocql.UUID, input *pageusecase.SyncPagesInput) (*pageusecase.SyncPagesOutput, error) {
	s.logger.Info("syncing pages",
		zap.String("tenant_id", tenantID.String()),
		zap.String("site_id", siteID.String()),
		zap.Int("page_count", len(input.Pages)))

	// Step 1: Validate site (no quota check - usage-based billing)
	site, err := s.validateSiteUC.Execute(ctx, tenantID, siteID)
	if err != nil {
		s.logger.Error("failed to validate site", zap.Error(err))
		return nil, err
	}

	// Step 2: Ensure search index exists
	if err := s.ensureIndexUC.Execute(ctx, siteID); err != nil {
		s.logger.Error("failed to ensure search index", zap.Error(err))
		return nil, err
	}

	// Step 3: Process pages (create, save, prepare for indexing)
	syncedCount, failedPages, pagesToIndex := s.processPages(ctx, siteID, site.TenantID, input.Pages)

	// Step 4: Bulk index pages to search
	indexedCount, err := s.indexPageUC.Execute(ctx, siteID, pagesToIndex)
	if err != nil {
		s.logger.Error("failed to index pages", zap.Error(err))
		return nil, err
	}

	// Step 5: Update site usage tracking (for billing)
	if indexedCount > 0 {
		if err := s.updateUsageUC.Execute(ctx, site, indexedCount); err != nil {
			s.logger.Warn("failed to update usage (non-fatal)", zap.Error(err))
			// Don't fail the whole operation
		}
	}

	// Step 6: Build output
	message := fmt.Sprintf("Successfully synced %d pages, indexed %d pages", syncedCount, indexedCount)
	if len(failedPages) > 0 {
		message += fmt.Sprintf(", failed %d pages", len(failedPages))
	}

	s.logger.Info("pages synced successfully",
		zap.String("site_id", siteID.String()),
		zap.Int("synced", syncedCount),
		zap.Int("indexed", indexedCount),
		zap.Int("failed", len(failedPages)))

	return &pageusecase.SyncPagesOutput{
		SyncedCount:  syncedCount,
		IndexedCount: indexedCount,
		FailedPages:  failedPages,
		Message:      message,
	}, nil
}

// Helper: Process pages - create entities, save to DB, collect pages to index
func (s *syncPagesService) processPages(
	ctx context.Context,
	siteID, tenantID gocql.UUID,
	pages []pageusecase.SyncPageInput,
) (int, []string, []*domainpage.Page) {
	syncedCount := 0
	var failedPages []string
	var pagesToIndex []*domainpage.Page

	for _, pageInput := range pages {
		// Create page entity (usecase)
		page, err := s.createPageUC.Execute(siteID, tenantID, pageInput)
		if err != nil {
			failedPages = append(failedPages, pageInput.PageID)
			continue
		}

		// Save to database (usecase)
		if err := s.upsertPageUC.Execute(ctx, page); err != nil {
			failedPages = append(failedPages, pageInput.PageID)
			continue
		}

		syncedCount++

		// Collect pages that should be indexed
		if page.ShouldIndex() {
			pagesToIndex = append(pagesToIndex, page)
		}
	}

	return syncedCount, failedPages, pagesToIndex
}
