package internal_api

import (
	"context"
	"errors"
	"net/http"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/log"
	"go.uber.org/zap"

	error2 "github.com/pingcap-inc/clinic/pkg/api/error"
	"github.com/pingcap-inc/clinic/pkg/config"
	"github.com/pingcap-inc/clinic/pkg/db"
	model2 "github.com/pingcap-inc/clinic/pkg/db/model"
	"github.com/pingcap-inc/clinic/pkg/o11y"
	"github.com/pingcap-inc/clinic/pkg/o11y/urls"
)

var errClusterDataURLsResolverUnavailable = errors.New("o11y resolver is unavailable")

type ClusterDataURLsService struct {
	cfg          *config.Config
	o11yResolver *urls.ClusterURLsResolver
}

func NewClusterDataURLsService(cfg *config.Config) *ClusterDataURLsService {
	// Initialize o11y resolver if enabled
	var o11yResolver *urls.ClusterURLsResolver
	if cfg.EnableO11yCanary {
		o11ySharedOpts := []o11y.Option{
			o11y.TimeoutOption(40 * time.Second),
			o11y.MaxIdleConnOption(10),
		}
		o11yCli, err := o11y.New(
			cfg.CentralO11yConfig.GSEndpoint,
			cfg.CentralO11yConfig.DSEndpointTPL,
			cfg.CentralO11yConfig.ClientId,
			cfg.CentralO11yConfig.ClientSecret,
			cfg.CentralO11yConfig.Env,
			append(o11ySharedOpts, o11y.ByocDSEndpointOption(cfg.CentralO11yConfig.ByocDSEndpoint))...,
		)
		if err != nil {
			log.Error("failed to create o11y client for ClusterDataURLsService", zap.Error(err))
		} else {
			o11yResolver = urls.NewClusterURLsResolver(o11yCli)
		}
	}

	return &ClusterDataURLsService{
		cfg:          cfg,
		o11yResolver: o11yResolver,
	}
}

// ResolveInternalReadTarget exposes the same capability-specific o11y lookup
// used by Clinic without duplicating deploy-type routing in internal handlers.
func (s *ClusterDataURLsService) ResolveInternalReadTarget(
	ctx context.Context,
	cluster *db.Cluster,
) (*urls.InternalReadTarget, error) {
	if s == nil || s.o11yResolver == nil {
		return nil, errClusterDataURLsResolverUnavailable
	}
	return s.o11yResolver.ResolveInternalReadTarget(ctx, cluster, urls.MetricsScopeChild, "")
}

// GetClusterDataURLs handles the request for getting cluster data URLs
// This is an internal API that returns comprehensive cluster information including o11y URLs
//
//	@Summary		Get cluster data URLs
//	@Tags			Internal
//	@Description	Get comprehensive cluster information including data URLs (internal API)
//	@Accept			json
//	@Param			cluster_id	path		string	true	"cluster id (cluster_id field, not the primary key id)"
//	@Success		200			{object}	ClusterDataURLsResponse
//	@Router			/internal/clusters/{cluster_id}/cluster-dataurls [get]
func (s *ClusterDataURLsService) GetClusterDataURLs(c *gin.Context) {
	clusterID := c.Param("cluster_id")
	if clusterID == "" {
		_ = c.Error(error2.ErrBadRequest.New("cluster_id is required"))
		return
	}

	// Get cluster info from DB with organization (no auth required for internal API)
	cluster, err := model2.GetAnyClusterWithOrg(clusterID)
	if err != nil {
		log.Error("failed to get cluster from db", zap.String("cluster_id", clusterID), zap.Error(err))
		_ = c.Error(error2.ErrNotFound.New("cluster not found"))
		return
	}

	// Build response
	resp := ClusterDataURLsResponse{
		ClusterName:    cluster.Name,
		ClusterID:      cluster.ClusterID,
		OrgID:          strconv.FormatUint(uint64(cluster.Organization.DbaasOrgID), 10),
		ProjectID:      cluster.ProjectID,
		Region:         cluster.Region,
		DeployType:     cluster.DeployType,
		DeployTypeV2:   cluster.DeployTypeV2,
		Provider:       cluster.Provider,
		Status:         cluster.Status,
		ParentID:       cluster.ParentID,
		ClinicTenantID: strconv.FormatUint(cluster.TenantID, 10),
	}

	// Set create time
	if cluster.CreationFinishedAt != nil {
		resp.CreateTime = cluster.CreationFinishedAt.Unix()
	} else {
		resp.CreateTime = cluster.CreatedAt.Unix()
	}

	// Resolve o11y URLs if resolver is available
	if s.o11yResolver != nil {
		ctx := c.Request.Context()
		clusterURLs, err := s.o11yResolver.Resolve(ctx, cluster)
		if err != nil {
			log.Error("failed to resolve cluster urls", zap.String("cluster_id", clusterID), zap.Error(err))
		} else {
			resp.ClusterMetricsURL = clusterURLs.ClusterMetricsURL
			resp.SlowQueryS3URL = clusterURLs.SlowQueryS3URL
			resp.StatementsS3URL = clusterURLs.StatementsS3URL
			resp.TopSQLS3URL = clusterURLs.TopSQLS3URL
			resp.LogStatsS3URL = clusterURLs.LogStatsS3URL
			resp.DataPlaneMetricsURL = clusterURLs.DataPlaneMetricsURL
			resp.LokiURL = clusterURLs.LokiURL
		}
	}

	c.JSON(http.StatusOK, resp)
}
