package installation

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

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

// LockExtensionNameNamespace takes the workspace-wide extension-name lock.
func LockExtensionNameNamespace(ctx context.Context, queryer coredb.QueryRower, workspaceID string) error {
	var lockedWorkspaceID string
	if err := queryer.QueryRow(ctx, `
SELECT id
FROM workspaces
WHERE id = $1
LIMIT 1
FOR UPDATE
`, strings.TrimSpace(workspaceID)).Scan(&lockedWorkspaceID); err != nil {
		return fmt.Errorf("lock extension name namespace: %w", err)
	}
	return nil
}

// ExtensionCapabilityConfig decodes the App's extensions capability.
func ExtensionCapabilityConfig(capabilities []serviceactor.Capability) (model.ExtensionCapabilityConfig, error) {
	for _, capability := range capabilities {
		if capability.CapabilityKey != serviceactor.CapabilityExtensions || !capability.DefaultEnabled {
			continue
		}
		config, err := model.DecodeExtensionCapabilityConfig(capability.Config)
		if err != nil {
			return model.ExtensionCapabilityConfig{}, fmt.Errorf("decode App Extension capability: %w", err)
		}
		return config, nil
	}
	return model.ExtensionCapabilityConfig{Extensions: []model.ExtensionCapabilityEntry{}}, nil
}

// SyncDefaultExtensions seeds the installation's default Extension
// selections. Personal installations claim owner-suffixed workspace-unique
// names; name conflicts disable the selection for a later explicit rename.
// The reconcile callback runs when an active selection was created.
func SyncDefaultExtensions(
	ctx context.Context,
	tx coredb.Tx,
	txStore *serviceactor.TxStore,
	installation serviceactor.Installation,
	createdByMemberID string,
	personalHandle func(defaultName string) string,
	selectionID func(installationID string, domain string) string,
	reconcile func() error,
	now time.Time,
) error {
	if installation.Status != serviceactor.StatusActive {
		return nil
	}
	capabilities, err := txStore.ListCapabilities(ctx, installation.ServiceDefinitionID)
	if err != nil {
		return err
	}
	config, err := ExtensionCapabilityConfig(capabilities)
	if err != nil {
		return err
	}
	if len(config.Extensions) == 0 {
		return nil
	}
	if err := LockExtensionNameNamespace(ctx, tx, installation.WorkspaceID); err != nil {
		return err
	}
	existing, err := txStore.ListInstallationExtensions(ctx, installation.WorkspaceID, installation.ID)
	if err != nil {
		return err
	}
	existingDomains := make(map[string]struct{}, len(existing))
	for _, selection := range existing {
		existingDomains[selection.DefinitionDomain] = struct{}{}
	}

	activeSelectionCreated := false
	for _, entry := range config.Extensions {
		if !entry.DefaultEnabled {
			continue
		}
		if _, found := existingDomains[entry.Domain]; found {
			continue
		}
		defaultName := entry.DefaultName
		if installation.OwnershipKind == serviceactor.OwnershipKindPersonal && personalHandle != nil {
			defaultName = personalHandle(entry.DefaultName)
		}
		status := serviceactor.StatusActive
		disabledReason := ""
		if claimed, found, err := txStore.LoadActiveInstallationExtensionByNameForUpdate(
			ctx,
			installation.WorkspaceID,
			defaultName,
		); err != nil {
			return err
		} else if found && claimed.ServiceInstallationID != installation.ID {
			status = serviceactor.StatusDisabled
			disabledReason = "extension_name_conflict"
		}
		selectionIDValue := installation.ID + "/" + entry.Domain
		if selectionID != nil {
			selectionIDValue = selectionID(installation.ID, entry.Domain)
		}
		selection, err := txStore.UpsertInstallationExtension(ctx, serviceactor.InstallationExtensionUpsert{
			InstallationExtension: serviceactor.InstallationExtension{
				ID:                    selectionIDValue,
				WorkspaceID:           installation.WorkspaceID,
				ServiceDefinitionID:   installation.ServiceDefinitionID,
				ServiceInstallationID: installation.ID,
				ExtensionName:         defaultName,
				DefinitionDomain:      entry.Domain,
				DefinitionVersion:     entry.Version,
				DefinitionDigest:      entry.DefinitionDigest,
				Status:                status,
				DisabledReason:        disabledReason,
				SelectionSource:       serviceactor.InstallationExtensionSourceAppDefault,
				CreatedByMemberID:     strings.TrimSpace(createdByMemberID),
			},
			Now: now,
		})
		if err != nil {
			return err
		}
		existingDomains[entry.Domain] = struct{}{}
		activeSelectionCreated = activeSelectionCreated || selection.Status == serviceactor.StatusActive
	}
	if !activeSelectionCreated {
		return nil
	}
	if reconcile == nil {
		return nil
	}
	return reconcile()
}
