using System.Text;
using Microsoft.Extensions.Logging.Abstractions;
using NeonSprawl.Server.Game.Skills;
using Xunit;

namespace NeonSprawl.Server.Tests.Game.Skills;

public sealed class ContentBackedSkillLevelCurveTests
{
    private const string LevelCurveSchemaJson =
        """
        {
          "$schema": "https://json-schema.org/draft/2020-12/schema",
          "type": "object",
          "additionalProperties": false,
          "required": ["schemaVersion", "levels"],
          "properties": {
            "schemaVersion": { "type": "integer", "const": 1 },
            "levels": {
              "type": "array",
              "minItems": 1,
              "items": {
                "type": "object",
                "additionalProperties": false,
                "required": ["level", "requiredXp"],
                "properties": {
                  "level": { "type": "integer", "minimum": 1 },
                  "requiredXp": { "type": "integer", "minimum": 0 }
                }
              }
            }
          }
        }
        """;

    private static ContentBackedSkillLevelCurve BuildCurve() =>
        new(
        [
            new ContentBackedSkillLevelCurve.LevelThresholdRow(1, 0),
            new ContentBackedSkillLevelCurve.LevelThresholdRow(2, 100),
            new ContentBackedSkillLevelCurve.LevelThresholdRow(3, 250),
            new ContentBackedSkillLevelCurve.LevelThresholdRow(4, 450),
        ]);

    [Theory]
    [InlineData(-5, 1)]
    [InlineData(0, 1)]
    [InlineData(99, 1)]
    [InlineData(100, 2)]
    [InlineData(249, 2)]
    [InlineData(250, 3)]
    [InlineData(449, 3)]
    [InlineData(450, 4)]
    [InlineData(9999, 4)]
    public void LevelFromTotalXp_ShouldResolveExpectedLevelAcrossBoundaries(int xp, int expectedLevel)
    {
        // Arrange
        var curve = BuildCurve();

        // Act
        var level = curve.LevelFromTotalXp(xp);

        // Assert
        Assert.Equal(expectedLevel, level);
    }

    [Fact]
    public void Load_ShouldThrow_WhenCurveHasUnexpectedTopLevelProperty()
    {
        // Arrange
        var root = Directory.CreateTempSubdirectory("neon-sprawl-level-curve-");
        try
        {
            var skillsDir = Path.Combine(root.FullName, "content", "skills");
            var schemaDir = Path.Combine(root.FullName, "content", "schemas");
            Directory.CreateDirectory(skillsDir);
            Directory.CreateDirectory(schemaDir);

            var curvePath = Path.Combine(skillsDir, "prototype_level_curve.json");
            var schemaPath = Path.Combine(schemaDir, "level-curve.schema.json");
            File.WriteAllText(schemaPath, LevelCurveSchemaJson, Encoding.UTF8);
            File.WriteAllText(
                curvePath,
                """
                {
                  "schemaVersion": 1,
                  "levels": [
                    { "level": 1, "requiredXp": 0 },
                    { "level": 2, "requiredXp": 100 }
                  ],
                  "extraField": "should-fail"
                }
                """,
                Encoding.UTF8);

            // Act
            var ex = Record.Exception(() =>
                ContentBackedSkillLevelCurve.Load(skillsDir, curvePath, schemaPath, NullLogger.Instance));

            // Assert
            var ioe = Assert.IsType<InvalidOperationException>(ex);
            Assert.Contains("extraField", ioe.Message, StringComparison.Ordinal);
        }
        finally
        {
            try
            {
                Directory.Delete(root.FullName, recursive: true);
            }
            catch (IOException)
            {
                // Best-effort cleanup only.
            }
        }
    }
}
