package catalogreconcile

import (
	"context"
	"encoding/json"
	"fmt"
	"sort"
	"strings"

	"github.com/nexuscrm/metadata/catalogsnapshot"
	consumerv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	"github.com/pingcap-inc/nexuscrm-controlplane/catalogapp"
	"github.com/pingcap-inc/nexuscrm-controlplane/componentbehavior"
)

type EffectiveOAuthAuthorityEntry struct {
	BusinessKey string
	OwnerPack   string
}

type EffectiveOAuthAuthorityMetadataReader interface {
	GetEffectiveConnectedApps(context.Context) ([]EffectiveOAuthAuthorityEntry, error)
	GetEffectiveOAuthCustomScopes(context.Context) ([]EffectiveOAuthAuthorityEntry, error)
}

type effectiveOAuthAuthorityBehavior struct {
	Schema    string `json:"schema"`
	OwnerPack string `json:"ownerPack,omitempty"`
}

func buildEffectiveOAuthAuthorityComponents(
	ctx context.Context,
	reader EffectiveOAuthAuthorityMetadataReader,
) ([]catalogapp.EffectiveMetadataComponent, error) {
	if reader == nil {
		return nil, fmt.Errorf("effective OAuth authority publication requires reader")
	}
	connectedApps, err := reader.GetEffectiveConnectedApps(ctx)
	if err != nil {
		return nil, fmt.Errorf("read effective Connected Apps: %w", err)
	}
	scopes, err := reader.GetEffectiveOAuthCustomScopes(ctx)
	if err != nil {
		return nil, fmt.Errorf("read effective OAuth custom scopes: %w", err)
	}
	result := make([]catalogapp.EffectiveMetadataComponent, 0, 1+len(connectedApps)+len(scopes))
	marker, err := effectiveOAuthAuthorityComponent(
		catalogsnapshot.EffectiveOAuthAuthorityMarkerComponentKind,
		catalogsnapshot.EffectiveOAuthAuthorityMarkerBusinessKey,
		effectiveOAuthAuthorityBehavior{Schema: "nexuscrm.effective-oauth-authority/v1"},
	)
	if err != nil {
		return nil, err
	}
	result = append(result, marker)
	for _, group := range []struct {
		kind    string
		name    string
		entries []EffectiveOAuthAuthorityEntry
		fold    bool
	}{
		{catalogsnapshot.EffectiveConnectedAppComponentKind, "Connected App", connectedApps, false},
		{catalogsnapshot.EffectiveOAuthCustomScopeComponentKind, "OAuth custom scope", scopes, true},
	} {
		seen := make(map[string]struct{}, len(group.entries))
		for index, entry := range group.entries {
			key := strings.TrimSpace(entry.BusinessKey)
			owner := strings.TrimSpace(entry.OwnerPack)
			identity := key
			if group.fold {
				key = strings.ToLower(key)
				identity = key
			}
			if key == "" || entry.BusinessKey != strings.TrimSpace(entry.BusinessKey) {
				return nil, fmt.Errorf("effective %s[%d] is invalid", group.name, index)
			}
			if _, duplicate := seen[identity]; duplicate {
				return nil, fmt.Errorf("effective %s contains duplicate %q", group.name, key)
			}
			seen[identity] = struct{}{}
			component, err := effectiveOAuthAuthorityComponent(
				group.kind,
				key,
				effectiveOAuthAuthorityBehavior{
					Schema:    "nexuscrm.effective-oauth-authority-entry/v1",
					OwnerPack: owner,
				},
			)
			if err != nil {
				return nil, err
			}
			result = append(result, component)
		}
	}
	sort.Slice(result, func(left, right int) bool {
		if result[left].ComponentKind != result[right].ComponentKind {
			return result[left].ComponentKind < result[right].ComponentKind
		}
		return result[left].BusinessKey < result[right].BusinessKey
	})
	return result, nil
}

func effectiveOAuthAuthorityComponent(
	kind string,
	businessKey string,
	behavior effectiveOAuthAuthorityBehavior,
) (catalogapp.EffectiveMetadataComponent, error) {
	encoded, err := json.Marshal(behavior)
	if err != nil {
		return catalogapp.EffectiveMetadataComponent{}, err
	}
	canonical, err := componentbehavior.CanonicalJSONV1(encoded)
	if err != nil {
		return catalogapp.EffectiveMetadataComponent{}, err
	}
	return catalogapp.EffectiveMetadataComponent{
		ComponentKind:          kind,
		ComponentFormatVersion: 1,
		BusinessKey:            businessKey,
		ContentDigest:          consumerv1.ComputeEffectiveMetadataComponentDigest(canonical),
		CanonicalBehavior:      canonical,
	}, nil
}

func (r *RepositoryMetadataReader) GetEffectiveConnectedApps(
	ctx context.Context,
) ([]EffectiveOAuthAuthorityEntry, error) {
	rows, err := r.GetExecutor(ctx).QueryContext(ctx, `SELECT d.ConnectedAppId, d.InstalledPackName
FROM _System_ConnectedAppDeclaration AS d
JOIN _System_ConnectedApp AS c ON c.Id = d.ConnectedAppId
WHERE COALESCE(d.IsDeleted, 0) = 0 AND c.IsActive = 1
ORDER BY BINARY d.ConnectedAppId, BINARY d.InstalledPackName`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var result []EffectiveOAuthAuthorityEntry
	for rows.Next() {
		var value EffectiveOAuthAuthorityEntry
		if err := rows.Scan(&value.BusinessKey, &value.OwnerPack); err != nil {
			return nil, err
		}
		result = append(result, value)
	}
	return result, rows.Err()
}

func (r *RepositoryMetadataReader) GetEffectiveOAuthCustomScopes(
	ctx context.Context,
) ([]EffectiveOAuthAuthorityEntry, error) {
	rows, err := r.GetExecutor(ctx).QueryContext(ctx, `SELECT Name, COALESCE(OwnerPack, '')
FROM _System_OAuthCustomScope
WHERE IsActive = 1
ORDER BY BINARY Name, BINARY COALESCE(OwnerPack, '')`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var result []EffectiveOAuthAuthorityEntry
	for rows.Next() {
		var value EffectiveOAuthAuthorityEntry
		if err := rows.Scan(&value.BusinessKey, &value.OwnerPack); err != nil {
			return nil, err
		}
		result = append(result, value)
	}
	return result, rows.Err()
}

var _ EffectiveOAuthAuthorityMetadataReader = (*RepositoryMetadataReader)(nil)
