package converter

import (
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/service/memory"
	memorystore "github.com/tidbcloud/AgentTeam/internal/store/resource/memory"
)

func ToMemory(m memorystore.Memory) dto.Memory {
	return ToMemoryWithRepositories(m, memory.MemoryRepositories{})
}

func ToMemoryWithRepositories(m memorystore.Memory, repositories memory.MemoryRepositories) dto.Memory {
	return dto.Memory{
		ID:                m.ID,
		Name:              m.Name,
		Description:       m.Description,
		Slug:              m.Slug,
		StorageScope:      m.StorageScope,
		StorageOwnerLogin: m.StorageOwnerLogin,
		Repositories:      toMemoryRepositories(repositories),
		CreatedAt:         m.CreatedAt.UTC().Format(timeLayout),
		UpdatedAt:         m.UpdatedAt.UTC().Format(timeLayout),
		IsActive:          m.IsActive,
		Pinned:            m.Pinned,
		PinnedAt:          optionalTime(m.PinnedAt),
		Role:              m.Role,
		CanManage:         m.CanManage,
		CreatedBy:         m.CreatedBy,
		UpdatedBy:         m.UpdatedBy,
	}
}

func toMemoryRepositories(repositories memory.MemoryRepositories) dto.MemoryRepositories {
	primary := ToResourceRepositoryRef(repositories.Primary)
	return dto.MemoryRepositories{Primary: primary}
}

func ToMemories(items []memorystore.Memory) []dto.Memory {
	if items == nil {
		return nil
	}
	out := make([]dto.Memory, 0, len(items))
	for _, item := range items {
		out = append(out, ToMemory(item))
	}
	return out
}

func ToMemoryListResponseWithRepositories(items []memorystore.Memory, repositories map[string]memory.MemoryRepositories, page, perPage int, total int64, hasMore bool) dto.MemoryListResponse {
	if items == nil {
		return dto.MemoryListResponse{Page: page, PerPage: perPage, Total: total, HasMore: hasMore}
	}
	out := make([]dto.Memory, 0, len(items))
	for _, item := range items {
		out = append(out, ToMemoryWithRepositories(item, repositories[item.ID]))
	}
	return dto.MemoryListResponse{Items: out, Page: page, PerPage: perPage, Total: total, HasMore: hasMore}
}

func ToAgentAccessibleMemory(item memory.AgentAccessibleMemory) dto.AgentAccessibleMemory {
	return dto.AgentAccessibleMemory{
		ID:                          item.ID,
		Name:                        item.Name,
		Description:                 item.Description,
		AccessLevel:                 item.AccessLevel,
		PermissionSetAt:             item.PermissionSetAt.UTC().Format(timeLayout),
		UpdatedAt:                   item.UpdatedAt.UTC().Format(timeLayout),
		Repository:                  ToResourceRepositoryRef(item.Repository),
		Branch:                      item.Branch,
		ImportExistingLocalContent:  item.ImportExistingLocalContent,
		ReplaceLocalContentOnSwitch: item.ReplaceLocalContentOnSwitch,
		SyncProtocol:                item.SyncProtocol,
		ContentID:                   item.ContentID,
		ContentVersionID:            item.ContentVersionID,
		ContentGeneration:           item.ContentGeneration,
		AcknowledgedSequence:        item.AcknowledgedSequence,
		ActiveReplicaID:             item.ActiveReplicaID,
	}
}

func ToAgentCloudMemory(item memory.AgentCloudMemory) dto.AgentCloudMemory {
	return dto.AgentCloudMemory{
		AgentID:                    item.AgentID,
		Enabled:                    item.Enabled,
		MemoryID:                   item.MemoryID,
		Name:                       item.Name,
		Branch:                     item.Branch,
		Repository:                 ToResourceRepositoryRef(item.Repository),
		Status:                     item.Status,
		SyncStatus:                 item.SyncStatus,
		LastSyncAt:                 optionalTime(&item.LastSyncAt),
		LastBackupAt:               optionalTime(&item.LastBackupAt),
		ImportExistingLocalContent: item.ImportExistingLocalContent,
	}
}

func ToAgentCloudMemoryBackup(item memory.AgentCloudMemoryBackup) dto.AgentCloudMemoryBackup {
	return dto.AgentCloudMemoryBackup{
		TagName:   item.TagName,
		Name:      item.Name,
		CommitSHA: item.CommitSHA,
		CreatedAt: optionalTime(&item.CreatedAt),
	}
}

