package page

import (
	"context"
	"fmt"
	"time"

	"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"
)

// GetSyncStatusUseCase handles retrieving synchronization status
type GetSyncStatusUseCase struct {
	pageRepo     domainpage.Repository
	siteRepo     domainsite.Repository
	searchClient *search.Client
	logger       *zap.Logger
}

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

// SyncStatusOutput provides synchronization status information
type SyncStatusOutput struct {
	SiteID              string    `json:"site_id"`
	TotalPages          int64     `json:"total_pages"`
	PublishedPages      int64     `json:"published_pages"`
	DraftPages          int64     `json:"draft_pages"`
	LastSyncedAt        time.Time `json:"last_synced_at"`
	PagesIndexedMonth   int64     `json:"pages_indexed_month"`   // Usage tracking
	SearchRequestsMonth int64     `json:"search_requests_month"` // Usage tracking
	LastResetAt         time.Time `json:"last_reset_at"`         // Monthly billing cycle
	SearchIndexStatus   string    `json:"search_index_status"`
	SearchIndexDocCount int64     `json:"search_index_doc_count"`
}

// Execute retrieves the current sync status for a site
func (uc *GetSyncStatusUseCase) Execute(ctx context.Context, tenantID, siteID gocql.UUID) (*SyncStatusOutput, error) {
	uc.logger.Info("executing get sync status use case",
		zap.String("tenant_id", tenantID.String()),
		zap.String("site_id", siteID.String()))

	// Get site to validate and get quota information
	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 total pages in database
	totalPages, 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)
	}

	// Get all pages to count by status (this could be optimized with a dedicated query)
	pages, err := uc.pageRepo.GetBySiteID(ctx, siteID)
	if err != nil {
		uc.logger.Error("failed to get pages", zap.Error(err))
		return nil, fmt.Errorf("failed to get pages: %w", err)
	}

	// Count pages by status
	var publishedPages, draftPages int64
	for _, page := range pages {
		if page.Status == "publish" {
			publishedPages++
		} else if page.Status == "draft" {
			draftPages++
		}
	}

	// Check search index status
	indexStatus := "not_created"
	var indexDocCount int64 = 0

	indexExists, err := uc.searchClient.IndexExists(siteID.String())
	if err != nil {
		uc.logger.Error("failed to check index existence", zap.Error(err))
		indexStatus = "error"
	} else if indexExists {
		indexStatus = "active"

		// Get index stats
		stats, err := uc.searchClient.GetStats(siteID.String())
		if err != nil {
			uc.logger.Error("failed to get index stats", zap.Error(err))
		} else {
			indexDocCount = stats.NumberOfDocuments
		}
	}

	uc.logger.Info("sync status retrieved successfully",
		zap.String("site_id", siteID.String()),
		zap.Int64("total_pages", totalPages),
		zap.Int64("published", publishedPages),
		zap.Int64("draft", draftPages))

	return &SyncStatusOutput{
		SiteID:              siteID.String(),
		TotalPages:          totalPages,
		PublishedPages:      publishedPages,
		DraftPages:          draftPages,
		LastSyncedAt:        site.LastIndexedAt,
		PagesIndexedMonth:   site.MonthlyPagesIndexed,
		SearchRequestsMonth: site.SearchRequestsCount,
		LastResetAt:         site.LastResetAt,
		SearchIndexStatus:   indexStatus,
		SearchIndexDocCount: indexDocCount,
	}, nil
}

// GetPageDetailsInput is the input for getting page details
type GetPageDetailsInput struct {
	PageID string `json:"page_id"`
}

// PageDetailsOutput provides detailed information about a specific page
type PageDetailsOutput struct {
	PageID           string    `json:"page_id"`
	Title            string    `json:"title"`
	Excerpt          string    `json:"excerpt"`
	URL              string    `json:"url"`
	Status           string    `json:"status"`
	PostType         string    `json:"post_type"`
	Author           string    `json:"author"`
	PublishedAt      time.Time `json:"published_at"`
	ModifiedAt       time.Time `json:"modified_at"`
	IndexedAt        time.Time `json:"indexed_at"`
	MeilisearchDocID string    `json:"meilisearch_doc_id"`
	IsIndexed        bool      `json:"is_indexed"`
}

// ExecuteGetPageDetails retrieves detailed information about a specific page
func (uc *GetSyncStatusUseCase) ExecuteGetPageDetails(ctx context.Context, tenantID, siteID gocql.UUID, input *GetPageDetailsInput) (*PageDetailsOutput, error) {
	uc.logger.Info("executing get page details use case",
		zap.String("tenant_id", tenantID.String()),
		zap.String("site_id", siteID.String()),
		zap.String("page_id", input.PageID))

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

	// Get page from database
	page, err := uc.pageRepo.GetByID(ctx, siteID, input.PageID)
	if err != nil {
		uc.logger.Error("failed to get page", zap.Error(err))
		return nil, fmt.Errorf("page not found")
	}

	// Check if page is indexed in Meilisearch
	isIndexed := !page.IndexedAt.IsZero()

	uc.logger.Info("page details retrieved successfully",
		zap.String("site_id", siteID.String()),
		zap.String("page_id", input.PageID))

	return &PageDetailsOutput{
		PageID:           page.PageID,
		Title:            page.Title,
		Excerpt:          page.Excerpt,
		URL:              page.URL,
		Status:           page.Status,
		PostType:         page.PostType,
		Author:           page.Author,
		PublishedAt:      page.PublishedAt,
		ModifiedAt:       page.ModifiedAt,
		IndexedAt:        page.IndexedAt,
		MeilisearchDocID: page.MeilisearchDocID,
		IsIndexed:        isIndexed,
	}, nil
}
