package skills

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

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

	"github.com/tidbcloud/AgentTeam/internal/service/resource/repos"
	activitystore "github.com/tidbcloud/AgentTeam/internal/store/resource/activity"
	reposstore "github.com/tidbcloud/AgentTeam/internal/store/resource/repos"
	store "github.com/tidbcloud/AgentTeam/internal/store/resource/skills"
)

const (
	skillActivityCreated             = "created"
	skillActivityImported            = "imported"
	skillActivitySettingsChanged     = "settings_changed"
	skillActivityArchived            = "archived"
	skillActivityDeleted             = "deleted"
	skillActivityBound               = "bound"
	skillActivityUnbound             = "unbound"
	skillActivityGrantAdded          = "grant_added"
	skillActivityGrantRemoved        = "grant_removed"
	skillActivityGrantRoleChanged    = "grant_role_changed"
	skillActivityBranchPinned        = "branch_pinned"
	skillActivityBackupRestored      = "backup_restored"
	skillActivityRepoLifecycleFailed = "repo_lifecycle_failed"
)

func (s *Service) skillActivityStore(tx *store.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 writeSkillActivity(ctx context.Context, activityStore *activitystore.Store, skill store.Skill, repoFullName, eventType, actorID, targetKind, targetID string, details map[string]any, occurredAt time.Time) error {
	return activity.WriteResourceActivityEvent(ctx, activityStore, activity.ResourceActivityEventInput{
		WorkspaceID:  skill.WorkspaceID,
		AssetType:    activitystore.AssetTypeSkill,
		AssetID:      skill.ID,
		RepoFullName: repoFullName,
		EventType:    eventType,
		ActorID:      actorID,
		TargetKind:   targetKind,
		TargetID:     targetID,
		Details:      details,
		Display: activity.ResourceActivityDisplay{
			Resource: activity.ResourceActivityDisplayResource{Type: activitystore.AssetTypeSkill, ID: skill.ID, Name: skill.Name, RepoFullName: repoFullName},
		},
		OccurredAt: occurredAt,
	})
}

func (s *Service) skillPrimaryRepoFullName(ctx context.Context, workspaceID, skillID string) string {
	if s == nil || s.repositoryStore == nil {
		return ""
	}
	repo, err := s.repositoryStore.Get(ctx, strings.TrimSpace(workspaceID), reposstore.ResourceTypeSkillRepository, strings.TrimSpace(skillID), reposstore.RepoRolePrimary)
	if err != nil {
		return ""
	}
	return strings.TrimSpace(repo.RepoFullName)
}

func (s *Service) skillActivityRef(ctx context.Context, skill store.Skill, native bool) (string, error) {
	if !native {
		return s.skillPrimaryRepoFullName(ctx, skill.WorkspaceID, skill.ID), nil
	}
	if strings.TrimSpace(skill.ContentID) == "" || strings.TrimSpace(skill.ActiveContentVersionID) == "" {
		return "", fmt.Errorf("native skill content reference is incomplete")
	}
	return "contentstore://" + skill.ContentID + "/" + skill.ActiveContentVersionID, nil
}

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

func (s *Service) recordSkillRepoLifecycleFailedActivity(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.ResourceTypeSkillRepository {
			continue
		}
		_ = activity.WriteResourceActivityEvent(ctx, s.activityStore, activity.ResourceActivityEventInput{
			WorkspaceID:  repo.WorkspaceID,
			AssetType:    activitystore.AssetTypeSkill,
			AssetID:      repo.ResourceID,
			RepoFullName: repo.RepoFullName,
			EventType:    skillActivityRepoLifecycleFailed,
			ActorID:      actorID,
			TargetKind:   "repo",
			TargetID:     repo.RepoFullName,
			Details: map[string]any{
				"repoRole": repo.RepoRole,
				"error":    errorMessage,
			},
			OccurredAt: occurredAt,
		})
	}
}

func skillSettingsDetails(before store.Skill, after store.Skill) map[string]any {
	changed := make([]string, 0, 5)
	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.VisibilityScope != after.VisibilityScope {
		addChange("visibilityScope", before.VisibilityScope, after.VisibilityScope)
	}
	if before.VisibilityOwnerID != after.VisibilityOwnerID {
		addChange("visibilityOwnerId", before.VisibilityOwnerID, after.VisibilityOwnerID)
	}
	if before.SkillCount != after.SkillCount {
		addChange("skillCount", before.SkillCount, after.SkillCount)
	}
	if before.IsActive != after.IsActive {
		addChange("isActive", before.IsActive, after.IsActive)
	}
	details["changedFields"] = changed
	return details
}

func skillRepoFailureRecords(skill store.Skill, storage repos.RepositoryStorage, repo repos.RepositoryIdentity, actor string, now time.Time) []reposstore.ResourceRepository {
	return []reposstore.ResourceRepository{repos.ResourceRepositoryFailureRecord(skill.WorkspaceID, reposstore.ResourceTypeSkillRepository, skill.ID, reposstore.RepoRolePrimary, storage, repo, actor, now)}
}
