using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Nexus.Api.Controllers;
using Nexus.Api.Data;
using Nexus.Api.Integrations;
using Nexus.Api.Models;
using Nexus.Api.Repositories;
using Nexus.Api.Services;
using Xunit;

namespace Nexus.Api.Tests;

public class OperationsSnapshotTests
{
    [Fact]
    public void GetSnapshot_RequiresAuthorization()
    {
        var method = typeof(OperationsController).GetMethod(nameof(OperationsController.GetSnapshot), BindingFlags.Instance | BindingFlags.Public);

        Assert.NotNull(method);
        Assert.NotNull(method!.GetCustomAttribute<AuthorizeAttribute>());
    }

    [Fact]
    public async Task GetSnapshotAsync_DoesNotOverlapRepositoryReads()
    {
        var guard = new RepositoryConcurrencyGuard();
        var runtime = new SnapshotRuntimeStub();
        var agentService = new SnapshotAgentServiceStub();
        var projectRepo = new GuardedProjectRepository(guard);
        var taskRepo = new GuardedTaskRepository(guard);
        var activityRepo = new GuardedActivityRepository(guard);
        var service = new OperationsService(runtime, agentService, projectRepo, taskRepo, activityRepo);

        await service.GetSnapshotAsync(CancellationToken.None);

        Assert.Equal(1, guard.MaxConcurrentCalls);
    }
}

internal sealed class RepositoryConcurrencyGuard
{
    private readonly Lock sync = new();
    private int currentCalls;

    public int MaxConcurrentCalls { get; private set; }

    public async Task<T> RunAsync<T>(T value, CancellationToken ct)
    {
        lock (sync)
        {
            currentCalls++;
            MaxConcurrentCalls = Math.Max(MaxConcurrentCalls, currentCalls);
        }

        try
        {
            await Task.Delay(25, ct);
            return value;
        }
        finally
        {
            lock (sync)
            {
                currentCalls--;
            }
        }
    }
}

internal sealed class GuardedProjectRepository(RepositoryConcurrencyGuard guard) : IProjectRepository
{
    public Task<List<Project>> GetAllAsync(CancellationToken ct = default)
        => guard.RunAsync(new List<Project>
        {
            new() { Name = "Alpha", Status = OperationalStatus.Online, Progress = 75 }
        }, ct);

    public ValueTask<Project?> GetByIdAsync(Guid id, CancellationToken ct = default) => throw new NotSupportedException();
    public Task<Project> AddAsync(Project project, CancellationToken ct = default) => throw new NotSupportedException();
    public Task UpdateAsync(Project project, CancellationToken ct = default) => throw new NotSupportedException();
    public Task DeleteAsync(Project project, CancellationToken ct = default) => throw new NotSupportedException();
    public Task<bool> HasTasksAsync(Guid projectId, CancellationToken ct = default) => throw new NotSupportedException();
    public Task<List<WorkTask>> GetTasksAsync(
        Guid projectId,
        CancellationToken ct = default)
        => throw new NotSupportedException();
}

internal sealed class GuardedTaskRepository(RepositoryConcurrencyGuard guard) : ITaskRepository
{
    public Task<List<WorkTask>> GetAllAsync(CancellationToken ct = default)
        => guard.RunAsync(new List<WorkTask>
        {
            new() { Title = "Blocked task", State = TaskStateHelper.ToStateString(TaskState.Blocked), UpdatedAt = DateTimeOffset.UtcNow },
            new() { Title = "Done task", State = TaskStateHelper.ToStateString(TaskState.Done), UpdatedAt = DateTimeOffset.UtcNow }
        }, ct);

    public ValueTask<WorkTask?> GetByIdAsync(Guid id, CancellationToken ct = default) => throw new NotSupportedException();
    public Task<List<WorkTask>> GetPendingApprovalAsync(CancellationToken ct = default) => throw new NotSupportedException();
    public Task<WorkTask> AddAsync(WorkTask task, CancellationToken ct = default) => throw new NotSupportedException();
    public Task<bool> TryResetStaleInProgressToBacklogAsync(Guid id, DateTimeOffset staleBefore, DateTimeOffset updatedAt, CancellationToken ct = default)
        => throw new NotSupportedException();
    public Task UpdateAsync(WorkTask task, CancellationToken ct = default) => throw new NotSupportedException();
    public Task DeleteAsync(WorkTask task, CancellationToken ct = default) => throw new NotSupportedException();
    public Task<int> CountAsync(CancellationToken ct = default) => throw new NotSupportedException();
    public Task<int> CountByStateAsync(string state, CancellationToken ct = default) => throw new NotSupportedException();
    public Task<WorkTask?> GetLastBlockedAsync(CancellationToken ct = default) => throw new NotSupportedException();
    public Task<TaskBoardQueryPage> GetBoardPageAsync(
        int doneLimit,
        DateTimeOffset? doneBeforeUpdatedAt,
        Guid? doneBeforeId,
        CancellationToken ct = default)
        => throw new NotSupportedException();
    public Task<TaskBoardCardDto?> GetBoardCardAsync(
        Guid id,
        CancellationToken ct = default)
        => throw new NotSupportedException();
}

internal sealed class GuardedActivityRepository(RepositoryConcurrencyGuard guard) : IActivityRepository
{
    public Task<List<ActivityEvent>> GetRecentAsync(int take, CancellationToken ct = default)
        => guard.RunAsync(new List<ActivityEvent>
        {
            new() { Id = 1, Type = "agent", Message = "recent activity", CreatedAt = DateTimeOffset.UtcNow }
        }, ct);

    public Task<List<ActivityEvent>> GetRecentForTasksAsync(IEnumerable<Guid> taskIds, CancellationToken ct = default)
        => guard.RunAsync(new List<ActivityEvent>(), ct);

    public Task<(List<ActivityEvent> Items, int TotalCount)> GetPagedAsync(string? type, string? sort, int page, int pageSize, CancellationToken ct = default)
        => throw new NotSupportedException();

    public Task<List<ActivityEvent>> GetByAgentAsync(string agentId, int take, CancellationToken ct = default)
        => throw new NotSupportedException();

    public Task<ActivityEvent> AddAsync(ActivityEvent activity, CancellationToken ct = default)
        => throw new NotSupportedException();
}

internal sealed class SnapshotRuntimeStub : IAgentRuntime
{
    public string Name => "stub";

    public Task<AgentRuntimeStatus> GetStatusAsync(CancellationToken cancellationToken = default)
        => Task.FromResult(new AgentRuntimeStatus("OpenClaw", OperationalStatus.Online, TimeSpan.FromMilliseconds(5), "ok"));

    public Task<AgentChatResult> ChatAsync(string message, string conversationId, string agentId, CancellationToken cancellationToken = default)
        => throw new NotSupportedException();
}

internal sealed class SnapshotAgentServiceStub : IAgentService
{
    public Task<IReadOnlyCollection<AgentInfo>> GetAgentsAsync(CancellationToken cancellationToken)
        => Task.FromResult<IReadOnlyCollection<AgentInfo>>(
            [
                new AgentInfo("iris", "Iris", "Orchestrator", "model", OperationalStatus.Online, DateTimeOffset.UtcNow, "/workspace", "ops")
            ]);

    public Task<AgentDetail?> GetAgentAsync(string id, CancellationToken cancellationToken)
        => throw new NotSupportedException();

    public Task<IReadOnlySet<string>> GetAllowedAgentIdsAsync(CancellationToken cancellationToken)
        => Task.FromResult<IReadOnlySet<string>>(new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "iris" });
}
