package app

import (
	"crypto/sha256"
	"encoding/hex"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/store/model"
	"github.com/tidbcloud/AgentTeam/internal/store/provider"
)

const serviceEventDeliveryCandidateMax = 100

func serviceEventStableKey(parts ...string) string {
	trimmed := make([]string, 0, len(parts))
	for _, part := range parts {
		trimmed = append(trimmed, strings.TrimSpace(part))
	}
	payload, _ := model.MarshalJSONObject("service event stable key", trimmed)
	sum := sha256.Sum256(payload)
	return hex.EncodeToString(sum[:])
}

func pullRequestSubscriptionSourceTypes() []string {
	return []string{
		pullRequestSubscriptionSourceType(PullRequestSubscriptionProviderGitHub, PullRequestSubscriptionResourcePullRequest),
		pullRequestSubscriptionSourceType(PullRequestSubscriptionProviderGitea, PullRequestSubscriptionResourcePullRequest),
	}
}

func pullRequestSubscriptionProviderFromSourceType(sourceType string) string {
	providerName, _, found := strings.Cut(strings.TrimSpace(sourceType), ".")
	if !found {
		return ""
	}
	return normalizePullRequestSubscriptionProvider(providerName)
}

type serviceEventProviderSourceConfig struct {
	Provider              string         `json:"provider,omitempty"`
	EventFamily           string         `json:"event_family,omitempty"`
	ResourceKind          string         `json:"resource_kind,omitempty"`
	ResourceRef           string         `json:"resource_ref,omitempty"`
	ResourceKey           string         `json:"resource_key,omitempty"`
	ProviderConnectionRef string         `json:"provider_connection_ref,omitempty"`
	EventFilters          map[string]any `json:"event_filters,omitempty"`
}

func decodeServiceEventProviderSourceConfig(payload []byte) serviceEventProviderSourceConfig {
	if len(payload) == 0 {
		return serviceEventProviderSourceConfig{}
	}
	var config serviceEventProviderSourceConfig
	_ = model.UnmarshalJSONObject("service event provider source config", payload, &config)
	config.Provider = normalizePullRequestSubscriptionProvider(config.Provider)
	config.EventFamily = strings.TrimSpace(config.EventFamily)
	config.ResourceKind = strings.TrimSpace(config.ResourceKind)
	config.ResourceRef = strings.TrimSpace(config.ResourceRef)
	config.ResourceKey = strings.TrimSpace(config.ResourceKey)
	config.ProviderConnectionRef = strings.TrimSpace(config.ProviderConnectionRef)
	if config.EventFilters == nil {
		config.EventFilters = map[string]any{}
	}
	return config
}

func serviceEventDeliveryBranchMetadata(payload []byte, keys ...string) string {
	metadata := decodeServiceEventSubscriptionJSONMap(payload)
	for _, key := range keys {
		if value, ok := metadata[key]; ok {
			if text, ok := value.(string); ok {
				if trimmed := strings.TrimSpace(text); trimmed != "" {
					return trimmed
				}
			}
		}
	}
	return ""
}

func stringFromServiceEventMetadata(value any) string {
	text, _ := value.(string)
	return strings.TrimSpace(text)
}

func serviceEventSubscriptionExternalRepositoryKey(providerName string, repositoryFullName string) string {
	providerKey := normalizePullRequestSubscriptionProvider(providerName)
	repositoryKey := strings.ToLower(strings.TrimSpace(repositoryFullName))
	if providerKey == "" || repositoryKey == "" {
		return ""
	}
	return providerKey + ":" + repositoryKey
}

type serviceEventSubscriptionMatchKeys struct {
	BaseBranchKey   string
	TargetBranchKey string
	HeadSHAKey      string
	BaseSHAKey      string
}

func serviceEventSubscriptionMatchKeysFromMetadata(metadata map[string]any) serviceEventSubscriptionMatchKeys {
	return serviceEventSubscriptionMatchKeys{
		BaseBranchKey:   strings.ToLower(stringFromServiceEventMetadata(metadata["base_branch"])),
		TargetBranchKey: strings.ToLower(stringFromServiceEventMetadata(metadata["target_branch"])),
		HeadSHAKey:      strings.ToLower(stringFromServiceEventMetadata(metadata["head_sha"])),
		BaseSHAKey:      strings.ToLower(stringFromServiceEventMetadata(metadata["base_sha"])),
	}
}

func pullRequestSubscriptionName(providerName string, resourceKey string) string {
	return pullRequestSubscriptionProviderDisplayName(providerName) + " pull request subscription " + strings.TrimSpace(resourceKey)
}

func serviceEventProviderEventFiltersMatch(filtersPayload []byte, event provider.ProviderEvent) bool {
	filters := decodeServiceEventSubscriptionJSONMap(filtersPayload)
	wanted := canonicalPullRequestSubscriptionEvents(pullRequestSubscriptionFilterEventsFromMap(filters))
	if len(wanted) == 0 {
		wanted = defaultPullRequestSubscriptionEventFilters()
	}
	actual := serviceEventProviderEventCategories(event)
	if len(actual) == 0 {
		return false
	}
	for _, eventName := range wanted {
		if _, ok := actual[eventName]; ok {
			return true
		}
	}
	return false
}

func serviceEventProviderEventCategories(event provider.ProviderEvent) map[string]struct{} {
	categories := map[string]struct{}{}
	eventKind := strings.ToLower(strings.TrimSpace(event.EventKind))
	actionKind := strings.ToLower(strings.TrimSpace(event.ActionKind))
	switch {
	case strings.Contains(eventKind, "issue_comment") || strings.Contains(eventKind, "review_comment"):
		categories["comments"] = struct{}{}
	case strings.Contains(eventKind, "review_submitted"):
		categories["reviews"] = struct{}{}
	case strings.Contains(eventKind, "synchronized") || strings.Contains(eventKind, "synchronize"):
		categories["synchronize"] = struct{}{}
	case strings.Contains(eventKind, "base_branch_pushed"):
		categories["base_branch_push"] = struct{}{}
	case strings.Contains(eventKind, "ci_status") || strings.Contains(eventKind, "workflow_run_status") || strings.Contains(eventKind, "workflow_job_status"):
		categories["status"] = struct{}{}
	case strings.Contains(eventKind, "reopened"):
		categories["reopen"] = struct{}{}
	case strings.Contains(eventKind, "closed"):
		if actionKind == "merged" {
			categories["merge"] = struct{}{}
		} else {
			categories["close"] = struct{}{}
		}
	case strings.Contains(eventKind, "status") || strings.Contains(eventKind, "check"):
		categories["status"] = struct{}{}
	}
	return categories
}

func serviceEventProviderEventCompletesSubscription(event provider.ProviderEvent) bool {
	categories := serviceEventProviderEventCategories(event)
	if _, ok := categories["close"]; ok {
		return true
	}
	_, ok := categories["merge"]
	return ok
}
