using System.Text.Json;

namespace ZB.MOM.NatsNet.Server;

public sealed partial class Account
{
    internal (NatsStream? Stream, Exception? Error) AddStream(StreamConfig config) =>
        AddStreamWithAssignment(config, null, null, pedantic: false);

    internal (NatsStream? Stream, Exception? Error) AddStreamWithStore(StreamConfig config, FileStoreConfig? fileStoreConfig) =>
        AddStreamWithAssignment(config, fileStoreConfig, null, pedantic: false);

    internal (NatsStream? Stream, Exception? Error) AddStreamPedantic(StreamConfig config, bool pedantic) =>
        AddStreamWithAssignment(config, null, null, pedantic);

    internal (NatsStream? Stream, Exception? Error) AddStreamWithAssignment(
        StreamConfig config,
        FileStoreConfig? fileStoreConfig,
        StreamAssignment? assignment,
        bool pedantic)
    {
        if (config == null)
            return (null, new ArgumentNullException(nameof(config)));

        var (server, jsa, error) = CheckForJetStream();
        if (error != null || server == null || jsa == null)
            return (null, error ?? new InvalidOperationException("jetstream not enabled for account"));

        if (string.IsNullOrWhiteSpace(config.Name))
            return (null, new InvalidOperationException("stream name is required"));

        var normalized = config.Clone();
        if (normalized.Subjects == null || normalized.Subjects.Length == 0)
            normalized.Subjects = [$"{normalized.Name}.>"];

        _ = pedantic;
        _ = fileStoreConfig;

        jsa.Lock.EnterWriteLock();
        try
        {
            if (jsa.Streams.TryGetValue(normalized.Name, out var existingObject) && existingObject is NatsStream existing)
            {
                if (StreamConfigsEqual(existing.Config, normalized))
                {
                    if (assignment != null)
                        existing.SetStreamAssignment(assignment);
                    return (existing, null);
                }

                return (null, new InvalidOperationException("stream name already in use"));
            }

            IStreamStore store = new JetStreamMemStore(normalized.Clone());

            var stream = NatsStream.Create(this, normalized, jsa, store, assignment, server);
            if (stream == null)
                return (null, new InvalidOperationException("stream creation failed"));

            jsa.Streams[normalized.Name] = stream;
            return (stream, null);
        }
        finally
        {
            jsa.Lock.ExitWriteLock();
        }
    }

    private static bool StreamConfigsEqual(StreamConfig left, StreamConfig right)
        => string.Equals(JsonSerializer.Serialize(left), JsonSerializer.Serialize(right), StringComparison.Ordinal);
}
