using System.Diagnostics;
using System.Globalization;
using System.Text;

namespace JobTrackerApi.Services;

public sealed record CvPdfArtifact(string FileName, string StoragePath, byte[] Bytes);

public interface ICvPdfExporter
{
    Task<CvPdfArtifact> ExportAsync(string ownerUserId, TailoredCvRenderResult renderResult, CancellationToken cancellationToken);
}

public sealed class PlaywrightCvPdfExporter : ICvPdfExporter
{
    private static readonly string[] BrowserCandidates =
    {
        "chromium",
        "chromium-browser",
        "google-chrome",
        "google-chrome-stable",
        "/usr/bin/chromium",
        "/usr/bin/chromium-browser",
        "/usr/bin/google-chrome",
        "/usr/bin/google-chrome-stable"
    };

    private readonly AppPaths _paths;
    private readonly ILogger<PlaywrightCvPdfExporter> _logger;
    private readonly int _retentionDays;

    public PlaywrightCvPdfExporter(AppPaths paths, ILogger<PlaywrightCvPdfExporter> logger, IConfiguration configuration)
    {
        _paths = paths;
        _logger = logger;
        _retentionDays = Math.Clamp(configuration.GetValue("CvExports:RetainDays", 30), 1, 365);
    }

    public async Task<CvPdfArtifact> ExportAsync(string ownerUserId, TailoredCvRenderResult renderResult, CancellationToken cancellationToken)
    {
        var now = DateTimeOffset.UtcNow;
        PruneExpiredExports(DateOnly.FromDateTime(now.UtcDateTime).AddDays(-_retentionDays));
        var folder = Path.Combine(_paths.GetOwnerCvExportsRoot(ownerUserId), now.ToString("yyyyMMdd"));
        Directory.CreateDirectory(folder);

        var suggestedFileName = string.IsNullOrWhiteSpace(renderResult.SuggestedFileName)
            ? $"tailored-cv-{now:yyyyMMddHHmmss}.pdf"
            : Path.GetFileName(renderResult.SuggestedFileName);
        var fileName = string.IsNullOrWhiteSpace(suggestedFileName) ? $"tailored-cv-{now:yyyyMMddHHmmss}.pdf" : suggestedFileName;
        var storagePath = Path.Combine(folder, $"{Guid.NewGuid():N}.pdf");

        var tempRoot = Path.Combine(Path.GetTempPath(), "jobtracker-cv-pdf", Guid.NewGuid().ToString("n"));
        var htmlPath = Path.Combine(tempRoot, "document.html");

        Directory.CreateDirectory(tempRoot);

        try
        {
            await File.WriteAllTextAsync(htmlPath, renderResult.Html ?? string.Empty, Encoding.UTF8, cancellationToken);

            var browserPath = ResolveBrowserPath();
            if (string.IsNullOrWhiteSpace(browserPath))
            {
                throw new InvalidOperationException("CV PDF export is unavailable. Install Chromium/Google Chrome or set CV_PDF_BROWSER_PATH.");
            }

            var startInfo = new ProcessStartInfo();
            startInfo.FileName = browserPath;
            foreach (var argument in BuildArguments(storagePath, htmlPath))
            {
                startInfo.ArgumentList.Add(argument);
            }
            startInfo.RedirectStandardOutput = true;
            startInfo.RedirectStandardError = true;
            startInfo.UseShellExecute = false;
            startInfo.CreateNoWindow = true;

            using var process = new Process();
            process.StartInfo = startInfo;
            process.Start();
            try
            {
                await process.WaitForExitAsync(cancellationToken).WaitAsync(TimeSpan.FromSeconds(60), cancellationToken);
            }
            finally
            {
                if (!process.HasExited) process.Kill(entireProcessTree: true);
            }

            var stdout = await process.StandardOutput.ReadToEndAsync();
            var stderr = await process.StandardError.ReadToEndAsync();

            if (process.ExitCode != 0)
            {
                throw new InvalidOperationException($"CV PDF export failed via browser CLI. ExitCode={process.ExitCode}. Stdout={stdout}. Stderr={stderr}");
            }

            if (!File.Exists(storagePath))
            {
                throw new InvalidOperationException($"CV PDF export did not create the expected file at {storagePath}.");
            }

            var bytes = await File.ReadAllBytesAsync(storagePath, cancellationToken);
            return new CvPdfArtifact(fileName, storagePath, bytes);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to export CV PDF to {Path}", storagePath);
            throw;
        }
        finally
        {
            TryDeleteDirectory(tempRoot);
        }
    }

    private void PruneExpiredExports(DateOnly cutoff)
    {
        foreach (var directory in Directory.EnumerateDirectories(_paths.CvExportsRoot))
        {
            var name = Path.GetFileName(directory);
            if (DateOnly.TryParseExact(name, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var legacyDate))
            {
                TryDeleteExpired(directory, legacyDate, cutoff);
                continue;
            }

            foreach (var datedDirectory in Directory.EnumerateDirectories(directory))
            {
                var datedName = Path.GetFileName(datedDirectory);
                if (DateOnly.TryParseExact(datedName, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date))
                {
                    TryDeleteExpired(datedDirectory, date, cutoff);
                }
            }
        }
    }

    private void TryDeleteExpired(string directory, DateOnly date, DateOnly cutoff)
    {
        if (date >= cutoff) return;
        try
        {
            Directory.Delete(directory, recursive: true);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Could not prune expired CV export directory {Directory}", directory);
        }
    }

    private static IReadOnlyList<string> BuildArguments(string storagePath, string htmlPath)
    {
        return new[]
        {
            "--headless=new",
            "--disable-gpu",
            "--no-sandbox",
            "--disable-dev-shm-usage",
            "--allow-file-access-from-files",
            "--enable-local-file-accesses",
            $"--print-to-pdf={storagePath}",
            new Uri(htmlPath).AbsoluteUri
        };
    }

    private static string? ResolveBrowserPath()
    {
        var configured = Environment.GetEnvironmentVariable("CV_PDF_BROWSER_PATH");
        if (!string.IsNullOrWhiteSpace(configured) && File.Exists(configured))
        {
            return configured;
        }

        foreach (var candidate in BrowserCandidates)
        {
            if (Path.IsPathRooted(candidate))
            {
                if (File.Exists(candidate)) return candidate;
                continue;
            }

            var resolved = FindOnPath(candidate);
            if (!string.IsNullOrWhiteSpace(resolved)) return resolved;
        }

        return null;
    }

    private static string? FindOnPath(string fileName)
    {
        var path = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
        var parts = path.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        foreach (var dir in parts)
        {
            var fullPath = Path.Combine(dir, fileName);
            if (File.Exists(fullPath)) return fullPath;
        }

        return null;
    }

    private static void TryDeleteDirectory(string path)
    {
        try
        {
            if (Directory.Exists(path))
            {
                Directory.Delete(path, recursive: true);
            }
        }
        catch
        {
            // best effort temp cleanup
        }
    }

}
