using DeepDrftAPI.Models;
using DeepDrftContent;
using DeepDrftContent.Constants;
using DeepDrftContent.FileDatabase.Models;
using DeepDrftContent.FileDatabase.Services;
using DeepDrftContent.Processors;
using Microsoft.Extensions.Logging;
using NetBlocks.Utilities.Environment;

namespace DeepDrftAPI
{
    public static class Startup
    {
        public static Task ConfigureDomainServices(WebApplicationBuilder builder)
        {
            // Audio services
            builder.Services.AddSingleton<AudioProcessor>();
            builder.Services.AddSingleton<Mp3AudioProcessor>();
            builder.Services.AddSingleton<FlacAudioProcessor>();
            builder.Services.AddSingleton<AudioProcessorRouter>();
            builder.Services.AddSingleton<TrackContentService>();

            // Image services
            builder.Services.AddSingleton<ImageProcessor>();

            // Waveform loudness profiling (upload-time, off the playback path)
            builder.Services.Configure<WaveformProfileOptions>(
                builder.Configuration.GetSection(nameof(WaveformProfileOptions)));
            builder.Services.AddSingleton<ILoudnessAlgorithm, RmsLoudnessAlgorithm>();
            builder.Services.AddSingleton<WaveformProfileService>();

            // File Database
            var fileDatabasePath = CredentialTools.ResolvePathOrThrow("filedatabase", "environment/filedatabase.json");
            builder.Configuration.AddJsonFile(fileDatabasePath, optional: false, reloadOnChange: false);
            var fileDatabaseSettings = builder.Configuration.GetSection(nameof(FileDatabaseSettings)).Get<FileDatabaseSettings>();
            if (fileDatabaseSettings is null) { throw new Exception("File database settings are not configured"); }

            var vaultPath = fileDatabaseSettings.VaultPath;
            builder.Services.AddSingleton(sp =>
            {
                var logger = sp.GetRequiredService<ILogger<FileDatabase>>();
                var db = FileDatabase.FromAsync(vaultPath, logger).GetAwaiter().GetResult();
                if (db is null) throw new Exception("Unable to initialize file database");
                InitializeTrackVault(db).GetAwaiter().GetResult();
                InitializeImageVault(db).GetAwaiter().GetResult();
                InitializeTrackWaveformsVault(db).GetAwaiter().GetResult();
                return db;
            });

            // Upload staging directory. Large audio bodies (multi-hundred-MB WAVs) must never stage on
            // the system temp mount — on the Linux host /tmp is a small RAM-backed tmpfs. We move BOTH
            // on-disk copies of an upload off /tmp onto the data disk:
            //   Layer 1 — the framework's multipart file-section buffer (FileBufferingReadStream), which
            //             reads its directory from the ASPNETCORE_TEMP env var (falling back to
            //             Path.GetTempPath()). Setting the var here, before the host runs, relocates it.
            //   Layer 2 — the controller's own staging file, via the injected UploadStagingDirectory.
            // Default location is a "staging" subdirectory beside the vaults; override with
            // Upload:StagingPath in appsettings.json.
            var uploadSettings = builder.Configuration.GetSection("Upload").Get<UploadSettings>();
            var stagingPath = ResolveStagingPath(uploadSettings?.StagingPath, vaultPath);
            Directory.CreateDirectory(stagingPath);

            // AspNetCoreTempDirectory caches this value on first read and throws if the directory is
            // absent, so set it (and create the dir) before any request is served.
            Environment.SetEnvironmentVariable("ASPNETCORE_TEMP", stagingPath);
            builder.Services.AddSingleton(new UploadStagingDirectory(stagingPath));

            return Task.CompletedTask;
        }

        /// <summary>
        /// Resolves the absolute upload-staging directory. An explicit <paramref name="configuredPath"/>
        /// (from <c>Upload:StagingPath</c>) wins; otherwise it defaults to a <c>staging</c> subdirectory
        /// under <paramref name="vaultPath"/> — on the data disk, never the system temp mount. Pure so
        /// the "never <c>/tmp</c>" invariant is unit-testable without standing up the host.
        /// </summary>
        public static string ResolveStagingPath(string? configuredPath, string vaultPath)
        {
            var path = string.IsNullOrWhiteSpace(configuredPath)
                ? Path.Combine(vaultPath, "staging")
                : configuredPath;
            return Path.GetFullPath(path);
        }

        private static async Task InitializeTrackVault(FileDatabase fileDatabase)
        {
            if (!fileDatabase.HasVault(VaultConstants.Tracks))
            {
                await fileDatabase.CreateVaultAsync(VaultConstants.Tracks, MediaVaultType.Audio);
            }
        }

        private static async Task InitializeImageVault(FileDatabase fileDatabase)
        {
            if (!fileDatabase.HasVault(VaultConstants.Images))
            {
                await fileDatabase.CreateVaultAsync(VaultConstants.Images, MediaVaultType.Image);
            }
        }

        // Ensure the track-waveforms vault exists. Holds the per-track high-resolution waveform datum
        // (every track — Mix, Session, Cut), keyed by the track's EntryKey.
        private static async Task InitializeTrackWaveformsVault(FileDatabase fileDatabase)
        {
            if (!fileDatabase.HasVault(VaultConstants.TrackWaveforms))
            {
                await fileDatabase.CreateVaultAsync(VaultConstants.TrackWaveforms, MediaVaultType.Media);
            }
        }
    }
}