/**
 * Registry Serialization Utilities
 *
 * Pre-serialization logic for ResourceRegistry.
 * Converts resource definitions to JSON-safe structures at startup.
 */

import type { DeploymentSpec } from './resource-registry'
import type { ResourceDefinition } from './types'
import type { ResourceEntry } from '../../organization-model/domains/resources'
import type { SystemEntry } from '../../organization-model/domains/systems'
import type {
  SerializedOrganizationData,
  SerializedAgentDefinition,
  SerializedWorkflowDefinition,
  CommandViewAgent,
  CommandViewWorkflow,
  CommandViewEdge
} from './serialized-types'
import { serializeDefinition } from '../../execution/engine/base/serialization'

function summarizeSystem(system: SystemEntry | undefined): ResourceDefinition['system'] {
  if (!system) return undefined

  return {
    id: system.id,
    title: system.label ?? system.title,
    description: system.description,
    kind: system.kind,
    lifecycle: system.lifecycle
  }
}

function createGovernanceMetadataResolver(resources: DeploymentSpec) {
  const resourcesById = new Map(Object.values(resources.organizationModel?.resources ?? {}).map((r) => [r.id, r]))
  const systemsById = new Map(Object.values(resources.organizationModel?.systems ?? {}).map((s) => [s.id, s]))

  return (
    resourceId: string,
    descriptor: ResourceEntry | undefined
  ): Pick<ResourceDefinition, 'systemPath' | 'system' | 'governanceStatus'> => {
    const resource = descriptor ?? resourcesById.get(resourceId)
    if (!resource) return {}

    return {
      systemPath: resource.systemPath,
      system: summarizeSystem(systemsById.get(resource.systemPath)),
      governanceStatus: resource.status
    }
  }
}

/**
 * Serialize all organization resources
 * Called once during ResourceRegistry construction
 */
export function serializeAllOrganizations(
  registry: Record<string, DeploymentSpec>
): Map<string, SerializedOrganizationData> {
  const cache = new Map<string, SerializedOrganizationData>()
  for (const [orgName, resources] of Object.entries(registry)) {
    cache.set(orgName, serializeOrganization(resources))
  }
  return cache
}

/**
 * Serialize single organization's resources
 */
export function serializeOrganization(resources: DeploymentSpec): SerializedOrganizationData {
  const getGovernanceMetadata = createGovernanceMetadataResolver(resources)

  // Serialize workflows
  const workflowDefinitions = new Map<string, SerializedWorkflowDefinition>()
  const workflowResources: ResourceDefinition[] = []
  const commandViewWorkflows: CommandViewWorkflow[] = []

  for (const workflow of resources.workflows ?? []) {
    const resourceId = workflow.config.resourceId
    const serialized = serializeDefinition(workflow) as SerializedWorkflowDefinition
    workflowDefinitions.set(resourceId, serialized)

    workflowResources.push({
      resourceId,
      name: workflow.config.name,
      description: workflow.config.description,
      version: workflow.config.version,
      type: 'workflow',
      status: workflow.config.status,
      links: workflow.config.links,
      category: workflow.config.category,
      ...getGovernanceMetadata(resourceId, workflow.config.resource)
    })

    commandViewWorkflows.push({
      resourceId,
      name: workflow.config.name,
      description: workflow.config.description,
      version: workflow.config.version,
      type: 'workflow',
      status: workflow.config.status,
      links: workflow.config.links,
      category: workflow.config.category,
      ...getGovernanceMetadata(resourceId, workflow.config.resource),
      stepCount: Object.keys(workflow.steps).length,
      entryPoint: workflow.entryPoint
    })
  }

  // Serialize agents
  const agentDefinitions = new Map<string, SerializedAgentDefinition>()
  const agentResources: ResourceDefinition[] = []
  const commandViewAgents: CommandViewAgent[] = []

  for (const agent of resources.agents ?? []) {
    const resourceId = agent.config.resourceId
    const serialized = serializeDefinition(agent) as SerializedAgentDefinition
    agentDefinitions.set(resourceId, serialized)

    agentResources.push({
      resourceId,
      name: agent.config.name,
      description: agent.config.description,
      version: agent.config.version,
      type: 'agent',
      status: agent.config.status,
      links: agent.config.links,
      category: agent.config.category,
      ...getGovernanceMetadata(resourceId, agent.config.resource)
    })

    commandViewAgents.push({
      resourceId,
      name: agent.config.name,
      description: agent.config.description,
      version: agent.config.version,
      type: 'agent',
      status: agent.config.status,
      links: agent.config.links,
      category: agent.config.category,
      ...getGovernanceMetadata(resourceId, agent.config.resource),
      modelProvider: agent.modelConfig.provider,
      modelId: agent.modelConfig.model,
      toolCount: agent.tools.length,
      hasKnowledgeMap: agent.knowledgeMap !== undefined,
      hasMemory: agent.config.memoryPreferences !== undefined,
      sessionCapable: agent.config.sessionCapable ?? false
    })
  }

  // Build edges from triggers and relationships
  const edges = buildEdges(resources)

  // Pass-through manifest data (already JSON-safe)
  const triggers = resources.triggers ?? []
  const integrations = resources.integrations ?? []
  const externalResources = resources.externalResources ?? []
  const humanCheckpoints = resources.humanCheckpoints ?? []

  return {
    version: resources.version,
    resources: {
      workflows: workflowResources,
      agents: agentResources,
      total: workflowResources.length + agentResources.length
    },
    definitions: {
      workflows: workflowDefinitions,
      agents: agentDefinitions
    },
    commandView: {
      workflows: commandViewWorkflows,
      agents: commandViewAgents,
      triggers,
      integrations,
      externalResources,
      humanCheckpoints,
      edges
    },
    triggers,
    integrations,
    externalResources,
    humanCheckpoints,
    relationships: resources.relationships
  }
}

