package app

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

	"github.com/tidbcloud/AgentTeam/internal/core/identifiers"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

const (
	pullRequestSubscriptionRateLimitActionCreate          = "create"
	pullRequestSubscriptionRateLimitActionList            = "list"
	pullRequestSubscriptionRateLimitActionPause           = "pause"
	pullRequestSubscriptionRateLimitActionResume          = "resume"
	pullRequestSubscriptionRateLimitActionRemove          = "remove"
	pullRequestSubscriptionRateLimitActionWebhookDelivery = "webhook_delivery"

	pullRequestSubscriptionRateLimitWindow = time.Minute
)

type pullRequestSubscriptionRateLimitEvent struct {
	WorkspaceID       string
	AppInstallationID string
	ActorType         string
	ActorID           string
	Action            string
	TargetKind        string
	TargetRef         string
	ResourceKey       string
	CorrelationID     string
	CreatedAt         time.Time
}

type pullRequestSubscriptionRateLimitConfig struct {
	ActorLimit        int
	InstallationLimit int
}

func recordPullRequestSubscriptionRateLimitEvent(ctx context.Context, tx coredb.Tx, event pullRequestSubscriptionRateLimitEvent) error {
	event.WorkspaceID = strings.TrimSpace(event.WorkspaceID)
	event.AppInstallationID = strings.TrimSpace(event.AppInstallationID)
	event.ActorType = strings.TrimSpace(event.ActorType)
	event.ActorID = strings.TrimSpace(event.ActorID)
	event.Action = strings.TrimSpace(event.Action)
	event.TargetKind = strings.TrimSpace(event.TargetKind)
	event.TargetRef = strings.TrimSpace(event.TargetRef)
	event.ResourceKey = strings.TrimSpace(event.ResourceKey)
	event.CorrelationID = strings.TrimSpace(event.CorrelationID)
	if event.WorkspaceID == "" || event.Action == "" {
		return badRequest("pull request subscription rate limit event is invalid")
	}
	config := pullRequestSubscriptionRateLimitConfigForAction(event.Action)
	if config.ActorLimit <= 0 && config.InstallationLimit <= 0 {
		return badRequest("pull request subscription rate limit action is not supported")
	}
	if event.CreatedAt.IsZero() {
		event.CreatedAt = time.Now().UTC()
	}
	windowStart := event.CreatedAt.Add(-pullRequestSubscriptionRateLimitWindow)
	if config.ActorLimit > 0 && event.ActorType != "" && event.ActorID != "" {
		count, err := countPullRequestSubscriptionRateLimitEvents(ctx, tx, `
SELECT COUNT(*)
FROM service_action_rate_limit_events
WHERE workspace_id = $1
  AND actor_type = $2
  AND actor_id = $3
  AND action = $4
  AND created_at >= $5
`, event.WorkspaceID, event.ActorType, event.ActorID, event.Action, windowStart)
		if err != nil {
			return err
		}
		if count >= config.ActorLimit {
			return storeerr.Conflict("pull_request_subscription_rate_limited")
		}
	}
	if config.InstallationLimit > 0 && event.AppInstallationID != "" {
		count, err := countPullRequestSubscriptionRateLimitEvents(ctx, tx, `
SELECT COUNT(*)
FROM service_action_rate_limit_events
WHERE service_installation_id = $1
  AND action = $2
  AND created_at >= $3
`, event.AppInstallationID, event.Action, windowStart)
		if err != nil {
			return err
		}
		if count >= config.InstallationLimit {
			return storeerr.Conflict("pull_request_subscription_rate_limited")
		}
	}
	eventID, err := identifiers.NewEntityID("prrl")
	if err != nil {
		return fmt.Errorf("generate pull request subscription rate limit event id: %w", err)
	}
	if _, err := tx.Exec(ctx, `
INSERT INTO service_action_rate_limit_events (
    id, workspace_id, owner_kind, owner_ref, service_installation_id, actor_type,
    actor_id, action, target_kind, target_ref, source_type, source_subject_key,
    correlation_id, created_at
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
`, eventID, event.WorkspaceID, "app", event.AppInstallationID, event.AppInstallationID, event.ActorType, event.ActorID, event.Action, event.TargetKind, event.TargetRef, "", event.ResourceKey, event.CorrelationID, event.CreatedAt); err != nil {
		return fmt.Errorf("record pull request subscription rate limit event: %w", err)
	}
	return nil
}

func countPullRequestSubscriptionRateLimitEvents(ctx context.Context, queryer coredb.QueryRower, query string, args ...any) (int, error) {
	var count int
	if err := queryer.QueryRow(ctx, query, args...).Scan(&count); err != nil {
		return 0, fmt.Errorf("count pull request subscription rate limit events: %w", err)
	}
	return count, nil
}

func pullRequestSubscriptionRateLimitConfigForAction(action string) pullRequestSubscriptionRateLimitConfig {
	switch strings.TrimSpace(action) {
	case pullRequestSubscriptionRateLimitActionCreate:
		return pullRequestSubscriptionRateLimitConfig{ActorLimit: 30, InstallationLimit: 120}
	case pullRequestSubscriptionRateLimitActionList:
		return pullRequestSubscriptionRateLimitConfig{ActorLimit: 120, InstallationLimit: 300}
	case pullRequestSubscriptionRateLimitActionPause, pullRequestSubscriptionRateLimitActionResume, pullRequestSubscriptionRateLimitActionRemove:
		return pullRequestSubscriptionRateLimitConfig{ActorLimit: 60, InstallationLimit: 180}
	case pullRequestSubscriptionRateLimitActionWebhookDelivery:
		return pullRequestSubscriptionRateLimitConfig{InstallationLimit: 120}
	default:
		return pullRequestSubscriptionRateLimitConfig{}
	}
}
