using System.Runtime.InteropServices;
using System.ComponentModel;
using XYZ.PrintService.Models;

namespace XYZ.PrintService.Native;

public static class RawPrinterHelper
{
    [DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Auto)]
    static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault);

    [DllImport("winspool.drv", SetLastError = true)]
    static extern bool ClosePrinter(IntPtr hPrinter);

    [DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Auto)]
    static extern bool StartDocPrinter(IntPtr hPrinter, int level, ref DOCINFOA di);

    [DllImport("winspool.drv", SetLastError = true)]
    static extern bool EndDocPrinter(IntPtr hPrinter);

    [DllImport("winspool.drv", SetLastError = true)]
    static extern bool StartPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.drv", SetLastError = true)]
    static extern bool EndPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.drv", SetLastError = true)]
    static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, out int dwWritten);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    struct DOCINFOA
    {
        public string pDocName;
        public string pOutputFile;
        public string pDataType;
    }

    /// <summary>
    /// Throws a Win32Exception with the last Windows error code
    /// </summary>
    static void ThrowLastWin32Error(string operation)
    {
        var errorCode = Marshal.GetLastWin32Error();
        if (errorCode != 0)
        {
            throw new Win32Exception(errorCode, $"Windows API error during {operation}: {new Win32Exception(errorCode).Message}");
        }
    }

    public static PrintResult SendStringToPrinter(string printerName, string data)
    {
        IntPtr hPrinter = IntPtr.Zero;
        IntPtr bytes = IntPtr.Zero;

        try
        {
            // Open printer
            if (!OpenPrinter(printerName, out hPrinter, IntPtr.Zero))
            {
                ThrowLastWin32Error("OpenPrinter");
                throw new Exception($"Failed to open printer '{printerName}'. The printer may not exist, be offline, or be in use by another application.");
            }

            var doc = new DOCINFOA
            {
                pDocName = "XYZ POS Label Job",
                pDataType = "RAW"
            };

            // Start document
            if (!StartDocPrinter(hPrinter, 1, ref doc))
            {
                ThrowLastWin32Error("StartDocPrinter");
                throw new Exception($"Failed to start print job on printer '{printerName}'.");
            }

            try
            {
                // Start page
                if (!StartPagePrinter(hPrinter))
                {
                    ThrowLastWin32Error("StartPagePrinter");
                    throw new Exception($"Failed to start page on printer '{printerName}'.");
                }

                try
                {
                    // Marshal string to ANSI bytes
                    bytes = Marshal.StringToCoTaskMemAnsi(data);
                    if (bytes == IntPtr.Zero)
                    {
                        throw new Exception("Failed to allocate memory for ZPL data.");
                    }

                    // Write ZPL data
                    int dwWritten = 0;
                    if (!WritePrinter(hPrinter, bytes, data.Length, out dwWritten))
                    {
                        ThrowLastWin32Error("WritePrinter");
                        throw new Exception($"Failed to write data to printer '{printerName}'. Only {dwWritten} of {data.Length} bytes were written.");
                    }

                    if (dwWritten != data.Length)
                    {
                        throw new Exception($"Incomplete write to printer '{printerName}'. Expected {data.Length} bytes, but only {dwWritten} bytes were written.");
                    }

                    // Success - all data written, return result
                    var result = new PrintResult
                    {
                        Success = true,
                        PrinterName = printerName,
                        BytesWritten = dwWritten,
                        Message = $"Successfully sent {dwWritten} bytes to printer '{printerName}'"
                    };

                    // End page
                    if (!EndPagePrinter(hPrinter))
                    {
                        ThrowLastWin32Error("EndPagePrinter");
                        // Log warning but don't fail - document may still be valid
                        result.Message += " (warning: EndPagePrinter failed)";
                    }

                    // End document
                    if (!EndDocPrinter(hPrinter))
                    {
                        ThrowLastWin32Error("EndDocPrinter");
                        // Log warning but don't fail - data may have been sent
                        result.Message += " (warning: EndDocPrinter failed)";
                    }

                    return result;
                }
                finally
                {
                    // Cleanup handled in outer finally
                }
            }
            finally
            {
                // Cleanup handled in outer finally
            }
        }
        finally
        {
            // Free allocated memory
            if (bytes != IntPtr.Zero)
            {
                Marshal.FreeCoTaskMem(bytes);
            }

            // Close printer handle
            if (hPrinter != IntPtr.Zero)
            {
                if (!ClosePrinter(hPrinter))
                {
                    ThrowLastWin32Error("ClosePrinter");
                    // Log warning but don't throw - print job may have completed
                }
            }
        }
    }
}