func ToAgentCloudMemoryBackupListResponse(page memory.AgentCloudMemoryBackupPage) dto.AgentCloudMemoryBackupListResponse {
	items := make([]dto.AgentCloudMemoryBackup, 0, len(page.Items))
	for _, item := range page.Items {
		items = append(items, ToAgentCloudMemoryBackup(item))
	}
	return dto.AgentCloudMemoryBackupListResponse{
		Items:   items,
		Page:    page.Page,
		PerPage: page.PerPage,
		HasMore: page.HasMore,
	}
}

// ToAgentAccessBatchResponse converts the service-layer batch into the wire
// DTO. Agents with no bound memory map to nil so Desktop can distinguish
// "no access" from "agent dropped from response".
func ToAgentAccessBatchResponse(batch memory.AgentMemoryAccessBatch) dto.AgentAccessBatchResponse {
	out := dto.AgentAccessBatchResponse{
		Agents:           make(map[string]*dto.AgentAccessibleMemory, len(batch.Agents)),
		SyncToken:        batch.SyncToken,
		SyncTokenExpires: optionalTime(&batch.SyncTokenExpires),
	}
	if batch.SyncTokenExpires.IsZero() {
		out.SyncTokenExpires = ""
	}
	for agentID, memory := range batch.Agents {
		if memory == nil {
			out.Agents[agentID] = nil
			continue
		}
		converted := ToAgentAccessibleMemory(*memory)
		out.Agents[agentID] = &converted
	}
	return out
}

func ToAgentAssetAccessEvent(item memory.AgentAssetAccessEvent) dto.AgentAssetAccessEvent {
	return dto.AgentAssetAccessEvent{
		EventID:     item.EventID,
		WorkspaceID: item.WorkspaceID,
		AgentIDs:    item.AgentIDs,
		AssetTypes:  item.AssetTypes,
		Version:     item.Version,
		OccurredAt:  item.OccurredAt.UTC().Format(timeLayout),
		Source:      item.Source,
		Attempts:    item.Attempts,
		MaxAttempts: item.MaxAttempts,
	}
}

func ToAgentAssetAccessEventLeaseResponse(items []memory.AgentAssetAccessEvent) dto.AgentAssetAccessEventLeaseResponse {
	out := dto.AgentAssetAccessEventLeaseResponse{Events: make([]dto.AgentAssetAccessEvent, 0, len(items))}
	for _, item := range items {
		out.Events = append(out.Events, ToAgentAssetAccessEvent(item))
	}
	return out
}

func ToAgentMemoryBindingsResponse(summary memory.AgentMemoryBindingsSummary) dto.AgentMemoryBindingsResponse {
	ownedAgents := make([]dto.AgentOwnership, 0, len(summary.OwnedAgents))
	for _, agent := range summary.OwnedAgents {
		ownedAgents = append(ownedAgents, dto.AgentOwnership{
			ID:          agent.ID,
			Name:        agent.Name,
			DisplayName: agent.DisplayName,
		})
	}
	bindings := make([]dto.AgentMemoryBindingSummary, 0, len(summary.Bindings))
	for _, binding := range summary.Bindings {
		bindings = append(bindings, dto.AgentMemoryBindingSummary{
			ID:                          binding.ID,
			AgentID:                     binding.AgentID,
			MemoryID:                    binding.MemoryID,
			Branch:                      binding.Branch,
			ImportExistingLocalContent:  binding.ImportExistingLocalContent,
			ReplaceLocalContentOnSwitch: binding.ReplaceLocalContentOnSwitch,
			CreatedBy:                   binding.CreatedBy,
			UpdatedBy:                   binding.UpdatedBy,
			CreatedAt:                   binding.CreatedAt.UTC().Format(timeLayout),
			UpdatedAt:                   binding.UpdatedAt.UTC().Format(timeLayout),
		})
	}
	memories := make([]dto.AgentMemoryBindingMemory, 0, len(summary.Memories))
	for _, memory := range summary.Memories {
		memories = append(memories, dto.AgentMemoryBindingMemory{
			ID:                          memory.ID,
			Slug:                        memory.Slug,
			Name:                        memory.Name,
			Description:                 memory.Description,
			IsActive:                    memory.IsActive,
			Role:                        memory.Role,
			CanManage:                   memory.CanManage,
			CanBind:                     memory.CanBind,
			Repository:                  ToResourceRepositoryRef(memory.Repository),
			BoundAgentID:                memory.BoundAgentID,
			BoundAgentName:              memory.BoundAgentName,
			BoundBranch:                 memory.BoundBranch,
			ImportExistingLocalContent:  memory.ImportExistingLocalContent,
			ReplaceLocalContentOnSwitch: memory.ReplaceLocalContentOnSwitch,
			UpdatedAt:                   memory.UpdatedAt.UTC().Format(timeLayout),
		})
	}
	return dto.AgentMemoryBindingsResponse{OwnedAgents: ownedAgents, Bindings: bindings, Memories: memories}
}

