package page

import (
	"context"
	"fmt"

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

	domainpage "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/page"
	domainsite "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/site"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/search"
)

// DeletePagesUseCase handles page deletion
type DeletePagesUseCase struct {
	pageRepo     domainpage.Repository
	siteRepo     domainsite.Repository
	searchClient *search.Client
	logger       *zap.Logger
}

// ProvideDeletePagesUseCase creates a new DeletePagesUseCase
func ProvideDeletePagesUseCase(
	pageRepo domainpage.Repository,
	siteRepo domainsite.Repository,
	searchClient *search.Client,
	logger *zap.Logger,
) *DeletePagesUseCase {
	return &DeletePagesUseCase{
		pageRepo:     pageRepo,
		siteRepo:     siteRepo,
		searchClient: searchClient,
		logger:       logger,
	}
}

// DeletePagesInput is the input for deleting pages
type DeletePagesInput struct {
	PageIDs []string `json:"page_ids"`
}

// DeletePagesOutput is the output after deleting pages
type DeletePagesOutput struct {
	DeletedCount   int      `json:"deleted_count"`
	DeindexedCount int      `json:"deindexed_count"`
	FailedPages    []string `json:"failed_pages,omitempty"`
	Message        string   `json:"message"`
}

// Execute deletes pages from both database and search index
func (uc *DeletePagesUseCase) Execute(ctx context.Context, tenantID, siteID gocql.UUID, input *DeletePagesInput) (*DeletePagesOutput, error) {
	uc.logger.Info("executing delete pages use case",
		zap.String("tenant_id", tenantID.String()),
		zap.String("site_id", siteID.String()),
		zap.Int("page_count", len(input.PageIDs)))

	// Get site to validate
	site, err := uc.siteRepo.GetByID(ctx, tenantID, siteID)
	if err != nil {
		uc.logger.Error("failed to get site", zap.Error(err))
		return nil, domainsite.ErrSiteNotFound
	}

	// Verify site is verified (skip for test mode)
	if site.RequiresVerification() && !site.IsVerified {
		uc.logger.Warn("site not verified", zap.String("site_id", siteID.String()))
		return nil, domainsite.ErrSiteNotVerified
	}

	deletedCount := 0
	deindexedCount := 0
	var failedPages []string

	// Delete pages from database
	if len(input.PageIDs) > 1 {
		// Use batch delete for multiple pages
		if err := uc.pageRepo.DeleteMultiple(ctx, siteID, input.PageIDs); err != nil {
			uc.logger.Error("failed to batch delete pages", zap.Error(err))
			return nil, fmt.Errorf("failed to delete pages: %w", err)
		}
		deletedCount = len(input.PageIDs)
	} else if len(input.PageIDs) == 1 {
		// Single page delete
		if err := uc.pageRepo.Delete(ctx, siteID, input.PageIDs[0]); err != nil {
			uc.logger.Error("failed to delete page",
				zap.String("page_id", input.PageIDs[0]),
				zap.Error(err))
			failedPages = append(failedPages, input.PageIDs[0])
		} else {
			deletedCount = 1
		}
	}

	// Delete from search index
	if deletedCount > 0 {
		if len(input.PageIDs) > 1 {
			// Batch delete from Meilisearch
			_, err := uc.searchClient.DeleteDocuments(siteID.String(), input.PageIDs)
			if err != nil {
				uc.logger.Error("failed to delete documents from search index", zap.Error(err))
				// Don't fail the whole operation since database delete succeeded
			} else {
				deindexedCount = len(input.PageIDs)
			}
		} else if len(input.PageIDs) == 1 && len(failedPages) == 0 {
			// Single document delete
			_, err := uc.searchClient.DeleteDocument(siteID.String(), input.PageIDs[0])
			if err != nil {
				uc.logger.Error("failed to delete document from search index",
					zap.String("page_id", input.PageIDs[0]),
					zap.Error(err))
				// Don't fail the whole operation since database delete succeeded
			} else {
				deindexedCount = 1
			}
		}
	}

	uc.logger.Info("pages deleted successfully",
		zap.String("site_id", siteID.String()),
		zap.Int("deleted", deletedCount),
		zap.Int("deindexed", deindexedCount),
		zap.Int("failed", len(failedPages)))

	message := fmt.Sprintf("Successfully deleted %d pages from database, removed %d from search index", deletedCount, deindexedCount)
	if len(failedPages) > 0 {
		message += fmt.Sprintf(", failed %d pages", len(failedPages))
	}

	return &DeletePagesOutput{
		DeletedCount:   deletedCount,
		DeindexedCount: deindexedCount,
		FailedPages:    failedPages,
		Message:        message,
	}, nil
}

// DeleteAllPagesInput is the input for deleting all pages for a site
type DeleteAllPagesInput struct{}

// ExecuteDeleteAll deletes all pages for a site
func (uc *DeletePagesUseCase) ExecuteDeleteAll(ctx context.Context, tenantID, siteID gocql.UUID) (*DeletePagesOutput, error) {
	uc.logger.Info("executing delete all pages use case",
		zap.String("tenant_id", tenantID.String()),
		zap.String("site_id", siteID.String()))

	// Get site to validate
	site, err := uc.siteRepo.GetByID(ctx, tenantID, siteID)
	if err != nil {
		uc.logger.Error("failed to get site", zap.Error(err))
		return nil, domainsite.ErrSiteNotFound
	}

	// Verify site is verified (skip for test mode)
	if site.RequiresVerification() && !site.IsVerified {
		uc.logger.Warn("site not verified", zap.String("site_id", siteID.String()))
		return nil, domainsite.ErrSiteNotVerified
	}

	// Count pages before deletion
	count, err := uc.pageRepo.CountBySiteID(ctx, siteID)
	if err != nil {
		uc.logger.Error("failed to count pages", zap.Error(err))
		return nil, fmt.Errorf("failed to count pages: %w", err)
	}

	// Delete all pages from database
	if err := uc.pageRepo.DeleteBySiteID(ctx, siteID); err != nil {
		uc.logger.Error("failed to delete all pages", zap.Error(err))
		return nil, fmt.Errorf("failed to delete pages: %w", err)
	}

	// Delete all documents from search index
	_, err = uc.searchClient.DeleteAllDocuments(siteID.String())
	if err != nil {
		uc.logger.Error("failed to delete all documents from search index", zap.Error(err))
		// Don't fail the whole operation since database delete succeeded
	}

	uc.logger.Info("all pages deleted successfully",
		zap.String("site_id", siteID.String()),
		zap.Int64("count", count))

	return &DeletePagesOutput{
		DeletedCount:   int(count),
		DeindexedCount: int(count),
		Message:        fmt.Sprintf("Successfully deleted all %d pages", count),
	}, nil
}
