using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

public class SoxVolumeFader
{
    /// <summary>
    /// Applies fade in and fade out volume effect to an audio file using SoX.
    /// </summary>
    /// <param name="inputFile">Full path to the input audio file.</param>
    /// <param name="soxExePath">Full path to Sox executable.</param>
    /// <param name="fadeIn">Fade in duration in seconds.</param>
    /// <param name="fadeOut">Fade out duration in seconds.</param>
    public static async Task ApplyFadeAsync(string inputFile, string soxExePath, float fadeIn, float fadeOut)
    {
        if (string.IsNullOrWhiteSpace(inputFile))
            throw new ArgumentException("Input file path is required.", nameof(inputFile));
        if (string.IsNullOrWhiteSpace(soxExePath))
            throw new ArgumentException("SoX executable path is required.", nameof(soxExePath));
        if (!File.Exists(inputFile))
            throw new FileNotFoundException("Input file not found.", inputFile);
        if (!File.Exists(soxExePath))
            throw new FileNotFoundException("SoX executable not found.", soxExePath);
        if (fadeIn < 0 || fadeOut < 0)
            throw new ArgumentOutOfRangeException(nameof(fadeIn), "Fade durations must be non-negative.");

        const double minFadeDuration = 0.01; // fades under 10ms considered zero
        const double minStayDuration = 0.001; // 1ms minimal stay duration to avoid overlap errors

        double durationSeconds = await GetAudioDurationAsync(inputFile, soxExePath);

        if (durationSeconds <= 0)
            throw new InvalidOperationException("Audio duration must be positive.");

        // Clean tiny fades (below threshold)
        fadeIn = (fadeIn < minFadeDuration) ? 0f : fadeIn;
        fadeOut = (fadeOut < minFadeDuration) ? 0f : fadeOut;

        double totalFade = fadeIn + fadeOut;

        if (totalFade > durationSeconds)
        {
            // Proportionally scale fades to fit duration exactly
            if (totalFade > 0)
            {
                fadeIn = (float)(fadeIn / totalFade * durationSeconds);
                fadeOut = (float)(fadeOut / totalFade * durationSeconds);
            }
            else
            {
                fadeIn = 0f;
                fadeOut = 0f;
            }
            totalFade = fadeIn + fadeOut;

            fadeIn = (fadeIn < minFadeDuration) ? 0f : fadeIn;
            fadeOut = (fadeOut < minFadeDuration) ? 0f : fadeOut;
        }

        double stayDuration = durationSeconds - totalFade;

        // Clamp stayDuration >= 0
        stayDuration = Math.Max(0, stayDuration);

        // Handle SoX overlap error workaround here:
        if (stayDuration < minStayDuration)
        {
            // stayDuration too small or zero
            // If both fades > 0, SoX throws overlap error. Fix:
            if (fadeIn > 0 && fadeOut > 0)
            {
                // Set smaller fade to zero to avoid overlap
                if (fadeIn > fadeOut)
                {
                    fadeOut = 0f;
                }
                else
                {
                    fadeIn = 0f;
                }
                stayDuration = 0; // stay is zero or near zero, so exactly 0 here
            }
            else
            {
                // Either fadeIn or fadeOut is zero already - safe to keep stay=0 exactly
                stayDuration = 0;
            }
        }

        // Final safety clamp to avoid negatives
        fadeIn = Math.Max(0, fadeIn);
        fadeOut = Math.Max(0, fadeOut);
        stayDuration = Math.Max(0, stayDuration);

        string inputDir = Path.GetDirectoryName(inputFile) ?? Directory.GetCurrentDirectory();

        string tempOutputFile = Path.Combine(
            inputDir,
            Path.GetFileNameWithoutExtension(inputFile) + "_fade_temp" + Path.GetExtension(inputFile));

        // Format arguments with two decimals precision
        string args = $"\"{inputFile}\" \"{tempOutputFile}\" fade t {fadeIn:F2} {stayDuration:F2} {fadeOut:F2}";

        var processInfo = new ProcessStartInfo
        {
            FileName = soxExePath,
            Arguments = args,
            CreateNoWindow = true,
            UseShellExecute = false,
            RedirectStandardError = true,
            RedirectStandardOutput = true,
        };

        using (var process = new Process { StartInfo = processInfo })
        {
            var tcs = new TaskCompletionSource<bool>();

            process.EnableRaisingEvents = true;
            process.Exited += (s, e) => tcs.TrySetResult(true);

            process.Start();

            string stdOut = await process.StandardOutput.ReadToEndAsync();
            string stdErr = await process.StandardError.ReadToEndAsync();

            await tcs.Task;

            if (process.ExitCode != 0)
            {
                try
                {
                    if (File.Exists(tempOutputFile)) File.Delete(tempOutputFile);
                }
                catch { /* ignore cleanup failures */ }

                throw new Exception($"SoX process failed with exit code {process.ExitCode}: {stdErr}");
            }
        }

        // Replace original file
        try
        {
            File.Delete(inputFile);
            File.Move(tempOutputFile, inputFile);
        }
        catch (Exception ex)
        {
            try
            {
                if (File.Exists(tempOutputFile)) File.Delete(tempOutputFile);
            }
            catch { /* ignore cleanup failures */ }

            throw new IOException("Failed to replace original audio file with faded version.", ex);
        }
    }

    /// <summary>
    /// Get audio duration using SoX.
    /// </summary>
    private static async Task<double> GetAudioDurationAsync(string inputFile, string soxExePath)
    {
        var processInfo = new ProcessStartInfo
        {
            FileName = soxExePath,
            Arguments = $"--i -D \"{inputFile}\"",
            CreateNoWindow = true,
            UseShellExecute = false,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
        };

        using (var process = new Process { StartInfo = processInfo })
        {
            var tcs = new TaskCompletionSource<bool>();
            process.EnableRaisingEvents = true;
            process.Exited += (s, e) => tcs.TrySetResult(true);

            process.Start();

            string output = await process.StandardOutput.ReadToEndAsync();
            string error = await process.StandardError.ReadToEndAsync();

            await tcs.Task;

            if (process.ExitCode != 0)
                throw new Exception($"SoX failed to get audio duration: {error}");

            if (double.TryParse(output.Trim(), out double duration))
                return duration;

            throw new Exception("Failed to parse audio duration from SoX output.");
        }
    }
}

remove the ability for the user to input a fade in and out and instead make it figure it out based off of the inputfile and apply a medium fade in and fade out no matter what the length of the input audio file is