// ---------------------------------------------------------------------------
// BT.Framework.Tests
// DataTableTests.cs — DataTable·DataTableLoader 단위 테스트
// ---------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using NUnit.Framework;
using BurningTimes.Core.Data;

namespace BurningTimes.Tests.Core.Data
{
    public class DataTableTests
    {
        private class MonsterRow : IDataRow<int>
        {
            public int Id;
            public string Name;
            public int Hp;
            public int Key => Id;

            public static MonsterRow FromCsv(string[] cols)
            {
                return new MonsterRow
                {
                    Id = int.Parse(cols[0]),
                    Name = cols[1],
                    Hp = int.Parse(cols[2]),
                };
            }
        }

        // -----------------------------------------------------------------
        // DataTable core
        // -----------------------------------------------------------------

        [Test]
        public void Constructor_FromRows_PreservesOrderAndCount()
        {
            var rows = new[]
            {
                new MonsterRow { Id = 10, Name = "A", Hp = 100 },
                new MonsterRow { Id = 20, Name = "B", Hp = 200 },
            };

            var table = new DataTable<int, MonsterRow>(rows);

            Assert.That(table.Count, Is.EqualTo(2));
            Assert.That(table.Rows[0].Id, Is.EqualTo(10));
            Assert.That(table.Rows[1].Id, Is.EqualTo(20));
        }

        [Test]
        public void Get_ReturnsRowByKey()
        {
            var table = new DataTable<int, MonsterRow>(new[]
            {
                new MonsterRow { Id = 10, Name = "A", Hp = 100 },
            });

            var row = table.Get(10);

            Assert.That(row.Name, Is.EqualTo("A"));
        }

        [Test]
        public void Get_OnMissingKey_Throws()
        {
            var table = new DataTable<int, MonsterRow>(Array.Empty<MonsterRow>());
            Assert.Throws<KeyNotFoundException>(() => table.Get(999));
        }

        [Test]
        public void TryGet_ReturnsFalseOnMissingKey()
        {
            var table = new DataTable<int, MonsterRow>(Array.Empty<MonsterRow>());
            Assert.IsFalse(table.TryGet(999, out _));
        }

        [Test]
        public void Contains_Works()
        {
            var table = new DataTable<int, MonsterRow>(new[]
            {
                new MonsterRow { Id = 10, Name = "A" },
            });

            Assert.IsTrue(table.Contains(10));
            Assert.IsFalse(table.Contains(99));
        }

        [Test]
        public void Constructor_DuplicateKey_Throws()
        {
            var rows = new[]
            {
                new MonsterRow { Id = 10, Name = "A" },
                new MonsterRow { Id = 10, Name = "B" },
            };

            Assert.Throws<ArgumentException>(() => new DataTable<int, MonsterRow>(rows));
        }

        [Test]
        public void Constructor_Null_Throws()
        {
            Assert.Throws<ArgumentNullException>(() => new DataTable<int, MonsterRow>(null));
        }

        // -----------------------------------------------------------------
        // CSV loader
        // -----------------------------------------------------------------

        [Test]
        public void FromCsv_ParsesBasicRows()
        {
            string csv = "Id,Name,Hp\n10,Slime,100\n20,Goblin,200\n";

            var table = DataTableLoader.FromCsv<int, MonsterRow>(csv, MonsterRow.FromCsv);

            Assert.That(table.Count, Is.EqualTo(2));
            Assert.That(table.Get(10).Name, Is.EqualTo("Slime"));
            Assert.That(table.Get(20).Hp, Is.EqualTo(200));
        }

        [Test]
        public void FromCsv_HandlesQuotedCommas()
        {
            string csv = "Id,Name,Hp\n10,\"Red, Small\",100\n";

            var table = DataTableLoader.FromCsv<int, MonsterRow>(csv, MonsterRow.FromCsv);

            Assert.That(table.Get(10).Name, Is.EqualTo("Red, Small"));
        }

        [Test]
        public void FromCsv_HandlesEscapedQuotes()
        {
            string csv = "Id,Name,Hp\n10,\"She said \"\"hi\"\"\",100\n";

            var table = DataTableLoader.FromCsv<int, MonsterRow>(csv, MonsterRow.FromCsv);

            Assert.That(table.Get(10).Name, Is.EqualTo("She said \"hi\""));
        }

        [Test]
        public void FromCsv_SkipsEmptyRows()
        {
            string csv = "Id,Name,Hp\n10,A,100\n\n20,B,200\n";

            var table = DataTableLoader.FromCsv<int, MonsterRow>(csv, MonsterRow.FromCsv);

            Assert.That(table.Count, Is.EqualTo(2));
        }

        [Test]
        public void FromCsv_CrlfLineEndings_Handled()
        {
            string csv = "Id,Name,Hp\r\n10,A,100\r\n20,B,200\r\n";

            var table = DataTableLoader.FromCsv<int, MonsterRow>(csv, MonsterRow.FromCsv);

            Assert.That(table.Count, Is.EqualTo(2));
        }

        [Test]
        public void FromCsv_NullText_Throws()
        {
            Assert.Throws<ArgumentNullException>(() =>
                DataTableLoader.FromCsv<int, MonsterRow>(null, MonsterRow.FromCsv));
        }

        // -----------------------------------------------------------------
        // DataTableLoadedEvent (value semantics)
        // -----------------------------------------------------------------

        [Test]
        public void DataTableLoadedEvent_CarriesTypeAndCount()
        {
            var evt = new DataTableLoadedEvent(typeof(MonsterRow), 5);
            Assert.That(evt.TableType, Is.EqualTo(typeof(MonsterRow)));
            Assert.That(evt.RowCount, Is.EqualTo(5));
        }
    }
}
