using System;
using System.Drawing.Printing;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using XYZ.PrintService.Models;

namespace XYZ.PrintService.Services;

[SupportedOSPlatform("windows")]
public static class PrinterDiscoveryService
{
    [SupportedOSPlatform("windows")]
    public static List<PrinterInfo> GetPrinters()
    {
        var list = new List<PrinterInfo>();

        foreach (string name in PrinterSettings.InstalledPrinters)
        {
            // Filter out obvious virtual/non-label printers by name
            var lower = name.ToLowerInvariant();
            if (lower.Contains("pdf") ||
                lower.Contains("xps") ||
                lower.Contains("onenote") ||
                lower.Contains("fax") ||
                lower.Contains("microsoft print") ||
                lower.Contains("image writer"))
            {
                continue;
            }

            var settings = new PrinterSettings { PrinterName = name };
            var port = GetPrinterPort(settings);

            // If we can't detect any port and the name doesn't look like a label/device printer, skip it
            if (string.IsNullOrWhiteSpace(port))
            {
                // Allow well-known label brands even if port is missing
                var brand = DetectBrand(name);
                if (brand == "Generic")
                {
                    continue;
                }
            }

            list.Add(new PrinterInfo
            {
                Id = name,
                Name = name,
                Brand = DetectBrand(name),
                Dpi = DetectDpi(settings),
                Port = port
            });
        }

        return list;
    }

    [SupportedOSPlatform("windows")]
    static string GetPrinterPort(PrinterSettings ps)
    {
        try
        {
            // Method 1: Try direct PortName property (available in .NET Framework)
            var portProperty = typeof(PrinterSettings).GetProperty("PortName", 
                System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
            if (portProperty != null)
            {
                var port = portProperty.GetValue(ps)?.ToString();
                if (!string.IsNullOrEmpty(port))
                    return port;
            }

            // Method 2: Try PrinterName parsing (some printers have port in name)
            // Example: "ZDesigner ZT231-203dpi ZPL on USB001"
            var printerName = ps.PrinterName;
            if (!string.IsNullOrEmpty(printerName))
            {
                // Check for common port patterns
                var portPatterns = new[] { "USB", "LPT", "COM", "TCP", "IP_" };
                foreach (var pattern in portPatterns)
                {
                    var index = printerName.IndexOf(pattern, StringComparison.OrdinalIgnoreCase);
                    if (index >= 0)
                    {
                        // Extract port (e.g., "USB001", "LPT1", "COM3")
                        var portPart = printerName.Substring(index);
                        var spaceIndex = portPart.IndexOf(' ');
                        if (spaceIndex > 0)
                            portPart = portPart.Substring(0, spaceIndex);
                        if (portPart.Length > 0)
                            return portPart;
                    }
                }
            }

            // Method 3: Try to query printer via WMI (Windows Management Instrumentation)
            // This is more reliable but requires additional dependencies
            // For now, return empty string if we can't determine
            return "";
        }
        catch (Exception ex)
        {
            // Log error but don't fail - port is optional
            Console.WriteLine($"Warning: Could not detect port for printer {ps.PrinterName}: {ex.Message}");
            return "";
        }
    }

    [SupportedOSPlatform("windows")]
    static int DetectDpi(PrinterSettings ps)
    {
        try
        {
            // Method 1: Try to get from PrinterResolution
            var res = ps.DefaultPageSettings.PrinterResolution;
            if (res != null && res.X > 0)
            {
                return res.X; // Use X resolution (should match Y for most printers)
            }

            // Method 2: Try to parse from printer name
            // Common patterns: "203dpi", "300dpi", "600dpi"
            var printerName = ps.PrinterName ?? "";
            var dpiMatch = Regex.Match(
                printerName, 
                @"(\d{3,4})\s*dpi", 
                RegexOptions.IgnoreCase
            );
            if (dpiMatch.Success && int.TryParse(dpiMatch.Groups[1].Value, out int parsedDpi))
            {
                // Validate DPI is reasonable (common values: 203, 300, 600)
                if (parsedDpi >= 150 && parsedDpi <= 1200)
                {
                    return parsedDpi;
                }
            }

            // Method 3: Try common DPI values based on printer brand
            var brand = DetectBrand(printerName);
            return brand switch
            {
                "Zebra" => 203, // Most Zebra printers are 203 DPI
                "Honeywell" => 203, // Most Honeywell printers are 203 DPI
                "TSC" => 203, // Most TSC printers are 203 DPI
                _ => 203 // Default to 203 DPI (most common for label printers)
            };
        }
        catch (Exception ex)
        {
            // Log error but default to 203 DPI
            Console.WriteLine($"Warning: Could not detect DPI for printer {ps.PrinterName}: {ex.Message}");
            return 203;
        }
    }

    static string DetectBrand(string name)
    {
        var n = name.ToLower();
        if (n.Contains("zebra")) return "Zebra";
        if (n.Contains("honeywell") || n.Contains("intermec")) return "Honeywell";
        if (n.Contains("tsc")) return "TSC";
        return "Generic";
    }
}
