using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

using AIRuntime.Runtime.Agents.Permissions;
using AIRuntime.Runtime.Capabilities;
using AIRuntime.Runtime.Services.Agent;

using RuntimeCore = AIRuntime.Runtime;


namespace AIRuntime.Runtime.Agents.Runtime
{
    /// <summary>
    /// Executes registered agents through AIRuntime.
    /// </summary>
    public sealed class AgentExecutor
    {
        private readonly AgentRegistry _registry;

        private readonly AgentSessionManager _sessionManager;

        private readonly RuntimeCore.AIRuntime _runtime;

        private readonly AgentCapabilityResolver _capabilityResolver;


        public AgentExecutor(
            AgentRegistry registry,
            AgentSessionManager sessionManager,
            RuntimeCore.AIRuntime runtime)
        {
            _registry =
                registry ??
                throw new ArgumentNullException(
                    nameof(registry));


            _sessionManager =
                sessionManager ??
                throw new ArgumentNullException(
                    nameof(sessionManager));


            _runtime =
                runtime ??
                throw new ArgumentNullException(
                    nameof(runtime));


            _capabilityResolver =
                new AgentCapabilityResolver();
        }


        public async Task<AgentRunResult> RunAsync(
            string agentId,
            string prompt,
            CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrWhiteSpace(agentId))
            {
                throw new ArgumentException(
                    "Agent id cannot be empty.",
                    nameof(agentId));
            }


            if (string.IsNullOrWhiteSpace(prompt))
            {
                throw new ArgumentException(
                    "Prompt cannot be empty.",
                    nameof(prompt));
            }


            AgentSession session =
                _sessionManager.CreateSession(
                    agentId);


            AgentExecutionContext executionContext =
                new AgentExecutionContext(
                    session);


            string previousPrompt =
                _runtime.SystemPrompt;


            try
            {
                _runtime.SystemPrompt =
                    executionContext
                        .Definition
                        .SystemPrompt;


                //--------------------------------------------------
                // Build an initial context from all runtime
                // capabilities so the resolver can apply the
                // agent's capability policy.
                //--------------------------------------------------

                AgentRuntimeContext availableContext =
                    new AgentRuntimeContext(
                        executionContext,
                        _runtime.GetCapabilities());


                IReadOnlyList<CapabilityDescriptor>
                    allowedCapabilities =
                        _capabilityResolver.Resolve(
                            availableContext,
                            availableContext.Capabilities);


                //--------------------------------------------------
                // Final execution context contains only the tools
                // this agent is permitted to see and execute.
                //--------------------------------------------------

                AgentRuntimeContext runtimeContext =
                    new AgentRuntimeContext(
                        executionContext,
                        allowedCapabilities);


                return await _runtime.RunAsync(
                    prompt,
                    runtimeContext,
                    cancellationToken)
                    .ConfigureAwait(false);
            }
            finally
            {
                _runtime.SystemPrompt =
                    previousPrompt;
            }
        }
    }
}
