namespace Fast.Shared.Logic;

/// <summary>
/// Converts Office documents (DOCX, XLSX, PPTX) to PDF using a Gotenberg service request.
/// </summary>
public static class PdfConverter
{
    private class GotenbergClientOptions
    {
        /// <summary>
        /// The name of the configuration section in appsettings.json.
        /// </summary>
        public const string SectionName = "GotenbergSharpClient";

        /// <summary>
        /// The default Gotenberg service URL.
        /// </summary>
        public string ServiceUrl { get; set; } = "http://cloud.implefast.com:3000";
    }

    /// <summary>
    /// Converts an Office file (DOCX, XLSX, PPTX) to a PDF file.
    /// </summary>
    public static async Task Convert(string officeFilePath, string pdfOutputPath)
    {
        if (!File.Exists(officeFilePath))
            throw new FileNotFoundException($"File not found: {officeFilePath}");

        var fileBytes = await File.ReadAllBytesAsync(officeFilePath);
        using var ms = new MemoryStream(fileBytes);
        ms.Position = 0;

        var inputFileName = Path.GetFileName(officeFilePath);
        await Convert(ms, inputFileName, pdfOutputPath);
    }

    /// <summary>
    /// Converts a DOCX stream to a PDF file.
    /// </summary>
    public static async Task ConvertDocx(MemoryStream docxStream, string pdfOutputPath)
    {
        var randomFileName = $"temp-{Util.String.GetNewGuid()}.docx";
        await Convert(docxStream, randomFileName, pdfOutputPath);
    }

    /// <summary>
    /// Converts a XLSX stream to a PDF file.
    /// </summary>
    public static async Task ConvertXlsx(MemoryStream xlsxStream, string pdfOutputPath)
    {
        var randomFileName = $"temp-{Util.String.GetNewGuid()}.xlsx";
        await Convert(xlsxStream, randomFileName, pdfOutputPath);
    }

    /// <summary>
    /// Converts an Office document (DOCX, XLSX, PPTX) MemoryStream to a PDF MemoryStream.
    /// </summary>
    public static async Task<MemoryStream> Convert(MemoryStream documentStream, string inputFileName)
    {
        var options = LoadConfiguration();
        var convertUrl = $"{options.ServiceUrl.TrimEnd('/')}/forms/libreoffice/convert";

        using var httpClient = new HttpClient();
        httpClient.Timeout = TimeSpan.FromSeconds(15);

        using var form = new MultipartFormDataContent();
        var fileBytes = documentStream.ToArray();

        var mimeType = Util.GetMimeType(inputFileName);

        var fileContent = new ByteArrayContent(fileBytes);
        fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(mimeType);

        form.Add(fileContent, "files", inputFileName);

        HttpResponseMessage response;
        try
        {
            response = await httpClient.PostAsync(convertUrl, form);
            response.EnsureSuccessStatusCode();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error converting file: {ex.Message}");
            throw;
        }

        var pdfBytes = await response.Content.ReadAsByteArrayAsync();
        var pdfStream = new MemoryStream(pdfBytes);
        pdfStream.Position = 0;

        Console.WriteLine($"Successfully converted: {inputFileName} to PDF");
        return pdfStream;
    }

    /// <summary>
    /// Converts an Office document (DOCX, XLSX, PPTX) MemoryStream to a PDF file.
    /// </summary>
    private static async Task Convert(MemoryStream documentStream, string inputFileName, string pdfOutputPath)
    {
        var outputDir = Path.GetDirectoryName(pdfOutputPath);
        if (!string.IsNullOrEmpty(outputDir) && !Directory.Exists(outputDir))
            Directory.CreateDirectory(outputDir);

        var pdfStream = await Convert(documentStream, inputFileName);
        var pdfBytes = pdfStream.ToArray();
        await File.WriteAllBytesAsync(pdfOutputPath, pdfBytes);

        Console.WriteLine($"Successfully converted: {inputFileName} → {pdfOutputPath}");
    }

    /// <summary>
    /// attempts to load config from 'appsettings.json'.
    /// if it can't, then it uses default values.
    /// </summary>
    private static GotenbergClientOptions LoadConfiguration()
    {
        const string appSettingsFileName = "appsettings.json";

        var basePath = Directory.GetCurrentDirectory();
        string[] possiblePaths =
        {
            Path.Combine(basePath, appSettingsFileName), // this path used when deployed
            Path.Combine(basePath, "..", "Fast.Shared", appSettingsFileName) // this path used when in development
        };

        var configPath = possiblePaths.FirstOrDefault(File.Exists);

        if (configPath == null)
        {
            Console.WriteLine($"Warning: {appSettingsFileName} not found.  Using default Gotenberg configuration.");
            // return a new instance with default values set in the class.
            return new GotenbergClientOptions();
        }

        var configuration = new ConfigurationBuilder()
            .AddJsonFile(configPath, optional: false, reloadOnChange: true)
            .Build();

        var options = new GotenbergClientOptions();
        var configSection = configuration.GetSection(GotenbergClientOptions.SectionName);

        if (!configSection.Exists())
        {
            Console.WriteLine(
                $"Warning: Configuration section '{GotenbergClientOptions.SectionName}' "
                + $"not found in {appSettingsFileName}.  Using default Gotenberg configuration."
            );
            // if the section doesn't exist, use default values.
            return options;
        }

        // if the section exists, bind its values.  this will overwrite
        // the defaults with any values specified in appsettings.json.
        configSection.Bind(options);

        return options;
    }
}
