using System.Net;
using Sunfish.Config;

namespace Sunfish.Tests;

public class ConfigTests
{
    private const string Sample = """
        # Comentario de cabecera

        [Server]
        Name = Pruebas
        HttpPort = 9090
        ; otro comentario
        Bind = 127.0.0.1

        [Share web]
        Location = /
        Path = C:\www
        Listing = false

        [Share fotos]
        Location = fotos/
        Path = D:\Fotos
        Upload = yes
        Users = ana, pepe
        """;

    [Fact]
    public void ParsesValuesAndDefaults()
    {
        var config = SunfishConfig.Parse(Sample);
        Assert.Empty(config.Errors);
        Assert.Equal("Pruebas", config.Server.Name);
        Assert.Equal(9090, config.Server.HttpPort);
        Assert.Equal([IPAddress.Loopback], config.Server.Bind);
        Assert.Equal(2121, config.Server.FtpPort);

        var web = config.Shares[0];
        Assert.Equal("web", web.Name);
        Assert.False(web.Listing);
        Assert.Equal("index.html", web.Index);
        Assert.True(web.Mac);
        Assert.True(web.WebDav);

        var fotos = config.Shares[1];
        Assert.Equal("/fotos", fotos.Location);
        Assert.True(fotos.Upload);
        Assert.False(fotos.Delete);
        Assert.Equal(["ana", "pepe"], fotos.Users);
    }

    [Fact]
    public void RewritingKeepsCommentsAndOrder()
    {
        var config = SunfishConfig.Parse(Sample);
        config.Server.HttpPort = 8181;
        config.Shares[1].Delete = true;
        string text = config.ToText().Replace("\r\n", "\n");

        Assert.Contains("# Comentario de cabecera", text);
        Assert.Contains("; otro comentario", text);
        Assert.Contains("HttpPort = 8181", text);
        Assert.Contains("Delete = true", text);
        Assert.True(text.IndexOf("[Share web]", StringComparison.Ordinal) < text.IndexOf("[Share fotos]", StringComparison.Ordinal));
        // Los valores por defecto que no estaban no se anaden.
        Assert.DoesNotContain("Mac =", text);
    }

    [Fact]
    public void RoundTripWithoutChangesIsIdentical()
    {
        var config = SunfishConfig.Parse(Sample);
        string normalizedSample = Sample.Replace("\r\n", "\n") + "\n";
        // Location se normaliza al leer ("fotos/" -> "/fotos"): es el unico cambio esperado.
        string expected = normalizedSample.Replace("Location = fotos/", "Location = /fotos");
        Assert.Equal(expected, config.ToText().Replace("\r\n", "\n"));
    }

    [Fact]
    public void AddingAndRemovingShares()
    {
        var config = SunfishConfig.Parse(Sample);
        config.Shares.RemoveAt(0);
        config.Shares.Add(new ShareConfig { Name = "musica", Location = "/musica", Path = "E:\\Musica", Zip = true });
        string text = config.ToText().Replace("\r\n", "\n");

        Assert.DoesNotContain("[Share web]", text);
        Assert.Contains("[Share musica]\nLocation = /musica\nPath = E:\\Musica\nZip = true\n", text);

        var reread = SunfishConfig.Parse(text);
        Assert.Equal(["fotos", "musica"], reread.Shares.Select(s => s.Name));
    }

    [Fact]
    public void ReportsErrorsWithLineNumbers()
    {
        var config = SunfishConfig.Parse("""
            [Server]
            HttpPort = muchos
            esto no es nada
            [Share a]
            Location = /x
            [Share b]
            Location = /x
            Path = C:\b
            [Raro]
            """);
        Assert.Contains(config.Errors, e => e.Contains("2") && e.Contains("muchos"));
        Assert.Contains(config.Errors, e => e.Contains("3"));
        Assert.Contains(config.Errors, e => e.Contains("Path"));
        Assert.Contains(config.Errors, e => e.Contains("Raro"));
        Assert.Equal(8080, config.Server.HttpPort);
        Assert.False(config.Shares[0].Enabled); // sin Path
        Assert.True(config.Shares[1].Enabled);  // la location ya no esta ocupada por un share activo
    }

    [Fact]
    public void RejectsReservedAndInvalidLocations()
    {
        var config = SunfishConfig.Parse("""
            [Share a]
            Location = /$sunfish/x
            Path = C:\a
            [Share b]
            Location = /a/../b
            Path = C:\b
            """);
        Assert.All(config.Shares, s => Assert.False(s.Enabled));
        Assert.Equal(2, config.Errors.Count);
    }

    [Fact]
    public void SaveIsAtomicAndReadable()
    {
        using var dir = new TempDir();
        string path = Path.Combine(dir.Path, "sub", "sunfish.conf");
        var config = new SunfishConfig();
        config.Shares.Add(new ShareConfig { Name = "x", Location = "/x", Path = dir.Path });
        config.Save(path);

        Assert.True(File.Exists(path));
        Assert.False(File.Exists(path + ".tmp"));
        var loaded = SunfishConfig.Load(path);
        Assert.Equal("/x", loaded.Shares.Single().Location);
    }
}
