package sqls

import (
	"context"
	"errors"
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/log"
	"github.com/tidbcloud/observability/o11ydiagnosis/pkg/storage"
	"go.uber.org/zap"
)

const (
	statementsV2DeltaLastCheckpoint = "_delta_log/_last_checkpoint"
	// Present from the first commit; _last_checkpoint appears only after the first checkpoint.
	statementsV2DeltaInitialCommit = "_delta_log/00000000000000000000.json"
)

// ProbeStatementsDataSources checks whether statements v2 Delta Lake data exists
// via ObjectExists on _delta_log/_last_checkpoint, falling back to the initial
// commit log 00000000000000000000.json (no list, no meta, no DuckDB).
func (s *DuckDBAdapter) ProbeStatementsDataSources(ctx context.Context, params *StatementsDataSourceProbeParams) (*StatementsDataSourcesResponse, error) {
	if params == nil {
		return nil, fmt.Errorf("params is required")
	}
	if s.store == nil {
		return nil, fmt.Errorf("storage is not configured")
	}

	bucket := storage.BucketName(params.Env, params.Region, params.SharedTier, params.NextGen)
	for _, suffix := range []string{statementsV2DeltaLastCheckpoint, statementsV2DeltaInitialCommit} {
		key := statementsV2DeltaLogKey(params.TenantID, params.ClusterID, suffix)
		available, err := s.store.ObjectExists(ctx, bucket, key)
		if err != nil {
			return nil, fmt.Errorf("probe statements v2 %s: %w", suffix, err)
		}
		if available {
			return &StatementsDataSourcesResponse{V2Available: true}, nil
		}
	}
	return &StatementsDataSourcesResponse{V2Available: false}, nil
}

func statementsV2DeltaLogKey(tenantID, clusterID, deltaLogSuffix string) string {
	return fmt.Sprintf(
		"deltalake/org=%s/cluster=%s/type=sqlstatement/component=sqlstatement/%s",
		tenantID, clusterID, deltaLogSuffix,
	)
}

// @Summary		Probe statements v2 data source
// @Tags			statements
// @Description	Cheap check that statements v2 Delta Lake data exists (HEAD _delta_log/_last_checkpoint, else 00000000000000000000.json). Use before calling list/summary with data_source=v2.
// @Produce		json
// @Param			X-Provider		header		string	true	"Cloud provider"
// @Param			X-Region		header		string	true	"Cloud region"
// @Param			X-Org-Id		header		string	true	"Organization ID"
// @Param			X-Project-Id	header		string	false	"Project ID"
// @Param			X-Cluster-Id	header		string	true	"Cluster ID"
// @Param			X-Deploy-Type	header		string	true	"Deployment tier"
// @Success		200	{object}	StatementsDataSourcesResponse
// @Router			/statements/datasource [get]
func (s *Service) StatementsDataSourceHandler(c *gin.Context) {
	f := recordHttpMetric(c, "/datasource")
	defer f()

	var header ClusterHeader
	if err := c.BindHeader(&header); err != nil {
		log.Warn("failed to bind statements datasource header", zap.Error(err))
		c.JSON(http.StatusBadRequest, errorResponse{
			Error:    true,
			Message:  "Bad Request",
			FullText: err.Error(),
		})
		return
	}
	if err := header.Validate(); err != nil {
		log.Warn("invalid statements datasource header", zap.Error(err))
		c.JSON(http.StatusBadRequest, errorResponse{
			Error:    true,
			Message:  "Bad Request",
			FullText: err.Error(),
		})
		return
	}

	if *header.DeployType == "byoc" {
		*header.BizType = "byoc"
		*header.DeployType = "nextgen"
	}

	params := &StatementsDataSourceProbeParams{
		Env:        *header.Env,
		Provider:   *header.Provider,
		Region:     *header.Region,
		TenantID:   *header.TenantID,
		ClusterID:  *header.ClusterID,
		SharedTier: *header.DeployType == TierShared,
		NextGen:    *header.DeployType == TierNextGen || *header.DeployType == TierPremium,
	}

	resp, err := s.SQLsDuckDBAdapter.ProbeStatementsDataSources(c.Request.Context(), params)
	if err != nil {
		if errors.Is(err, ErrRateLimited) {
			c.JSON(http.StatusTooManyRequests, errorResponse{
				Error:    true,
				Message:  "rate limited",
				FullText: err.Error(),
			})
			return
		}
		log.Warn("statements datasource probe failed", zap.Error(err), zap.String("cluster", params.ClusterID))
		c.JSON(http.StatusInternalServerError, errorResponse{
			Error:    true,
			Message:  "failed to probe statements v2 data source",
			FullText: err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, resp)
}
