import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.OptionalDouble;
import java.util.OptionalLong;

public class AudioStorageCalculatorTest
{
    static class TestCase
    {
        String name;
        int duration;
        double sampleRateKhz;
        int bitDepth;
        int channels;
        long expectedBytes;
        double expectedKb;

        TestCase(String name, int d, double sr, int bd, int c, long bytes, double kb)
        {
            this.name = name;
            this.duration = d;
            this.sampleRateKhz = sr;
            this.bitDepth = bd;
            this.channels = c;
            this.expectedBytes = bytes;
            this.expectedKb = kb;
        }
    }

    public static void main(String[] args)
    {
        TestCase[] testSuite = new TestCase[] {
            new TestCase("Standard Benchmark (Quite OK Audio)", 152, 44.1, 16, 2, 26812800L, 26184.375),
            new TestCase("Short Voice Note (Mono, Low Quality)", 10, 8.0, 8, 1, 80000L, 78.125),
            new TestCase("Studio Master Track (High-Res Stereo)", 300, 96.0, 24, 2, 172800000L, 168750.0),
            new TestCase("Podcast Clip (Stereo Broadcast Quality)", 60, 48.0, 16, 2, 11520000L, 11250.0),
            new TestCase("Single Sample Frame Edge Case", 1, 1.0, 8, 1, 1000L, 0.9765625)
        };

        int totalTests = testSuite.length;
        int passedTests = 0;

        InputStream originalSystemIn = System.in;
        PrintStream originalSystemOut = System.out;

        System.out.println("==================================================");
        System.out.println("   RUNNING AUTOMATED AUDIO STORAGE CALCULATOR TEST");
        System.out.println("==================================================");

        for (int i = 0; i < totalTests; i++)
        {
            TestCase tc = testSuite[i];
            boolean currentTestPassed = true;
            String mismatchDetails = "";

            String simulatedInput = tc.duration + "\n" + tc.sampleRateKhz + "\n" + tc.bitDepth + "\n" + tc.channels + "\n";
            
            ByteArrayOutputStream outputCapture = new ByteArrayOutputStream();
            PrintStream capturePrintStream = new PrintStream(outputCapture);

            long studentBytes = -1L;
            double studentKb = -1.0;

            try
            {
                System.setIn(new ByteArrayInputStream(simulatedInput.getBytes()));
                System.setOut(capturePrintStream);

                AudioStorageCalculator.main(new String[0]);
                
                capturePrintStream.flush();
                String programOutput = outputCapture.toString();

                OptionalLong studentBytesOpt = extractLong(programOutput, "Bytes:\\s*(\\d+)");
                OptionalDouble studentKbOpt = extractDouble(programOutput, "Size:\\s*([\\d.]+)");
                
                // --- 1. Validate Raw Bytes ---
                if (studentBytesOpt.isEmpty()) 
                {
                    currentTestPassed = false;
                    mismatchDetails += "     -> Raw Bytes: Could not parse value. Ensure your line matches 'Bytes: <number>' exactly.\n";
                } 
                else 
                {
                    studentBytes = studentBytesOpt.getAsLong();
                    if (studentBytes != tc.expectedBytes) 
                    {
                        currentTestPassed = false;
                        mismatchDetails += String.format("     -> Raw Bytes: Expected %d but found %d\n", tc.expectedBytes, studentBytes);
                    }
                }
                
                // --- 2. Validate Audio Size (KB) ---
                if (studentKbOpt.isEmpty()) 
                {
                    currentTestPassed = false;
                    mismatchDetails += "     -> Audio Size: Could not parse value. Ensure your line matches 'Size: <number>' exactly.\n";
                } 
                else 
                {
                    studentKb = studentKbOpt.getAsDouble();
                    if (Math.abs(studentKb - tc.expectedKb) > 0.001) 
                    {
                        currentTestPassed = false;
                        mismatchDetails += String.format("     -> Audio Size: Expected %.3f KB but found %.3f KB\n", tc.expectedKb, studentKb);
                    }
                }
            }
            catch (Exception e)
            {
                currentTestPassed = false;
                mismatchDetails += "     -> Runtime Error: Code crashed! (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")\n";
            }

            System.setOut(originalSystemOut);

            if (currentTestPassed)
            {
                System.out.println("[PASS] Test #" + (i + 1) + ": " + tc.name);
                passedTests++;
            }
            else
            {
                System.out.println("[FAIL] Test #" + (i + 1) + ": " + tc.name);
                System.out.println("  ----------------------------------------------");
                System.out.println("  [DEBUG DIAGNOSTICS]");
                System.out.println("  ----------------------------------------------");
                System.out.println("   Input Parameters Mocked:");
                System.out.println("     • Duration:     " + tc.duration + " seconds");
                System.out.println("     • Sample Rate:  " + tc.sampleRateKhz + " kHz");
                System.out.println("     • Bit Depth:    " + tc.bitDepth + "-bit");
                System.out.println("     • Channels:     " + tc.channels + " (" + (tc.channels == 2 ? "Stereo" : "Mono") + ")");
                System.out.println("\n   Output Discrepancies Found:");
                System.out.print(mismatchDetails);
                System.out.println("  ----------------------------------------------\n");
            }
        }

        System.setIn(originalSystemIn);
        System.setOut(originalSystemOut);

        System.out.println("==================================================");
        System.out.println("TESTING SUMMARY");
        System.out.println("==================================================");
        System.out.println("Total Tests Run: " + totalTests);
        System.out.println("Passed:          " + passedTests);
        System.out.println("Failed:          " + (totalTests - passedTests));
        System.out.println("--------------------------------------------------");
        
        if (passedTests == totalTests)
        {
            System.out.println("RESULT: ALL TESTS PASSED SUCCESSFULLY! 🎉");
        }
        else
        {
            System.out.println("RESULT: SOME TESTS FAILED. CHECK DETAILS ABOVE. ❌");
        }
        System.out.println("==================================================");
    }

    private static OptionalLong extractLong(String text, String regex)
    {
        Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(text);
        if (m.find())
        {
            try {
                return OptionalLong.of(Long.parseLong(m.group(1)));
            } catch (NumberFormatException e) {
                return OptionalLong.empty();
            }
        }
        return OptionalLong.empty();
    }
    
    private static OptionalDouble extractDouble(String text, String regex)
    {
        Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(text);
        if (m.find())
        {
            try {
                return OptionalDouble.of(Double.parseDouble(m.group(1)));
            } catch (NumberFormatException e) {
                return OptionalDouble.empty();
            }
        }
        return OptionalDouble.empty();
    }
}