using System.Text.Json;
using System.Text.Json.Serialization;
using Nexus.Api.Data;
using Nexus.Api.Integrations;

namespace Nexus.Api.Services;

public sealed record AgentConfig
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = string.Empty;

    [JsonPropertyName("name")]
    public string Name { get; init; } = string.Empty;

    [JsonPropertyName("workspace")]
    public string? Workspace { get; init; }

    [JsonPropertyName("agentDir")]
    public string? AgentDir { get; init; }

    [JsonPropertyName("model")]
    public string? Model { get; init; }

    [JsonPropertyName("identity")]
    public AgentIdentityConfig? Identity { get; init; }

    [JsonPropertyName("subagents")]
    public SubAgentConfig? Subagents { get; init; }
}

public sealed record SubAgentConfig
{
    [JsonPropertyName("allowAgents")]
    public IReadOnlyList<string>? AllowAgents { get; init; }
}

public sealed record AgentIdentityConfig
{
    [JsonPropertyName("name")]
    public string Name { get; init; } = string.Empty;

    [JsonPropertyName("theme")]
    public string Theme { get; init; } = string.Empty;
}

public sealed record AgentInfo(
    string Id,
    string Name,
    string Role,
    string Model,
    OperationalStatus Status,
    DateTimeOffset? LastSeen,
    string? Workspace,
    string? Description
);

public sealed record AgentDetail(
    string Id,
    string Name,
    string Role,
    string Model,
    OperationalStatus Status,
    DateTimeOffset? LastSeen,
    string? Workspace,
    string? AgentDir,
    string? Description,
    IReadOnlyList<string>? SubAgents,
    string? IdentityName
);

public interface IAgentService
{
    Task<IReadOnlyCollection<AgentInfo>> GetAgentsAsync(CancellationToken cancellationToken);
    Task<AgentDetail?> GetAgentAsync(string id, CancellationToken cancellationToken);
}

public sealed class AgentService(IConfiguration configuration, IAgentRuntime runtime) : IAgentService
{
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNameCaseInsensitive = true,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
    };

    public async Task<IReadOnlyCollection<AgentInfo>> GetAgentsAsync(CancellationToken cancellationToken)
    {
        var configs = await LoadAgentConfigsAsync(cancellationToken);
        var runtimeStatus = await runtime.GetStatusAsync(cancellationToken);
        var overallOperational = runtimeStatus.Status;

        var now = DateTimeOffset.UtcNow;
        var agents = new List<AgentInfo>(configs.Count);
        foreach (var config in configs)
        {
            var model = config.Model ?? "deepseek/deepseek-v4-flash";
            var role = DeriveRole(config.Id);
            var description = config.Identity?.Theme ?? string.Empty;

            if (string.IsNullOrEmpty(description))
            {
                description = config.Id switch
                {
                    "main" => "Primary conversational agent — routing and general-purpose chat",
                    _ => description
                };
            }

            agents.Add(new AgentInfo(
                Id: config.Id,
                Name: config.Identity?.Name ?? config.Name ?? config.Id,
                Role: role,
                Model: model,
                Status: overallOperational,
                LastSeen: now,
                Workspace: config.Workspace,
                Description: description
            ));
        }

        return agents.AsReadOnly();
    }

    public async Task<AgentDetail?> GetAgentAsync(string id, CancellationToken cancellationToken)
    {
        var configs = await LoadAgentConfigsAsync(cancellationToken);
        var config = configs.FirstOrDefault(a =>
            a.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
        if (config is null) return null;

        var runtimeStatus = await runtime.GetStatusAsync(cancellationToken);
        var now = DateTimeOffset.UtcNow;
        var role = DeriveRole(config.Id);
        var description = config.Identity?.Theme ?? string.Empty;

        if (string.IsNullOrEmpty(description) && config.Id == "main")
            description = "Primary conversational agent — routing and general-purpose chat";

        return new AgentDetail(
            Id: config.Id,
            Name: config.Identity?.Name ?? config.Name ?? config.Id,
            Role: role,
            Model: config.Model ?? "deepseek/deepseek-v4-flash",
            Status: runtimeStatus.Status,
            LastSeen: now,
            Workspace: config.Workspace,
            AgentDir: config.AgentDir,
            Description: description,
            SubAgents: config.Subagents?.AllowAgents,
            IdentityName: config.Identity?.Name
        );
    }

    private static string DeriveRole(string agentId) => agentId.ToLowerInvariant() switch
    {
        "iris" => "Orchestrator",
        "programmer" => "Developer",
        "reviewer" => "Reviewer",
        "architekt" => "Architect",
        "main" => "Assistant",
        _ => "Custom"
    };

    private async Task<IReadOnlyList<AgentConfig>> LoadAgentConfigsAsync(CancellationToken cancellationToken)
    {
        var path = configuration.GetValue<string>("AgentConfigPath")
            ?? "/home/node/.openclaw/openclaw.json";

        if (!File.Exists(path))
            return Array.Empty<AgentConfig>();

        var json = await File.ReadAllTextAsync(path, cancellationToken);
        using var document = JsonDocument.Parse(json, new JsonDocumentOptions { AllowTrailingCommas = true });
        var root = document.RootElement;

        if (!root.TryGetProperty("agents", out var agentsElement))
            return Array.Empty<AgentConfig>();

        if (!agentsElement.TryGetProperty("list", out var listElement))
            return Array.Empty<AgentConfig>();

        var defaults = agentsElement.TryGetProperty("defaults", out var defaultsElement)
            ? JsonSerializer.Deserialize<AgentDefaults>(defaultsElement.GetRawText(), JsonOptions)
            : null;

        var configs = new List<AgentConfig>();
        foreach (var agentElement in listElement.EnumerateArray())
        {
            var config = JsonSerializer.Deserialize<AgentConfig>(agentElement.GetRawText(), JsonOptions);
            if (config is null || string.IsNullOrWhiteSpace(config.Id))
                continue;

            // Inherit defaults for missing fields
            if (string.IsNullOrWhiteSpace(config.Name))
                config = config with { Name = config.Id };
            if (string.IsNullOrWhiteSpace(config.Model) && defaults?.Model?.Primary is not null)
                config = config with { Model = defaults.Model.Primary };
            if (string.IsNullOrWhiteSpace(config.Workspace) && defaults?.Workspace is not null)
                config = config with { Workspace = defaults.Workspace };

            configs.Add(config);
        }

        return configs.AsReadOnly();
    }

    private sealed record AgentDefaults
    {
        [JsonPropertyName("workspace")]
        public string? Workspace { get; init; }

        [JsonPropertyName("model")]
        public AgentDefaultModel? Model { get; init; }
    }

    private sealed record AgentDefaultModel
    {
        [JsonPropertyName("primary")]
        public string? Primary { get; init; }
    }
}
