package testkit

import (
	"context"
	"errors"
	"fmt"
	"log/slog"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/store/serviceaccount"
	"github.com/tidbcloud/AgentTeam/internal/store/taskboard"
	"github.com/tidbcloud/AgentTeam/internal/store/unread"
	unreadservice "github.com/tidbcloud/AgentTeam/internal/unread"
)

type apiTestUnreadRuntimeStore struct {
	store *unread.Store
}

func newAPITestUnreadRuntime(store *Store, logger *slog.Logger) (*unreadservice.Service, error) {
	if store == nil || store.Graph == nil || store.Graph.UnreadRuntime == nil {
		return nil, fmt.Errorf("unread runtime store is not configured")
	}
	runtime, err := unreadservice.New(unreadservice.Config{})
	if err != nil {
		return nil, err
	}
	return unreadservice.NewService(runtime, apiTestUnreadRuntimeStore{store: store.Graph.UnreadRuntime}, logger)
}

func (adapter apiTestUnreadRuntimeStore) UpsertSnapshots(ctx context.Context, snapshots []unreadservice.Snapshot) error {
	return adapter.store.UpsertSnapshots(ctx, snapshots)
}

func (adapter apiTestUnreadRuntimeStore) UpsertTaskBarrierSnapshots(ctx context.Context, snapshots []unreadservice.Snapshot) error {
	return adapter.store.UpsertTaskBarrierSnapshots(ctx, snapshots)
}

func (adapter apiTestUnreadRuntimeStore) LoadSnapshotPage(ctx context.Context, workspaceID string, memberID string, scopeIDs []string, afterScopeID string, limit int) ([]unreadservice.Snapshot, error) {
	return adapter.store.LoadSnapshotPage(ctx, unread.SnapshotPageRequest{
		WorkspaceID:  workspaceID,
		MemberID:     memberID,
		ScopeIDs:     scopeIDs,
		AfterScopeID: afterScopeID,
		Limit:        limit,
	})
}

func (adapter apiTestUnreadRuntimeStore) LoadScopeRosterPage(ctx context.Context, workspaceID string, scopeID string, afterMemberID string, limit int) ([]unreadservice.RosterMember, error) {
	return adapter.store.LoadScopeRosterPage(ctx, unread.RosterPageRequest{
		WorkspaceID:   workspaceID,
		ScopeID:       scopeID,
		AfterMemberID: afterMemberID,
		Limit:         limit,
	})
}

type apiTestTaskUnreadBridge struct {
	runtime *unreadservice.Service
}

type apiTestServiceAccountDeletionObserver struct {
	runtime *unreadservice.Service
}

func newAPITestServiceAccountDeletionObserver(runtime *unreadservice.Service) *apiTestServiceAccountDeletionObserver {
	if runtime == nil {
		return nil
	}
	return &apiTestServiceAccountDeletionObserver{runtime: runtime}
}

func (observer *apiTestServiceAccountDeletionObserver) LocalAccountsDeleted(_ context.Context, accounts []serviceaccount.Account) error {
	if observer == nil || observer.runtime == nil {
		return nil
	}
	var result error
	for _, account := range accounts {
		workspaceID := strings.TrimSpace(account.WorkspaceID)
		scopeID := strings.TrimSpace(account.MessageScopeID)
		if workspaceID == "" || scopeID == "" {
			continue
		}
		_, err := observer.runtime.ApplyScopeInvalidatedWithDeltas(unreadservice.ScopeInvalidated{
			WorkspaceID: workspaceID,
			ScopeID:     scopeID,
			Reason:      "service_account_delete",
		})
		result = errors.Join(result, err)
	}
	return result
}

func newAPITestTaskUnreadBridge(runtime *unreadservice.Service) *apiTestTaskUnreadBridge {
	if runtime == nil {
		return nil
	}
	return &apiTestTaskUnreadBridge{runtime: runtime}
}

func (bridge *apiTestTaskUnreadBridge) HandleTaskChange(_ context.Context, event taskboard.TaskChangeEvent) {
	if bridge == nil || bridge.runtime == nil {
		return
	}
	workspaceID := strings.TrimSpace(event.Context.WorkspaceID)
	if workspaceID == "" {
		return
	}
	switch event.EventType {
	case taskboard.TaskChangeCreated:
		for _, task := range event.Tasks {
			bridge.suppressDoneTask(workspaceID, task)
		}
	case taskboard.TaskChangeUpdated:
		bridge.suppressDoneTask(workspaceID, event.Task)
	case taskboard.TaskChangeDeleted:
		scopeID := strings.TrimSpace(event.TaskID)
		if scopeID == "" {
			return
		}
		_, _ = bridge.runtime.ApplyScopeInvalidatedWithDeltas(unreadservice.ScopeInvalidated{
			WorkspaceID: workspaceID,
			ScopeID:     scopeID,
			Reason:      "task_deleted",
			Permanent:   true,
		})
	}
}

func (bridge *apiTestTaskUnreadBridge) suppressDoneTask(workspaceID string, task taskboard.Task) {
	if strings.TrimSpace(task.Status) != string(taskboard.StatusDone) {
		return
	}
	scopeID := strings.TrimSpace(task.TaskScopeID)
	if scopeID == "" {
		return
	}
	_, _ = bridge.runtime.ApplyScopeInvalidatedWithDeltas(unreadservice.ScopeInvalidated{
		WorkspaceID: strings.TrimSpace(workspaceID),
		ScopeID:     scopeID,
		Reason:      "task_done",
	})
}
