package memory

import (
	"context"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/service/resource/activity"

	activitystore "github.com/tidbcloud/AgentTeam/internal/store/resource/activity"
	memorystore "github.com/tidbcloud/AgentTeam/internal/store/resource/memory"
	reposstore "github.com/tidbcloud/AgentTeam/internal/store/resource/repos"
)

const (
	memoryActivityCreated             = "created"
	memoryActivityForked              = "forked"
	memoryActivitySettingsChanged     = "settings_changed"
	memoryActivityArchived            = "archived"
	memoryActivityDeleted             = "deleted"
	memoryActivityBound               = "bound"
	memoryActivityUnbound             = "unbound"
	memoryActivityGrantAdded          = "grant_added"
	memoryActivityGrantRemoved        = "grant_removed"
	memoryActivityGrantRoleChanged    = "grant_role_changed"
	memoryActivityRepoLifecycleFailed = "repo_lifecycle_failed"
)

func (s *MemoryService) memoryActivityStore(tx *memorystore.Store) *activitystore.Store {
	if s == nil || s.activityStore == nil {
		return nil
	}
	if tx != nil && tx.DB() != nil {
		return s.activityStore.WithDB(tx.DB())
	}
	return s.activityStore
}

func writeMemoryActivity(ctx context.Context, store *activitystore.Store, workspaceID string, memory memorystore.Memory, repoFullName, eventType, actorID, targetKind, targetID string, details map[string]any, occurredAt time.Time) error {
	return activity.WriteResourceActivityEvent(ctx, store, activity.ResourceActivityEventInput{
		WorkspaceID:  strings.TrimSpace(workspaceID),
		AssetType:    activitystore.AssetTypeMemory,
		AssetID:      memory.ID,
		RepoFullName: repoFullName,
		EventType:    eventType,
		ActorID:      actorID,
		TargetKind:   targetKind,
		TargetID:     targetID,
		Details:      details,
		Display: activity.ResourceActivityDisplay{
			Resource: activity.ResourceActivityDisplayResource{Type: activitystore.AssetTypeMemory, ID: memory.ID, Name: memory.Name, RepoFullName: repoFullName},
		},
		OccurredAt: occurredAt,
	})
}

func (s *MemoryService) memoryPrimaryRepoFullName(ctx context.Context, workspaceID, memoryID string) string {
	if s == nil || s.repositoryStore == nil {
		return ""
	}
	repo, err := s.repositoryStore.Get(ctx, workspaceID, reposstore.ResourceTypeMemory, memoryID, reposstore.RepoRolePrimary)
	if err != nil {
		return ""
	}
	return strings.TrimSpace(repo.RepoFullName)
}

func memoryPrimaryRepoFullNameFromRepos(repos []reposstore.ResourceRepository) string {
	for _, repo := range repos {
		if strings.TrimSpace(repo.RepoRole) == reposstore.RepoRolePrimary {
			return strings.TrimSpace(repo.RepoFullName)
		}
	}
	return ""
}

func (s *MemoryService) recordMemoryRepoLifecycleFailedActivity(ctx context.Context, repos []reposstore.ResourceRepository, actorID string, occurredAt time.Time, cause error) {
	if s == nil || s.activityStore == nil {
		return
	}
	errorMessage := ""
	if cause != nil {
		errorMessage = cause.Error()
	}
	for _, repo := range repos {
		if strings.TrimSpace(repo.ResourceType) != reposstore.ResourceTypeMemory {
			continue
		}
		_ = activity.WriteResourceActivityEvent(ctx, s.activityStore, activity.ResourceActivityEventInput{
			WorkspaceID:  repo.WorkspaceID,
			AssetType:    activitystore.AssetTypeMemory,
			AssetID:      repo.ResourceID,
			RepoFullName: repo.RepoFullName,
			EventType:    memoryActivityRepoLifecycleFailed,
			ActorID:      actorID,
			TargetKind:   "repo",
			TargetID:     repo.RepoFullName,
			Details: map[string]any{
				"repoRole": repo.RepoRole,
				"error":    errorMessage,
			},
			OccurredAt: occurredAt,
		})
	}
}

func memorySettingsDetails(before memorystore.Memory, after memorystore.Memory) map[string]any {
	changed := make([]string, 0, 4)
	details := map[string]any{}
	addChange := func(field string, from any, to any) {
		changed = append(changed, field)
		details[field] = map[string]any{"from": from, "to": to}
	}
	if before.Name != after.Name {
		addChange("name", before.Name, after.Name)
	}
	if before.Slug != after.Slug {
		addChange("slug", before.Slug, after.Slug)
	}
	if before.Description != after.Description {
		addChange("description", before.Description, after.Description)
	}
	if before.IsActive != after.IsActive {
		addChange("isActive", before.IsActive, after.IsActive)
	}
	details["changedFields"] = changed
	return details
}