/**
 * Build Command View edges from relationships
 * NOTE: Trigger relationships are now declared in ResourceRelationships, not on TriggerDefinition
 */
export function buildEdges(resources: DeploymentSpec): CommandViewEdge[] {
  const edges: CommandViewEdge[] = []
  let edgeId = 0

  // All relationship edges (triggers, agents, workflows can all declare relationships)
  for (const [resourceId, declaration] of Object.entries(resources.relationships ?? {})) {
    for (const agentId of declaration.triggers?.agents ?? []) {
      edges.push({
        id: `edge-${edgeId++}`,
        source: resourceId,
        target: agentId,
        relationship: 'triggers'
      })
    }
    for (const workflowId of declaration.triggers?.workflows ?? []) {
      edges.push({
        id: `edge-${edgeId++}`,
        source: resourceId,
        target: workflowId,
        relationship: 'triggers'
      })
    }
    for (const integrationId of declaration.uses?.integrations ?? []) {
      edges.push({
        id: `edge-${edgeId++}`,
        source: resourceId,
        target: integrationId,
        relationship: 'uses'
      })
    }
  }

  // External Resource edges (forward-only)
  for (const external of resources.externalResources ?? []) {
    // External -> Internal
    for (const workflowId of external.triggers?.workflows ?? []) {
      edges.push({
        id: `edge-${edgeId++}`,
        source: external.resourceId,
        target: workflowId,
        relationship: 'triggers'
      })
    }
    for (const agentId of external.triggers?.agents ?? []) {
      edges.push({
        id: `edge-${edgeId++}`,
        source: external.resourceId,
        target: agentId,
        relationship: 'triggers'
      })
    }

    // External -> Integration
    for (const integrationId of external.uses?.integrations ?? []) {
      edges.push({
        id: `edge-${edgeId++}`,
        source: external.resourceId,
        target: integrationId,
        relationship: 'uses'
      })
    }
  }

  // Human Checkpoint edges
  for (const humanCheckpoint of resources.humanCheckpoints ?? []) {
    // Agent/Workflow -> ApprovalPoint
    for (const agentId of humanCheckpoint.requestedBy?.agents ?? []) {
      edges.push({
        id: `edge-${edgeId++}`,
        source: agentId,
        target: humanCheckpoint.resourceId,
        relationship: 'approval'
      })
    }
    for (const workflowId of humanCheckpoint.requestedBy?.workflows ?? []) {
      edges.push({
        id: `edge-${edgeId++}`,
        source: workflowId,
        target: humanCheckpoint.resourceId,
        relationship: 'approval'
      })
    }

    // ApprovalPoint -> Agent/Workflow
    for (const agentId of humanCheckpoint.routesTo?.agents ?? []) {
      edges.push({
        id: `edge-${edgeId++}`,
        source: humanCheckpoint.resourceId,
        target: agentId,
        relationship: 'triggers'
      })
    }
    for (const workflowId of humanCheckpoint.routesTo?.workflows ?? []) {
      edges.push({
        id: `edge-${edgeId++}`,
        source: humanCheckpoint.resourceId,
        target: workflowId,
        relationship: 'triggers'
      })
    }
  }

  return edges
}
