package runtime

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/app/installation"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
)

const (
	DefaultCapabilityTTL = time.Minute
	MaxCapabilityTTL     = 5 * time.Minute
)

// AuthenticatedInstallation is the app runtime auth result for a bearer capability token.
type AuthenticatedInstallation struct {
	InstallationID string
	AppID          string
	AppSlug        string
	AppName        string
	WorkspaceID    string
	MemberID       string
	Handle         string
	RequestID      string
	TokenScopes    []string
	GrantedScopes  []string
}

// InstallationAuthRecord is the immutable installation identity and current
// grant set used to mint and validate short-lived App capability tokens.
type InstallationAuthRecord struct {
	InstallationID string
	AppID          string
	AppSlug        string
	AppName        string
	WorkspaceID    string
	MemberID       string
	Handle         string
	Enabled        bool
	GrantedScopes  []string
}

// LoadInstallationAuthRecord loads the active identity and current grant set
// for one App installation. The two reads remain bounded by installationID.
func LoadInstallationAuthRecord(ctx context.Context, queryer coredb.Queryer, installationID string) (InstallationAuthRecord, error) {
	row := queryer.QueryRow(ctx, `
SELECT
    installation.id,
    definition.id,
    definition.slug,
    definition.name,
    installation.workspace_id,
    service_member.member_id,
    service_member.handle,
    installation.status,
    service_member.status
FROM service_installations installation
JOIN service_definitions definition
  ON definition.id = installation.service_definition_id
 AND definition.service_kind = installation.service_kind
JOIN service_members service_member
  ON service_member.service_installation_id = installation.id
 AND service_member.service_kind = installation.service_kind
WHERE installation.id = $1
  AND installation.service_kind = $2
`, strings.TrimSpace(installationID), serviceactor.KindApp)

	var record InstallationAuthRecord
	var installationStatus string
	var memberStatus string
	if err := row.Scan(
		&record.InstallationID,
		&record.AppID,
		&record.AppSlug,
		&record.AppName,
		&record.WorkspaceID,
		&record.MemberID,
		&record.Handle,
		&installationStatus,
		&memberStatus,
	); err != nil {
		if coredb.IsNotFound(err) {
			return InstallationAuthRecord{}, ErrNotFound
		}
		return InstallationAuthRecord{}, fmt.Errorf("load app service actor auth record: %w", err)
	}
	record.InstallationID = strings.TrimSpace(record.InstallationID)
	record.AppID = strings.TrimSpace(record.AppID)
	record.AppSlug = strings.TrimSpace(record.AppSlug)
	record.AppName = strings.TrimSpace(record.AppName)
	record.WorkspaceID = strings.TrimSpace(record.WorkspaceID)
	record.MemberID = strings.TrimSpace(record.MemberID)
	record.Handle = strings.TrimSpace(record.Handle)
	record.Enabled = strings.TrimSpace(installationStatus) == serviceactor.StatusActive && strings.TrimSpace(memberStatus) == serviceactor.StatusActive

	grantsByInstallation, err := installation.LoadActiveGrantScopesByInstallation(ctx, queryer, []string{record.InstallationID})
	if err != nil {
		return InstallationAuthRecord{}, fmt.Errorf("load app service actor grants: %w", err)
	}
	record.GrantedScopes = grantsByInstallation[record.InstallationID]
	if record.GrantedScopes == nil {
		record.GrantedScopes = []string{}
	}
	return record, nil
}

// NormalizeCapabilityScopes trims and de-duplicates app capability scopes while preserving order.
func NormalizeCapabilityScopes(scopes []string) []string {
	if len(scopes) == 0 {
		return nil
	}

	seen := make(map[string]struct{}, len(scopes))
	normalized := make([]string, 0, len(scopes))
	for _, scope := range scopes {
		trimmed := strings.TrimSpace(scope)
		if trimmed == "" {
			continue
		}
		if _, exists := seen[trimmed]; exists {
			continue
		}
		seen[trimmed] = struct{}{}
		normalized = append(normalized, trimmed)
	}
	if len(normalized) == 0 {
		return nil
	}
	return normalized
}

// CapabilityScopeSetCovers reports whether every requested scope is covered by grantedScopes.
func CapabilityScopeSetCovers(grantedScopes []string, requestedScopes []string) bool {
	if len(requestedScopes) == 0 {
		return true
	}

	allowed := make(map[string]struct{}, len(grantedScopes))
	for _, scope := range grantedScopes {
		allowed[strings.TrimSpace(scope)] = struct{}{}
	}
	for _, scope := range requestedScopes {
		if _, exists := allowed[strings.TrimSpace(scope)]; !exists {
			return false
		}
	}
	return true
}