func ToMemoryPermissionGrant(grant memory.MemoryPermissionGrant) dto.MemoryPermissionGrant {
	return dto.MemoryPermissionGrant{ID: grant.ID, MemoryID: grant.MemoryID, SubjectType: grant.SubjectType, SubjectID: grant.SubjectID, Role: grant.Role, CreatedBy: grant.CreatedBy, UpdatedBy: grant.UpdatedBy, CreatedAt: grant.CreatedAt.UTC().Format(timeLayout), UpdatedAt: grant.UpdatedAt.UTC().Format(timeLayout)}
}

func ToMemoryPermissionsResponse(summary memory.MemoryPermissionsSummary) dto.MemoryPermissionsResponse {
	members := make([]dto.MemoryPermissionGrant, 0, len(summary.Members))
	for _, grant := range summary.Members {
		members = append(members, ToMemoryPermissionGrant(grant))
	}
	var owner *dto.MemoryPermissionGrant
	if summary.Owner != nil {
		converted := ToMemoryPermissionGrant(*summary.Owner)
		owner = &converted
	}
	return dto.MemoryPermissionsResponse{MyRole: summary.MyRole, CanManage: summary.CanManage, Owner: owner, Members: members}
}

func ToAgentMemoryBindingResponse(result memory.AgentMemoryBindingResult) dto.AgentMemoryBindingResponse {
	return dto.AgentMemoryBindingResponse{AgentID: result.AgentID, MemoryID: result.MemoryID, PreviousMemoryID: result.PreviousMemoryID, Branch: result.Branch, ImportExistingLocalContent: result.ImportExistingLocalContent, ReplaceLocalContentOnSwitch: result.ReplaceLocalContentOnSwitch, Unbound: result.Unbound}
}

func ToMemoryContentVersion(item memory.MemoryContentVersion) dto.MemoryContentVersion {
	return dto.MemoryContentVersion{
		VersionID: item.VersionID, ParentVersionID: item.ParentVersionID, TargetVersionID: item.TargetVersionID,
		OperationID: item.OperationID, OperationKind: item.OperationKind, ReplicaID: item.ReplicaID,
		CoveredFromSequence: item.CoveredFromSequence, CoveredThroughSequence: item.CoveredThroughSequence,
		Generation: item.Generation, RootDigest: item.RootDigest, FileCount: item.FileCount, SizeBytes: item.SizeBytes,
		SourceAuthorName: item.SourceAuthorName, SourceAuthorEmail: item.SourceAuthorEmail, SourceMessage: item.SourceMessage,
		CreatedBy: item.CreatedBy, CreatedAt: item.CreatedAt.UTC().Format(timeLayout),
	}
}

func ToMemoryContentVersionListResponse(page memory.MemoryContentVersionPage) dto.MemoryContentVersionListResponse {
	items := make([]dto.MemoryContentVersion, 0, len(page.Items))
	for _, item := range page.Items {
		items = append(items, ToMemoryContentVersion(item))
	}
	return dto.MemoryContentVersionListResponse{Items: items, Page: page.Page, PerPage: page.PerPage, HasMore: page.HasMore}
}

func ToMemoryContentCompareResponse(entries []memory.MemoryContentCompareEntry) dto.MemoryContentCompareResponse {
	items := make([]dto.MemoryContentCompareEntry, 0, len(entries))
	for _, entry := range entries {
		items = append(items, dto.MemoryContentCompareEntry{Path: entry.Path, Change: entry.Change, BeforeDigest: entry.BeforeDigest, AfterDigest: entry.AfterDigest})
	}
	return dto.MemoryContentCompareResponse{Items: items}
}

func ToMemoryContentBackup(item memory.MemoryContentBackup) dto.MemoryContentBackup {
	return dto.MemoryContentBackup{ID: item.ID, VersionID: item.VersionID, Label: item.Label, CreatedBy: item.CreatedBy, CreatedAt: item.CreatedAt.UTC().Format(timeLayout)}
}

func ToMemoryContentBackupListResponse(items []memory.MemoryContentBackup) dto.MemoryContentBackupListResponse {
	result := make([]dto.MemoryContentBackup, 0, len(items))
	for _, item := range items {
		result = append(result, ToMemoryContentBackup(item))
	}
	return dto.MemoryContentBackupListResponse{Items: result}
}
