package public

import (
	"context"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/versioning"
)

const (
	registrationModeOpen       = "open"
	registrationModeInviteOnly = "invite_only"
	publicConfigUnknownVersion = "unknown"
)

type MessageSearchSupportStore interface {
	SupportsMessageSearch(ctx context.Context) bool
}

type MigrationStateInspector interface {
	InspectMigrationState(context.Context, coredb.AdminDB) (coredb.MigrationState, error)
}

// PublicConfigHandler serves unauthenticated browser bootstrap configuration.
type PublicConfigHandler struct {
	store                        MessageSearchSupportStore
	migrationInspector           MigrationStateInspector
	coreDB                       coredb.AdminDB
	environment                  string
	pingCAPAuthEnabled           bool
	passwordAuthDisabled         bool
	openRegistration             bool
	registrationMode             string
	teamsEnabled                 bool
	agentToolsSettingsEnabled    bool
	organizationDirectoryEnabled bool
	organizationBYOKEnabled      bool
	billingEnabled               bool
	daemonConnectBaseURL         string
	meshXEnabled                 bool
	meshXKnowledgeBasesEnabled   bool
	meshXWorkflowsEnabled        bool
	meshXMiniappPairingEnabled   bool
	meshXRolesEnabled            bool
	meshXBaseURL                 string
}

// Get handles GET /api/v1/public/config.
// It returns the public server configuration consumed before login.
func (h *PublicConfigHandler) Get(ctx *gin.Context) {
	messageSearchEnabled := false
	if h.store != nil {
		messageSearchEnabled = h.store.SupportsMessageSearch(ctx.Request.Context())
	}
	deploymentEnvironment := ""
	serverBuildTime := ""
	var coreSchemaVersion *int64
	if isStagingEnvironment(h.environment) {
		deploymentEnvironment = "staging"
		serverBuildTime = versioning.ServerBuildTime()
		coreSchemaVersion = h.coreSchemaVersion(ctx.Request.Context())
	}
	ctx.JSON(http.StatusOK, dto.AppPublicConfigResponse{
		PingCAPAuthEnabled:  h.pingCAPAuthEnabled,
		PasswordAuthEnabled: !h.passwordAuthDisabled,
		OpenRegistration:    h.openRegistration,
		RegistrationMode:    normalizeRegistrationMode(h.registrationMode),
		// Keep the wire name for app compatibility. Public history window enforcement is temporarily
		// disabled, so expose 0 to mean "no active limit" instead of leaking stale retention config.
		MessageHistoryDays:           0,
		MessageSearchEnabled:         messageSearchEnabled,
		TeamsEnabled:                 h.teamsEnabled,
		AgentToolsSettingsEnabled:    h.agentToolsSettingsEnabled,
		OrganizationDirectoryEnabled: h.organizationDirectoryEnabled,
		OrganizationBYOKEnabled:      h.organizationBYOKEnabled,
		BillingEnabled:               h.billingEnabled,
		DaemonConnectBaseURL:         daemonConnectBaseURLForRequest(ctx.Request, h.daemonConnectBaseURL),
		MeshXEnabled:                 h.meshXEnabled,
		MeshXKnowledgeBasesEnabled:   h.meshXKnowledgeBasesEnabled,
		MeshXWorkflowsEnabled:        h.meshXWorkflowsEnabled,
		MeshXMiniappPairingEnabled:   h.meshXMiniappPairingEnabled,
		MeshXRolesEnabled:            h.meshXRolesEnabled,
		MeshXBaseURL:                 strings.TrimSpace(h.meshXBaseURL),
		DesktopQuickStartEnabled:     h.billingEnabled,
		ServerVersion:                publicConfigServerVersion(),
		ServerGitHash:                versioning.ServerGitCommit(),
		DeploymentEnvironment:        deploymentEnvironment,
		ServerBuildTime:              serverBuildTime,
		CoreSchemaVersion:            coreSchemaVersion,
	})
}

func publicConfigServerVersion() string {
	if version := strings.TrimSpace(versioning.ServerVersion()); version != "" {
		return version
	}
	return publicConfigUnknownVersion
}

func (h *PublicConfigHandler) coreSchemaVersion(ctx context.Context) *int64 {
	if h.migrationInspector == nil || h.coreDB == nil {
		return nil
	}
	state, err := h.migrationInspector.InspectMigrationState(ctx, h.coreDB)
	if err != nil {
		return nil
	}
	version := state.CurrentVersion
	return &version
}

func isStagingEnvironment(environment string) bool {
	return strings.EqualFold(strings.TrimSpace(environment), "staging")
}

func normalizeRegistrationMode(mode string) string {
	switch strings.ToLower(strings.TrimSpace(mode)) {
	case registrationModeInviteOnly:
		return registrationModeInviteOnly
	case registrationModeOpen, "":
		return registrationModeOpen
	default:
		return strings.ToLower(strings.TrimSpace(mode))
	}
}

func daemonConnectBaseURLForRequest(request *http.Request, configuredBaseURL string) string {
	if trimmed := strings.TrimSpace(configuredBaseURL); trimmed != "" {
		return trimmed
	}

	host := forwardedHeaderValue(request.Header.Get("X-Forwarded-Host"))
	if host == "" {
		host = strings.TrimSpace(request.Host)
	}
	if host == "" {
		return ""
	}

	scheme := strings.ToLower(forwardedHeaderValue(request.Header.Get("X-Forwarded-Proto")))
	switch scheme {
	case "http", "https":
	case "":
		if request.URL != nil && request.URL.Scheme != "" {
			scheme = strings.ToLower(strings.TrimSpace(request.URL.Scheme))
		}
		if scheme == "" {
			if request.TLS != nil {
				scheme = "https"
			} else {
				scheme = "http"
			}
		}
	default:
		if request.TLS != nil {
			scheme = "https"
		} else {
			scheme = "http"
		}
	}

	return scheme + "://" + host
}

func forwardedHeaderValue(raw string) string {
	if comma := strings.Index(raw, ","); comma >= 0 {
		raw = raw[:comma]
	}
	return strings.TrimSpace(raw)
}
