using System.Text;
using System.Windows.Forms;

namespace DbaMessageRestorer;

internal static class Program
{
    private const int MaximumPrintedBlocks = 300;

    [STAThread]
    private static int Main(string[] args)
    {
        Console.InputEncoding = Encoding.UTF8;
        Console.OutputEncoding = Encoding.UTF8;
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        try
        {
            string? zipPath = args.Length switch
            {
                0 => SelectZipFile(),
                1 => Path.GetFullPath(args[0]),
                _ => throw new ArgumentException(
                    "사용법: DbaMessageRestorer.exe [archive.zip]")
            };

            if (zipPath is null) return 0;
            if (!File.Exists(zipPath))
                throw new FileNotFoundException("ZIP 파일을 찾을 수 없습니다.", zipPath);

            DbaZipMessageResult result = ZipDbaMessageReader.Read(zipPath);
            PrintResult(result);

            if (AskYesNo("복원한 메시지 바이트를 파일로 저장할까요? [y/N]: ", false))
                SaveRecoveredMessage(result);

            return 0;
        }
        catch (Exception exception)
        {
            Console.Error.WriteLine();
            Console.Error.WriteLine("처리 실패:");
            Console.Error.WriteLine(exception);
            return 1;
        }
    }

    private static string? SelectZipFile()
    {
        using var dialog = new OpenFileDialog
        {
            Title = "NPNCB DBA 메시지를 복원할 ZIP 파일을 선택하세요",
            Filter = "ZIP 파일 (*.zip)|*.zip|모든 파일 (*.*)|*.*",
            CheckFileExists = true,
            Multiselect = false,
            RestoreDirectory = true
        };
        return dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
    }

    private static void SaveRecoveredMessage(DbaZipMessageResult result)
    {
        bool text = result.DecodedMessage.IsValidUtf8;
        using var dialog = new SaveFileDialog
        {
            Title = "복원 메시지 저장",
            Filter = text
                ? "UTF-8 텍스트 (*.txt)|*.txt|바이너리 (*.bin)|*.bin|모든 파일 (*.*)|*.*"
                : "바이너리 (*.bin)|*.bin|모든 파일 (*.*)|*.*",
            DefaultExt = text ? "txt" : "bin",
            AddExtension = true,
            FileName = Path.GetFileNameWithoutExtension(result.ZipPath) +
                       (text ? ".dba-message.txt" : ".dba-message.bin"),
            InitialDirectory = Path.GetDirectoryName(result.ZipPath) ?? string.Empty,
            OverwritePrompt = true,
            RestoreDirectory = true
        };

        if (dialog.ShowDialog() != DialogResult.OK) return;
        File.WriteAllBytes(dialog.FileName, result.DecodedMessage.MessageBytes);
        Console.WriteLine($"저장 완료: {dialog.FileName}");
    }

    private static bool AskYesNo(string prompt, bool defaultValue)
    {
        Console.Write(prompt);
        string? answer = Console.ReadLine()?.Trim();
        if (string.IsNullOrEmpty(answer)) return defaultValue;
        if (answer.Equals("y", StringComparison.OrdinalIgnoreCase) ||
            answer.Equals("yes", StringComparison.OrdinalIgnoreCase) ||
            answer.Equals("예", StringComparison.OrdinalIgnoreCase)) return true;
        if (answer.Equals("n", StringComparison.OrdinalIgnoreCase) ||
            answer.Equals("no", StringComparison.OrdinalIgnoreCase) ||
            answer.Equals("아니오", StringComparison.OrdinalIgnoreCase)) return false;
        return defaultValue;
    }

    private static void PrintResult(DbaZipMessageResult result)
    {
        Console.WriteLine();
        Console.WriteLine("NPNCB DBA 메시지 복원 결과");
        Console.WriteLine($"ZIP: {result.ZipPath}");
        Console.WriteLine($"엔트리: {result.EntryName}");
        Console.WriteLine($"raw DEFLATE: {result.CompressedSize:N0} bytes");
        Console.WriteLine($"물리적 블록/NPNCB: {result.PhysicalBlockCount:N0}/{result.NpncbCount:N0}");
        Console.WriteLine($"연결 DBA: {result.DbaBitCount:N0} bits ({result.DecodedMessage.WholeDbaByteCount:N0} complete bytes)");
        Console.WriteLine($"0x00 종료자 발견: {(result.DecodedMessage.TerminatorFound ? "yes" : "no")}");

        if (result.DecodedMessage.IsValidUtf8)
        {
            Console.WriteLine($"복원 메시지: {result.DecodedMessage.Message}");
        }
        else
        {
            Console.WriteLine("복원 바이트는 유효한 UTF-8이 아닙니다.");
            Console.WriteLine($"오류: {result.DecodedMessage.Error}");
        }

        Console.WriteLine($"복원 bytes(hex): {Convert.ToHexString(result.DecodedMessage.MessageBytes)}");

        int count = Math.Min(result.NpncbBlocks.Count, MaximumPrintedBlocks);
        if (count == 0) return;

        Console.WriteLine();
        Console.WriteLine("No.  Block  Final  BlockStart  DBAStart  Bits");
        for (int i = 0; i < count; i++)
        {
            NpncbDbaReadLog block = result.NpncbBlocks[i];
            Console.WriteLine(
                $"{i,3}  {block.PhysicalBlockIndex,5}  " +
                $"{(block.IsFinal ? "yes" : "no"),5}  " +
                $"{block.BlockStartBit,10}  {block.DbaStartBit,8}  {block.DbaBitCount,4}");
        }

        if (count < result.NpncbBlocks.Count)
            Console.WriteLine($"... {result.NpncbBlocks.Count - count:N0} rows omitted");
    }
}
