using QuantEngine.Application.Services;
using QuantEngine.Core.Interfaces;
using QuantEngine.Core.Models;

namespace QuantEngine.Core.Tests;

public class ApplicationServiceTests
{
    [Fact]
    public async Task WorkspaceService_ForwardsSettingAndHistoryOperations()
    {
        var repo = new FakeWorkspaceRepository();
        var history = new FakeHistoryStore();
        var service = new WorkspaceService(repo, history);

        var setting = new Setting { Ordinal = 1, Key = "risk_mode", ValueJson = "\"RISK_ON\"" };
        Assert.True(await service.UpsertSettingAsync(setting));
        Assert.Equal(setting, repo.LastSetting);

        var payload = new Dictionary<string, object?> { ["foo"] = "bar" };
        Assert.Equal(1, await service.AppendHistoryAsync("decision_result_history", payload));
        Assert.Equal("decision_result_history", history.LastDomain);
        Assert.Equal("bar", history.LastPayload?["foo"]);
    }

    [Fact]
    public async Task ApprovalService_ForwardsApprovalAndLockOperations()
    {
        var repo = new FakeWorkspaceRepository();
        var service = new ApprovalService(repo);

        var approval = new WorkspaceApproval { Domain = "settings", TargetRef = "portfolio", Status = "APPROVED" };
        Assert.True(await service.UpsertApprovalAsync(approval));
        Assert.Equal(approval, repo.LastApproval);

        var lockRow = new WorkspaceLock { Domain = "settings", TargetRef = "portfolio", LockedBy = "qa", Reason = "review" };
        Assert.True(await service.AcquireLockAsync(lockRow));
        Assert.Equal(lockRow, repo.LastLock);
        Assert.True(await service.ReleaseLockAsync("settings", "portfolio"));
        Assert.Equal(("settings", "portfolio"), repo.LastReleasedLock);
    }

    [Fact]
    public async Task CollectionService_AppendsRunSnapshotAndErrorRecords()
    {
        var history = new FakeHistoryStore();
        var service = new CollectionService(history);

        await service.AppendRunAsync(new CollectionRun
        {
            RunId = "run-1",
            CollectorName = "kis",
            StartedAt = "2026-06-26T09:00:00+09:00",
            Status = "PASS"
        });

        Assert.Equal("collection_run_history", history.LastDomain);
        Assert.Equal("run-1", history.LastPayload?["run_id"]);

        await service.AppendSnapshotAsync(new CollectionSnapshot
        {
            RunId = "run-1",
            DatasetName = "decision_result_history",
            Ticker = "005930",
            SourcePriority = "KIS",
            SourceStatus = "PASS",
            PayloadJson = "{}",
            ProvenanceJson = "{}"
        });

        Assert.Equal("collection_snapshot_history", history.LastDomain);
        Assert.Equal("005930", history.LastPayload?["ticker"]);

        await service.AppendSourceErrorAsync(new CollectionSourceError
        {
            RunId = "run-1",
            SourceName = "naver",
            ErrorKind = "TIMEOUT",
            ErrorMessage = "timeout"
        });

        Assert.Equal("collection_source_error_history", history.LastDomain);
        Assert.Equal("TIMEOUT", history.LastPayload?["error_kind"]);
    }

    [Fact]
    public async Task FormulaService_ForwardsFormulaExecutionAndHistory()
    {
        var history = new FakeHistoryStore();
        var service = new FormulaService(history);

        var timing = service.ComputeTimingDecision(new Dictionary<string, object>
        {
            ["entryModeGate"] = "PASS",
            ["entryMode"] = "BREAKOUT",
            ["leaderGate"] = "PASS",
            ["acGate"] = "CLEAR",
            ["priceStatus"] = "PRICE_OK",
            ["atr20"] = 1.0,
            ["leaderTotal"] = 4,
            ["flowCredit"] = 0.7,
            ["avgTradeValue5D"] = 100,
            ["spreadPct"] = 0.5
        });

        Assert.NotEqual(string.Empty, timing.Action);

        await service.AppendFormulaRunAsync("timing", new Dictionary<string, object?>
        {
            ["action"] = timing.Action,
            ["entry_score"] = timing.EntryScore
        });

        Assert.Equal("formula_timing_history", history.LastDomain);
        Assert.Equal(timing.Action, history.LastPayload?["action"]);
    }

    private sealed class FakeWorkspaceRepository : IWorkspaceRepository
    {
        public Setting? LastSetting { get; private set; }
        public WorkspaceApproval? LastApproval { get; private set; }
        public WorkspaceLock? LastLock { get; private set; }
        public (string Domain, string TargetRef)? LastReleasedLock { get; private set; }

        public Task<IEnumerable<Setting>> GetSettingsAsync() => Task.FromResult(Enumerable.Empty<Setting>());
        public Task<Setting?> GetSettingByKeyAsync(string key) => Task.FromResult<Setting?>(null);
        public Task<bool> UpsertSettingAsync(Setting setting) { LastSetting = setting; return Task.FromResult(true); }
        public Task<bool> DeleteSettingAsync(string key) => Task.FromResult(true);

        public Task<IEnumerable<AccountSnapshot>> GetAccountSnapshotsAsync() => Task.FromResult(Enumerable.Empty<AccountSnapshot>());
        public Task<bool> InsertAccountSnapshotsAsync(IEnumerable<AccountSnapshot> snapshots) => Task.FromResult(true);
        public Task<bool> ClearAccountSnapshotsAsync() => Task.FromResult(true);

        public Task<IEnumerable<WorkspaceApproval>> GetApprovalsAsync() => Task.FromResult(Enumerable.Empty<WorkspaceApproval>());
        public Task<WorkspaceApproval?> GetApprovalAsync(string domain, string targetRef) => Task.FromResult<WorkspaceApproval?>(null);
        public Task<bool> UpsertApprovalAsync(WorkspaceApproval approval) { LastApproval = approval; return Task.FromResult(true); }

        public Task<IEnumerable<WorkspaceLock>> GetLocksAsync() => Task.FromResult(Enumerable.Empty<WorkspaceLock>());
        public Task<WorkspaceLock?> GetLockAsync(string domain, string targetRef) => Task.FromResult<WorkspaceLock?>(null);
        public Task<bool> AcquireLockAsync(WorkspaceLock @lock) { LastLock = @lock; return Task.FromResult(true); }
        public Task<bool> ReleaseLockAsync(string domain, string targetRef) { LastReleasedLock = (domain, targetRef); return Task.FromResult(true); }
    }

    private sealed class FakeHistoryStore : IPostgresqlHistoryStore
    {
        public string? LastDomain { get; private set; }
        public IDictionary<string, object?>? LastPayload { get; private set; }

        public Task<int> AppendAsync(string domain, IDictionary<string, object?> payload)
        {
            LastDomain = domain;
            LastPayload = new Dictionary<string, object?>(payload);
            return Task.FromResult(1);
        }

        public Task<IReadOnlyList<IDictionary<string, object?>>> SnapshotAsync(string domain, int limit = 500)
            => Task.FromResult<IReadOnlyList<IDictionary<string, object?>>>(Array.Empty<IDictionary<string, object?>>());
    }
}
