package app

import (
	"context"
	"strings"
	"time"

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

func (store *Store) PauseServiceEventSubscription(ctx context.Context, workspaceID string, actor ServiceEventActor, subscriptionID string, reason string) (ServiceEventSubscriptionRecord, error) {
	return store.setServiceEventSubscriptionStatus(ctx, workspaceID, actor, subscriptionID, ServiceEventSubscriptionStatusPaused, reason, pullRequestSubscriptionRateLimitActionPause)
}

func (store *Store) ResumeServiceEventSubscription(ctx context.Context, workspaceID string, actor ServiceEventActor, subscriptionID string) (ServiceEventSubscriptionRecord, error) {
	return store.setServiceEventSubscriptionStatus(ctx, workspaceID, actor, subscriptionID, ServiceEventSubscriptionStatusActive, "", pullRequestSubscriptionRateLimitActionResume)
}

// RemoveServiceEventSubscription removes the canonical subscription so a
// future create can make a fresh active subscription for the same tuple.
func (store *Store) RemoveServiceEventSubscription(ctx context.Context, workspaceID string, actor ServiceEventActor, subscriptionID string) (ServiceEventSubscriptionRecord, error) {
	trimmedWorkspaceID := strings.TrimSpace(workspaceID)
	trimmedSubscriptionID := strings.TrimSpace(subscriptionID)
	if trimmedWorkspaceID == "" || trimmedSubscriptionID == "" {
		return ServiceEventSubscriptionRecord{}, badRequest("workspaceId and subscriptionId are required")
	}
	if err := validateServiceEventActor(actor); err != nil {
		return ServiceEventSubscriptionRecord{}, err
	}
	var subscription ServiceEventSubscriptionRecord
	if err := coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		loaded, err := loadServiceEventSubscriptionForUpdate(ctx, tx, trimmedWorkspaceID, trimmedSubscriptionID)
		if err != nil {
			return err
		}
		if !isPullRequestServiceEventSubscription(loaded) {
			return storeerr.NotFound("watch_not_found")
		}
		if err := requireServiceEventActorAuthorization(ctx, tx, trimmedWorkspaceID, actor, loaded.AppInstallationID, []string{ServiceEventSubscriptionScopeWrite}); err != nil {
			return err
		}
		if err := requireServiceEventActorOwnsSubscription(ctx, tx, trimmedWorkspaceID, actor, loaded); err != nil {
			return err
		}
		now := time.Now().UTC()
		if err := recordPullRequestSubscriptionRateLimitEvent(ctx, tx, pullRequestSubscriptionRateLimitEvent{
			WorkspaceID:       trimmedWorkspaceID,
			AppInstallationID: loaded.AppInstallationID,
			ActorType:         actor.ActorType,
			ActorID:           actor.ActorID,
			Action:            pullRequestSubscriptionRateLimitActionRemove,
			TargetKind:        loaded.TargetKind,
			TargetRef:         loaded.TargetRef,
			ResourceKey:       loaded.ResourceKey,
			CorrelationID:     loaded.CorrelationID,
			CreatedAt:         now,
		}); err != nil {
			return err
		}
		removed, err := store.serviceActorStore().InTx(tx).RemoveEventSubscription(ctx, trimmedWorkspaceID, loaded.ID, now)
		if err != nil {
			return err
		}
		loaded.UpdatedAt = now
		loaded.RemovedAt = removed.RemovedAt
		subscription = loaded
		return nil
	}); err != nil {
		return ServiceEventSubscriptionRecord{}, err
	}
	return subscription, nil
}

func (store *Store) setServiceEventSubscriptionStatus(ctx context.Context, workspaceID string, actor ServiceEventActor, subscriptionID string, status string, reason string, rateLimitAction string) (ServiceEventSubscriptionRecord, error) {
	trimmedWorkspaceID := strings.TrimSpace(workspaceID)
	trimmedSubscriptionID := strings.TrimSpace(subscriptionID)
	if trimmedWorkspaceID == "" || trimmedSubscriptionID == "" {
		return ServiceEventSubscriptionRecord{}, badRequest("workspaceId and subscriptionId are required")
	}
	if err := validateServiceEventActor(actor); err != nil {
		return ServiceEventSubscriptionRecord{}, err
	}
	if !validServiceEventSubscriptionStatus(status) {
		return ServiceEventSubscriptionRecord{}, badRequest("invalid service event subscription status")
	}
	var subscription ServiceEventSubscriptionRecord
	if err := coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		loaded, err := loadServiceEventSubscriptionForUpdate(ctx, tx, trimmedWorkspaceID, trimmedSubscriptionID)
		if err != nil {
			return err
		}
		if !isPullRequestServiceEventSubscription(loaded) {
			return storeerr.NotFound("watch_not_found")
		}
		if loaded.Status == ServiceEventSubscriptionStatusBlocked {
			return storeerr.Conflict("service_event_subscription_blocked")
		}
		if err := requireServiceEventActorAuthorization(ctx, tx, trimmedWorkspaceID, actor, loaded.AppInstallationID, []string{ServiceEventSubscriptionScopeWrite}); err != nil {
			return err
		}
		if err := requireServiceEventActorOwnsSubscription(ctx, tx, trimmedWorkspaceID, actor, loaded); err != nil {
			return err
		}
		now := time.Now().UTC()
		if err := recordPullRequestSubscriptionRateLimitEvent(ctx, tx, pullRequestSubscriptionRateLimitEvent{
			WorkspaceID:       trimmedWorkspaceID,
			AppInstallationID: loaded.AppInstallationID,
			ActorType:         actor.ActorType,
			ActorID:           actor.ActorID,
			Action:            rateLimitAction,
			TargetKind:        loaded.TargetKind,
			TargetRef:         loaded.TargetRef,
			ResourceKey:       loaded.ResourceKey,
			CorrelationID:     loaded.CorrelationID,
			CreatedAt:         now,
		}); err != nil {
			return err
		}
		var pausedAt any
		if status == ServiceEventSubscriptionStatusPaused {
			pausedAt = now
		}
		if _, err := store.serviceActorStore().InTx(tx).SetEventSubscriptionStatus(ctx, serviceactor.EventSubscriptionStatusUpdate{
			WorkspaceID: trimmedWorkspaceID,
			ID:          trimmedSubscriptionID,
			Status:      status,
			Reason:      strings.TrimSpace(reason),
			Now:         now,
		}); err != nil {
			return err
		}
		loaded.Status = status
		loaded.PausedReason = strings.TrimSpace(reason)
		if t, ok := pausedAt.(time.Time); ok {
			loaded.PausedAt = &t
		} else {
			loaded.PausedAt = nil
		}
		loaded.UpdatedAt = now
		subscription = loaded
		return nil
	}); err != nil {
		return ServiceEventSubscriptionRecord{}, err
	}
	return subscription, nil
}

func validServiceEventSubscriptionStatus(status string) bool {
	switch strings.TrimSpace(status) {
	case ServiceEventSubscriptionStatusActive, ServiceEventSubscriptionStatusPaused, ServiceEventSubscriptionStatusCompleted, ServiceEventSubscriptionStatusBlocked:
		return true
	default:
		return false
	}
}
