using ZB.MOM.WW.Secrets.Cli.Interactive;
using ZB.MOM.WW.Secrets.MasterKey;

namespace ZB.MOM.WW.Secrets.Tests.Cli.Interactive;

/// <summary>
/// Tests for <see cref="TargetConfigReader"/>: it must compose the target app's configuration
/// (base json + optional environment overlay + environment variables) exactly the way the app
/// would at startup, so fixes land in the store the app actually reads.
/// </summary>
public sealed class TargetConfigReaderTests : IDisposable
{
    private readonly List<string> _tempDirs = [];

    public void Dispose()
    {
        foreach (string dir in _tempDirs)
        {
            if (Directory.Exists(dir))
            {
                try { Directory.Delete(dir, recursive: true); } catch (IOException) { /* best-effort temp cleanup */ }
            }
        }
    }

    private string NewTempDir()
    {
        string dir = Path.Combine(Path.GetTempPath(), "zb-secrets-cfg-" + Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(dir);
        _tempDirs.Add(dir);
        return dir;
    }

    [Fact]
    public void Reads_secrets_section_and_binds_options()
    {
        string dir = NewTempDir();
        string path = Path.Combine(dir, "appsettings.json");
        File.WriteAllText(path, """
            {
              "Secrets": {
                "SqlitePath": "custom.db",
                "MasterKey": {
                  "Source": "Environment",
                  "EnvVarName": "MY_KEY"
                }
              }
            }
            """);

        StoreTarget target = new TargetConfigReader().Read(path);

        Assert.Equal(MasterKeySource.Environment, target.Secrets.MasterKey.Source);
        Assert.Equal("MY_KEY", target.Secrets.MasterKey.EnvVarName);
        Assert.Equal(Path.Combine(dir, "custom.db"), target.Secrets.SqlitePath);
    }

    [Fact]
    public void Environment_overlay_wins_over_base()
    {
        string dir = NewTempDir();
        File.WriteAllText(Path.Combine(dir, "appsettings.json"), """
            {
              "Secrets": { "SqlitePath": "base.db" }
            }
            """);
        File.WriteAllText(Path.Combine(dir, "appsettings.Production.json"), """
            {
              "Secrets": { "SqlitePath": "prod.db" }
            }
            """);

        StoreTarget target = new TargetConfigReader().Read(
            Path.Combine(dir, "appsettings.json"), environment: "Production");

        Assert.Equal(Path.Combine(dir, "prod.db"), target.Secrets.SqlitePath);
    }

    [Fact]
    public void Binds_sqlserver_options_when_section_present()
    {
        string dir = NewTempDir();
        File.WriteAllText(Path.Combine(dir, "appsettings.json"), """
            {
              "Secrets": {
                "SqlitePath": "secrets.db",
                "SqlServer": {
                  "ConnectionString": "Server=hub;Database=ZbSecrets;Trusted_Connection=True"
                }
              }
            }
            """);

        StoreTarget withSql = new TargetConfigReader().Read(Path.Combine(dir, "appsettings.json"));

        Assert.NotNull(withSql.SqlServer);
        Assert.Equal(
            "Server=hub;Database=ZbSecrets;Trusted_Connection=True",
            withSql.SqlServer!.ConnectionString);

        string dir2 = NewTempDir();
        File.WriteAllText(Path.Combine(dir2, "appsettings.json"), """
            {
              "Secrets": { "SqlitePath": "secrets.db" }
            }
            """);

        StoreTarget noSql = new TargetConfigReader().Read(Path.Combine(dir2, "appsettings.json"));

        Assert.Null(noSql.SqlServer);
    }

    [Fact]
    public void SqlitePath_is_resolved_relative_to_the_app_directory()
    {
        string dir = NewTempDir();
        File.WriteAllText(Path.Combine(dir, "appsettings.json"), """
            {
              "Secrets": { "SqlitePath": "secrets.db" }
            }
            """);

        StoreTarget target = new TargetConfigReader().Read(Path.Combine(dir, "appsettings.json"));

        Assert.True(Path.IsPathRooted(target.Secrets.SqlitePath));
        Assert.Equal(Path.Combine(dir, "secrets.db"), target.Secrets.SqlitePath);
    }

    [Fact]
    public void Missing_file_throws_FileNotFoundException_with_path()
    {
        string dir = NewTempDir();
        string path = Path.Combine(dir, "appsettings.json");

        FileNotFoundException ex = Assert.Throws<FileNotFoundException>(
            () => new TargetConfigReader().Read(path));

        Assert.Contains(path, ex.Message);
    }

    [Fact]
    public void Custom_section_path_is_honored()
    {
        string dir = NewTempDir();
        File.WriteAllText(Path.Combine(dir, "appsettings.json"), """
            {
              "MySecrets": {
                "SqlitePath": "elsewhere.db",
                "MasterKey": { "EnvVarName": "OTHER_KEY" }
              }
            }
            """);

        StoreTarget target = new TargetConfigReader().Read(
            Path.Combine(dir, "appsettings.json"), sectionPath: "MySecrets");

        Assert.Equal("OTHER_KEY", target.Secrets.MasterKey.EnvVarName);
        Assert.Equal(Path.Combine(dir, "elsewhere.db"), target.Secrets.SqlitePath);
    }
}
