using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
using UglyToad.PdfPig;
using UglyToad.PdfPig.Content;

namespace StellaOps.Feedser.Source.Common.Pdf;

/// <summary>
/// Extracts text from PDF advisories using UglyToad.PdfPig without requiring native dependencies.
/// </summary>
public sealed class PdfTextExtractor
{
    public async Task<PdfExtractionResult> ExtractTextAsync(Stream pdfStream, PdfExtractionOptions? options = null, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(pdfStream);
        options ??= PdfExtractionOptions.Default;

        using var buffer = new MemoryStream();
        await pdfStream.CopyToAsync(buffer, cancellationToken).ConfigureAwait(false);
        var rawBytes = buffer.ToArray();
        buffer.Position = 0;

        using var document = PdfDocument.Open(buffer, new ParsingOptions
        {
            ClipPaths = true,
            UseLenientParsing = true,
        });

        var builder = new StringBuilder();
        var pageCount = 0;

        var totalPages = document.NumberOfPages;
        for (var index = 1; index <= totalPages; index++)
        {
            cancellationToken.ThrowIfCancellationRequested();

            Page page;
            try
            {
                page = document.GetPage(index);
            }
            catch (InvalidOperationException ex) when (ex.Message.Contains("empty stack", StringComparison.OrdinalIgnoreCase))
            {
                continue;
            }

            pageCount++;
            if (options.MaxPages.HasValue && pageCount > options.MaxPages.Value)
            {
                break;
            }

            if (pageCount > 1 && options.PageSeparator is not null)
            {
                builder.Append(options.PageSeparator);
            }

            string text;
            try
            {
                if (options.PreserveLayout)
                {
                    text = page.Text;
                }
                else
                {
                    text = FlattenWords(page.GetWords());
                }
            }
            catch (InvalidOperationException ex) when (ex.Message.Contains("empty stack", StringComparison.OrdinalIgnoreCase))
            {
                try
                {
                    text = FlattenWords(page.GetWords());
                }
                catch
                {
                    try
                    {
                        text = FlattenLetters(page.Letters);
                    }
                    catch
                    {
                        continue;
                    }
                }
            }

            if (!string.IsNullOrWhiteSpace(text))
            {
                builder.AppendLine(text.Trim());
            }
        }

        if (builder.Length == 0)
        {
            var raw = Encoding.ASCII.GetString(rawBytes);
            var matches = Regex.Matches(raw, "\\(([^\\)]+)\\)", RegexOptions.CultureInvariant);
            foreach (Match match in matches)
            {
                var value = match.Groups[1].Value;
                if (!string.IsNullOrWhiteSpace(value))
                {
                    builder.AppendLine(value.Trim());
                }
            }

            if (builder.Length > 0 && matches.Count > 0)
            {
                pageCount = Math.Max(pageCount, matches.Count);
            }
        }
        else if (builder.Length > 0 && pageCount == 0)
        {
            pageCount = 1;
        }

        return new PdfExtractionResult(builder.ToString().Trim(), pageCount);
    }

    private static string FlattenWords(IEnumerable<Word> words)
    {
        var builder = new StringBuilder();
        var first = true;
        foreach (var word in words)
        {
            if (string.IsNullOrWhiteSpace(word.Text))
            {
                continue;
            }

            if (!first)
            {
                builder.Append(' ');
            }

            builder.Append(word.Text.Trim());
            first = false;
        }

        return builder.ToString();
    }

    private static string FlattenLetters(IEnumerable<Letter> letters)
    {
        var builder = new StringBuilder();
        foreach (var letter in letters)
        {
            if (letter.Value is null)
            {
                continue;
            }

            builder.Append(letter.Value);
        }

        return builder.ToString();
    }
}

public sealed record PdfExtractionResult(string Text, int PagesProcessed);

public sealed record PdfExtractionOptions
{
    public static PdfExtractionOptions Default { get; } = new();

    /// <summary>
    /// Maximum number of pages to read. Null reads the entire document.
    /// </summary>
    public int? MaxPages { get; init; }

    /// <summary>
    /// When true, uses PdfPig's native layout text. When false, collapses to a single line per page.
    /// </summary>
    public bool PreserveLayout { get; init; } = true;

    /// <summary>
    /// Separator inserted between pages. Null disables separators.
    /// </summary>
    public string? PageSeparator { get; init; } = "\n\n";
}
