package agent

import (
	"context"
	"fmt"
	"sort"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

const (
	roleDefinitionAssetItemType = "role_definition_asset"
	portableKnowledgeItemType   = "portable_knowledge"
	portableSkillItemType       = "skill"
	CloudMemoryIdentityPath     = "memory/system/identity.md"
)

type roleBindingMaterialization struct {
	RoleID               string
	RoleVersionID        string
	BindingKind          string
	ActiveDefinitionPath string
}

type roleManifestItem struct {
	ItemType string
	Name     string
	Content  string
}

// LoadAgentWorkspaceMaterialization returns the current workspace materialization
// plan for an agent.
func (service *Service) LoadAgentWorkspaceMaterialization(ctx context.Context, agentID string) (*WorkspaceMaterialization, error) {
	binding, err := service.loadRoleBindingMaterialization(ctx, agentID)
	if err != nil {
		return nil, fmt.Errorf("load role binding for materialization: %w", err)
	}
	if binding == nil {
		return nil, nil
	}
	manifestItems, err := service.loadRoleManifestMaterializationItems(ctx, binding)
	if err != nil {
		return nil, fmt.Errorf("load role manifest items for materialization: %w", err)
	}
	return workspaceMaterializationFromRoleBinding(binding, manifestItems), nil
}

func (service *Service) loadRoleBindingMaterialization(ctx context.Context, agentID string) (*roleBindingMaterialization, error) {
	row := service.db.QueryRow(ctx, `
	SELECT
	    COALESCE(role_id, ''),
	    COALESCE(role_version_id, ''),
	    COALESCE(binding_kind, ''),
	    COALESCE(active_definition_path, '')
	FROM agent_role_bindings
	WHERE agent_id = $1
	`, agentID)

	var binding roleBindingMaterialization
	if err := row.Scan(&binding.RoleID, &binding.RoleVersionID, &binding.BindingKind, &binding.ActiveDefinitionPath); err != nil {
		if coredb.IsNotFound(err) {
			return nil, nil
		}
		return nil, err
	}
	return &binding, nil
}

func (service *Service) loadRoleManifestMaterializationItems(ctx context.Context, binding *roleBindingMaterialization) ([]roleManifestItem, error) {
	if binding == nil {
		return nil, nil
	}
	roleVersionID := strings.TrimSpace(binding.RoleVersionID)
	if roleVersionID == "" {
		return nil, nil
	}

	rows, err := service.db.Query(ctx, `
	SELECT item_type, name, content
	FROM role_package_manifest_items
	WHERE role_version_id = $1
	ORDER BY sort_order ASC, id ASC
	`, roleVersionID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	items := make([]roleManifestItem, 0)
	for rows.Next() {
		var item roleManifestItem
		if err := rows.Scan(&item.ItemType, &item.Name, &item.Content); err != nil {
			return nil, err
		}
		items = append(items, item)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

func workspaceMaterializationFromRoleBinding(binding *roleBindingMaterialization, items []roleManifestItem) *WorkspaceMaterialization {
	if binding == nil {
		return nil
	}
	seedFiles, activeDefinitionPath := materializationSeedFilesFromRoleManifest(items, binding.ActiveDefinitionPath)
	if len(seedFiles) == 0 {
		return nil
	}
	return &WorkspaceMaterialization{
		RoleID:               strings.TrimSpace(binding.RoleID),
		SourceKind:           strings.TrimSpace(binding.BindingKind),
		SourceRef:            strings.TrimSpace(binding.RoleVersionID),
		ActiveDefinitionPath: activeDefinitionPath,
		SeedFiles:            seedFiles,
	}
}

func materializationSeedFilesFromRoleManifest(items []roleManifestItem, activeDefinitionPath string) ([]WorkspaceSeedFile, string) {
	definitionPath := CloudMemoryIdentityPath

	seedFiles := make([]WorkspaceSeedFile, 0, len(items))
	knowledgeSeeds := make([]WorkspaceSeedFile, 0)
	definitionAdded := false
	for _, item := range items {
		switch strings.TrimSpace(item.ItemType) {
		case roleDefinitionAssetItemType:
			if definitionAdded {
				continue
			}
			seedFiles = append(seedFiles, WorkspaceSeedFile{Path: definitionPath, Content: item.Content})
			definitionAdded = true
		case portableKnowledgeItemType:
			path, ok := portableKnowledgePath(item.Name)
			if !ok {
				continue
			}
			knowledgeSeeds = append(knowledgeSeeds, WorkspaceSeedFile{Path: path, Content: item.Content})
		case portableSkillItemType:
			path, ok := portableSkillPath(item.Name)
			if !ok {
				continue
			}
			seedFiles = append(seedFiles, WorkspaceSeedFile{Path: path, Content: item.Content})
		}
	}
	sort.Slice(knowledgeSeeds, func(i, j int) bool { return knowledgeSeeds[i].Path < knowledgeSeeds[j].Path })
	seedFiles = append(seedFiles, knowledgeSeeds...)
	return seedFiles, definitionPath
}

func portableKnowledgePath(name string) (string, bool) {
	trimmed := strings.TrimSpace(name)
	if trimmed == "" {
		return "", false
	}
	lower := strings.ToLower(trimmed)
	if strings.Contains(lower, "/") || strings.Contains(lower, "\\") {
		return "", false
	}
	switch lower {
	case ".", "..", "memory.md", "state", "kb", "archive":
		return "", false
	}
	if strings.HasSuffix(lower, ".md") {
		trimmed = strings.TrimSuffix(trimmed, ".md")
	}
	trimmed = strings.TrimSpace(trimmed)
	if trimmed == "" {
		return "", false
	}
	return ".agents/knowledge/" + trimmed + ".md", true
}

func portableSkillPath(name string) (string, bool) {
	trimmed := strings.TrimSpace(name)
	if trimmed == "" {
		return "", false
	}
	lower := strings.ToLower(trimmed)
	if strings.Contains(lower, "/") || strings.Contains(lower, "\\") {
		return "", false
	}
	if lower == "." || lower == ".." {
		return "", false
	}
	return ".agents/skills/" + trimmed + "/SKILL.md", true
}
