using PuppeteerSharp;
using PuppeteerSharp.Media;
using Microsoft.Extensions.Configuration;

namespace FrontOffice.Main.Utilities.Pdf;

public interface IChromiumPdfService
{
    Task<byte[]> GeneratePdfAsync(string html, string? fileName = null, CancellationToken ct = default);
}

public class ChromiumPdfService : IChromiumPdfService
{
    private static Task _browserPreparationTask = Task.CompletedTask;
    private static bool _browserReady;
    private readonly IConfiguration _config;
    private static string? _executablePath; // path to system Chrome/Chromium if available
    private static IBrowser? _browser; // reused headless browser instance
    private static readonly SemaphoreSlim BrowserLock = new(1, 1);

    public ChromiumPdfService(IConfiguration config)
    {
        _config = config;
        PrepareBrowserAsync();
    }

    private void PrepareBrowserAsync()
    {
        if (_browserReady) return;
        _browserPreparationTask = PrepareBrowserCoreAsync();
    }

    private Task PrepareBrowserCoreAsync()
    {
        try
        {
            // Config / env override
            _executablePath = _config["Pdf:ChromiumExecutablePath"] ?? Environment.GetEnvironmentVariable("CHROMIUM_EXECUTABLE_PATH");
            if (!string.IsNullOrWhiteSpace(_executablePath) && File.Exists(_executablePath))
            {
#if DEBUG
                Console.WriteLine($"[PDF] Using configured Chromium executable: {_executablePath}");
#endif
                _browserReady = true; return Task.CompletedTask;
            }

            // Common system locations
            var candidates = new[]{"/usr/bin/google-chrome","/usr/bin/google-chrome-stable","/usr/bin/chromium","/usr/bin/chromium-browser","/snap/bin/chromium"};
            _executablePath = candidates.FirstOrDefault(File.Exists);
            if (_executablePath != null)
            {
#if DEBUG
                Console.WriteLine($"[PDF] Found system Chrome/Chromium: {_executablePath}");
#endif
                _browserReady = true; return Task.CompletedTask;
            }

            // Fall back to PuppeteerSharp auto-download on first launch (no explicit path).
#if DEBUG
            Console.WriteLine("[PDF] No local Chromium path resolved. Will rely on PuppeteerSharp auto-download at first launch.");
#endif
            _browserReady = true; // allow launch to proceed; it will fetch if needed
        }
        catch (Exception ex)
        {
#if DEBUG
            Console.WriteLine($"[PDF] Unexpected preparation error: {ex.Message}");
#endif
        }
        return Task.CompletedTask;
    }

    private async Task EnsureBrowserAsync()
    {
        await _browserPreparationTask;
        if (!_browserReady)
        {
            throw new InvalidOperationException("Chromium not ready for launch.");
        }

        if (_browser != null && !_browser.IsClosed) return;

        await BrowserLock.WaitAsync();
        try
        {
            if (_browser != null && !_browser.IsClosed) return; // double-check after lock
            var launchOptions = new LaunchOptions
            {
                Headless = true,
                Args = new[] {"--no-sandbox","--disable-setuid-sandbox"},
                ExecutablePath = _executablePath // may be null; PuppeteerSharp will download if needed
            };
            _browser = await Puppeteer.LaunchAsync(launchOptions);
#if DEBUG
            Console.WriteLine("[PDF] Chromium browser instance launched.");
#endif
        }
        finally
        {
            BrowserLock.Release();
        }
    }

    public async Task<byte[]> GeneratePdfAsync(string html, string? fileName = null, CancellationToken ct = default)
    {
        if (string.IsNullOrWhiteSpace(html)) throw new ArgumentException("Html content is empty", nameof(html));

        // Ensure browser available (download may happen here if not done yet)
        await EnsureBrowserAsync();
        ct.ThrowIfCancellationRequested();

        // Inject default styles (RTL + font-face) if not present
        if (!html.Contains("<html", StringComparison.OrdinalIgnoreCase))
        {
            html = $"<html><head><meta charset=\"utf-8\"><style>{GetDefaultStyles()}</style></head><body>{html}</body></html>";
        }
        else if (!html.Contains("<style", StringComparison.OrdinalIgnoreCase))
        {
            html = html.Replace("<head>", "<head><style>" + GetDefaultStyles() + "</style>");
        }

        if (_browser == null) throw new InvalidOperationException("Browser not initialized");

        await using var page = await _browser.NewPageAsync();
        await page.SetContentAsync(html, new NavigationOptions { WaitUntil = new[] { WaitUntilNavigation.Networkidle0 } });

        var pdfOptions = new PdfOptions
        {
            Format = PaperFormat.A4,
            PrintBackground = true,
            DisplayHeaderFooter = false,
            MarginOptions = new MarginOptions { Top = "20mm", Bottom = "20mm", Left = "15mm", Right = "15mm" }
        };

        ct.ThrowIfCancellationRequested();
        var data = await page.PdfDataAsync(pdfOptions);
        return data;
    }

    private string GetDefaultStyles()
    {
        // Relative paths work when using SetContent; ensure fonts are served or embedded
        return "@font-face{font-family:'Vazir';src:url('fonts/Vazir-Regular.ttf') format('truetype');font-weight:400;}" +
               "@font-face{font-family:'Vazir';src:url('fonts/Vazir-Medium.ttf') format('truetype');font-weight:500;}" +
               "@font-face{font-family:'Vazir';src:url('fonts/Vazir-Bold.ttf') format('truetype');font-weight:700;}" +
               "body{font-family:'Vazir','Arial';direction:rtl;text-align:right;font-size:12px;line-height:1.6;margin:0;padding:25px;}";
    }
}
