package tidbtools

import (
	"context"
	"fmt"
	"strconv"
	"sync"

	"github.com/gin-gonic/gin"
	toolsdto "github.com/tidbcloud/devops/api/tidbtools"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	ccsvcapi "github.com/tidbcloud/devops/pkg/service/cloudconfig/api"
)

// overviewPageSize caps the per-tool row count in the overview fan-out.
// The Tools page shows only the most recent activity per card; a small
// page keeps the aggregate response cheap.
const overviewPageSize = int32(5)

// GetOverview
//
//	@Id				TidbToolsControllerGetOverview
//	@Description	aggregated overview across all tools for a cluster
//	@Tags			TidbToolsController
//	@Accept			json
//	@Produce		json
//	@Param			cluster_id	path		string	true	"cluster id"
//	@Success		200			{object}	tidbtools.GetOverviewResp
//	@Failure		default		{object}	api.BaseResp
//	@Router			/api/v1/devops/{cluster_id}/tidbtools/overview [get]
func (c *TidbToolsController) GetOverview(ctx *gin.Context) {
	clusterID, ci, ok := c.requireClusterAccess(ctx)
	if !ok {
		return
	}

	// Fan out to the implemented sub-tools. Each goroutine writes at most
	// one section of the response; failures are collected into Warnings
	// so one flaky tool never fails the entire overview.
	resp := &toolsdto.GetOverviewResp{}
	var mu sync.Mutex
	var wg sync.WaitGroup

	addWarning := func(tool string, err error) {
		mu.Lock()
		defer mu.Unlock()
		resp.Warnings = append(resp.Warnings, fmt.Sprintf("%s: %s", tool, err.Error()))
	}

	// Use the request's context so CheckClusterScope-propagated trace IDs
	// flow through. Per-RPC timeouts are applied at the service layer.
	rctx := context.WithoutCancel(ctx.Request.Context())

	if ci.clusterType == dedicatedType {
		wg.Add(1)
		go func() {
			defer wg.Done()
			pb, err := c.svc.ListDMJobs(rctx, ci.orgID, ci.projectID, clusterID, 1, overviewPageSize)
			if err != nil {
				addWarning("dm_jobs", err)
				return
			}
			mu.Lock()
			resp.DMJobs = toolsdto.ToListDMJobsResp(pb)
			mu.Unlock()
		}()

		wg.Add(1)
		go func() {
			defer wg.Done()
			pb, err := c.svc.ListImports(rctx, ci.orgID, ci.projectID, clusterID, 1, overviewPageSize)
			if err != nil {
				addWarning("imports", err)
				return
			}
			mu.Lock()
			resp.Imports = toolsdto.ToListImportsResp(pb)
			mu.Unlock()
		}()
	}

	if ci.clusterType == dedicatedType {
		wg.Add(1)
		go func() {
			defer wg.Done()
			pb, err := c.svc.ListBackups(rctx, ci.orgID, ci.projectID, clusterID, 1, uint32(overviewPageSize))
			if err != nil {
				addWarning("backups", err)
				return
			}
			mu.Lock()
			resp.Backups = toolsdto.ToListBackupsResp(pb)
			mu.Unlock()
		}()

		wg.Add(1)
		go func() {
			defer wg.Done()
			pb, err := c.svc.ListLogBackups(rctx, ci.orgID, ci.projectID, clusterID, 1, uint32(overviewPageSize))
			if err != nil {
				addWarning("log_backups", err)
				return
			}
			mu.Lock()
			resp.LogBackups = toolsdto.ToListLogBackupsResp(pb)
			mu.Unlock()
		}()

		wg.Add(1)
		go func() {
			defer wg.Done()
			pb, err := c.svc.ListBackupExports(rctx, ci.orgID, ci.projectID, clusterID, 1, uint32(overviewPageSize))
			if err != nil {
				addWarning("backup_exports", err)
				return
			}
			mu.Lock()
			resp.BackupExports = toolsdto.ToListBackupExportsResp(pb)
			mu.Unlock()
		}()

		wg.Add(1)
		go func() {
			defer wg.Done()
			pb, err := c.svc.ListRestores(rctx, ci.orgID, ci.projectID, clusterID, 1, uint32(overviewPageSize))
			if err != nil {
				addWarning("restores", err)
				return
			}
			mu.Lock()
			resp.Restores = toolsdto.ToListRestoresResp(pb)
			mu.Unlock()
		}()
	}

	if ci.clusterType == logicalType && supportsBackupRestore(ci.clusterType, ci.servicePlan) {
		clusterIDStr := strconv.FormatUint(clusterID, 10)
		wg.Add(1)
		go func() {
			defer wg.Done()
			pb, err := c.svc.ListPremiumBackups(rctx, ci.orgID, ci.projectID, clusterID, 1, overviewPageSize)
			if err != nil {
				addWarning("backups", err)
				return
			}
			mu.Lock()
			resp.Backups = toolsdto.ToListPremiumBackupsResp(pb)
			mu.Unlock()
		}()

		wg.Add(1)
		go func() {
			defer wg.Done()
			pb, err := c.svc.ListPremiumExports(rctx, ci.orgID, ci.projectID, clusterID, 1, overviewPageSize)
			if err != nil {
				addWarning("exports", err)
				return
			}
			mu.Lock()
			resp.Exports = toolsdto.ToListPremiumExportsResp(pb)
			mu.Unlock()
		}()

		wg.Add(1)
		go func() {
			defer wg.Done()
			pb, err := c.svc.GetPremiumRestoreStatus(rctx, ci.orgID, ci.projectID, clusterID)
			if err != nil {
				addWarning("restores", err)
				return
			}
			mu.Lock()
			resp.Restores = toolsdto.ToListPremiumRestoresResp(clusterIDStr, pb)
			mu.Unlock()
		}()
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		pb, err := c.cloudConfig.ListInternalChangefeeds(rctx, ccsvcapi.ListInternalChangefeedsParam{
			TiDBClusterID: clusterID,
			ClusterType:   string(ci.clusterType),
		})
		if err != nil {
			addWarning("changefeeds", err)
			return
		}
		mu.Lock()
		resp.Changefeeds = toolsdto.ToListChangefeedsOverviewResp(pb)
		mu.Unlock()
	}()

	wg.Wait()
	ctrl.Success(ctx, resp)
}
