using System;
using System.Text.Json;
using PolyTrader.Core.Persistence.Ef;
using PolyTrader.Tests.TestSupport;
using PolyTraderSharp.Models;
using PolyTraderSharp.Services;
using Xunit;

namespace PolyTrader.Tests
{
    /// <summary>
    /// Sicherheitsnetz für das S-0-Datenfundament: JSONL-Log-Format (pur) und die
    /// Entscheidungsjournal-/Order-Event-Repos (EF InMemory, inkl. Enum-als-String-Roundtrip).
    /// </summary>
    public class AnalysisJournalTests
    {
        // ----- LogJson (pur) -----

        [Fact]
        public void LogJson_produces_valid_single_line_json()
        {
            var e = new LogMessageEventArgs("Zeile1\nZeile2 mit \"Quotes\" und 0,95 €", LogLevel.Trade, "sig-123");
            string line = LogJson.Format(e);

            Assert.DoesNotContain('\n', line);                    // eine Zeile (JSONL)
            using var doc = JsonDocument.Parse(line);             // valides JSON
            Assert.Equal("Trade", doc.RootElement.GetProperty("level").GetString());
            Assert.Equal("sig-123", doc.RootElement.GetProperty("cid").GetString());
            Assert.Contains("Zeile2", doc.RootElement.GetProperty("msg").GetString());
        }

        [Fact]
        public void LogJson_omits_empty_correlation_id()
        {
            string line = LogJson.Format(new LogMessageEventArgs("msg", LogLevel.Info));
            using var doc = JsonDocument.Parse(line);
            Assert.False(doc.RootElement.TryGetProperty("cid", out _));
        }

        [Fact]
        public void LogJson_parse_roundtrips_format()
        {
            var e = new LogMessageEventArgs("Nachricht äöü", LogLevel.Warning, "sig-9");
            var p = LogJson.ParseLine(LogJson.Format(e));

            Assert.NotNull(p);
            Assert.Equal("Warning", p!.Level);
            Assert.Equal("sig-9", p.Cid);
            Assert.Equal("Nachricht äöü", p.Message);
        }

        [Theory]
        [InlineData("")]
        [InlineData("   ")]
        [InlineData("kein json")]
        [InlineData("{kaputt")]
        public void LogJson_parse_returns_null_for_broken_lines(string line)
        {
            Assert.Null(LogJson.ParseLine(line));
        }

        // ----- Repos (EF InMemory) -----

        private static InMemoryContextFactory<CoreDbContext> Factory() => new(o => new CoreDbContext(o));

        [Fact]
        public void DecisionJournal_write_and_query_roundtrip()
        {
            var journal = new EfDecisionJournal(Factory());
            journal.Write(new DecisionRecord
            {
                SignalId = "sig-1", ModuleName = "CopyTrading", AccountId = 1, TokenId = "tok",
                Side = "BUY", Decision = TradeDecision.Rejected, Reason = DecisionReason.MaxBuyPriceExceeded,
                SignalPrice = 0.97m, Message = "test"
            });
            journal.Write(new DecisionRecord
            {
                SignalId = "sig-2", AccountId = 1, TokenId = "tok",
                Decision = TradeDecision.Executed, Reason = DecisionReason.OrderPlaced
            });

            var rejects = journal.Query(d => d.Reason == DecisionReason.MaxBuyPriceExceeded);
            Assert.Single(rejects);
            Assert.Equal("sig-1", rejects[0].SignalId);
            Assert.Equal(TradeDecision.Rejected, rejects[0].Decision);   // Enum-Roundtrip
            Assert.Equal(2, journal.Query(d => d.AccountId == 1).Count);
        }

        [Fact]
        public void OrderEventLog_write_and_query_roundtrip()
        {
            var log = new EfOrderEventLog(Factory());
            log.Write(new OrderEventRecord
            {
                SignalId = "sig-1", AccountId = 1, TokenId = "tok",
                EventType = OrderEventType.LadderStep, Side = "SELL", Price = 0.42m, Response = "OK"
            });

            var events = log.Query(e => e.SignalId == "sig-1");
            Assert.Single(events);
            Assert.Equal(OrderEventType.LadderStep, events[0].EventType);
        }

        [Fact]
        public void CopySignal_gets_signal_id_automatically()
        {
            var a = new CopySignal();
            var b = new CopySignal();
            Assert.False(string.IsNullOrEmpty(a.SignalId));
            Assert.NotEqual(a.SignalId, b.SignalId);
        }
    }
}
