using DocumentOperator.Application.Common.Configuration;
using DocumentOperator.Application.Common.Interfaces;
using DocumentOperator.Domain.Common.Exceptions;
using FluentValidation;
using MediatR;
using Microsoft.Extensions.Options;

namespace DocumentOperator.Application.ExtractZugferd;

/// <summary>
/// Command to extract ZUGFeRD XML from a PDF document
/// </summary>
public record ExtractZugferdCommand : IRequest<ZugferdExtractionResult>
{
    /// <summary>
    /// PDF document stream. Must be positioned at the beginning (Position = 0).
    /// </summary>
    public required Stream PdfStream { get; init; }
}

/// <summary>
/// Handler for ExtractZugferdCommand.
/// Extracts ZUGFeRD XML from PDF and returns XML content
/// </summary>
public class ExtractZugferdCommandHandler(
    IPdfProcessor pdfProcessor,
    IOptions<ZugferdSettings> settings) 
    : IRequestHandler<ExtractZugferdCommand, ZugferdExtractionResult>
{
    private readonly ZugferdSettings _settings = settings.Value;

    public async Task<ZugferdExtractionResult> Handle(ExtractZugferdCommand request, CancellationToken cancellationToken)
    {
        // Get all attachments
        var attachmentInfo = await pdfProcessor.CheckAttachmentsAsync(request.PdfStream);

        // Find ZUGFeRD XML file using configured names and patterns
        var zugferdAttachment = attachmentInfo.Attachments.FirstOrDefault(a =>
            _settings.ZugferdFileNames.Any(name => 
                a.FileName.Equals(name, StringComparison.OrdinalIgnoreCase)) ||
            _settings.ZugferdFileNamePatterns.Any(pattern => 
                a.FileName.Contains(pattern, StringComparison.OrdinalIgnoreCase)));

        if (zugferdAttachment == null)
        {
            throw new NotFoundException("ZUGFeRD XML not found in PDF attachments");
        }

        // Reset stream position for extraction
        request.PdfStream.Position = 0;

        // Extract all attachments as ZIP
        byte[] zipBytes = await pdfProcessor.ExtractAttachmentsAsync(request.PdfStream);

        // Find ZUGFeRD XML in ZIP
        using var zipStream = new MemoryStream(zipBytes);
        using var zipArchive = new System.IO.Compression.ZipArchive(zipStream, System.IO.Compression.ZipArchiveMode.Read);

        var zugferdEntry = zipArchive.Entries.FirstOrDefault(e =>
            e.Name.Equals(zugferdAttachment.FileName, StringComparison.OrdinalIgnoreCase));

        if (zugferdEntry == null)
        {
            throw new NotFoundException($"ZUGFeRD XML '{zugferdAttachment.FileName}' not found in extracted attachments");
        }

        // Read XML content
        using var entryStream = zugferdEntry.Open();
        using var reader = new StreamReader(entryStream);
        string xmlContent = await reader.ReadToEndAsync(cancellationToken);

        return new ZugferdExtractionResult
        {
            FileName = zugferdAttachment.FileName,
            XmlContent = xmlContent,
            FileSize = zugferdAttachment.SizeBytes
        };
    }
}

/// <summary>
/// Validator for ExtractZugferdCommand.
/// </summary>
public class ExtractZugferdCommandValidator : AbstractValidator<ExtractZugferdCommand>
{
    public ExtractZugferdCommandValidator()
    {
        RuleFor(x => x.PdfStream)
            .NotNull()
            .WithMessage("PDF stream is required");
    }
}

/// <summary>
/// Result DTO for ZUGFeRD extraction
/// </summary>
public record ZugferdExtractionResult
{
    /// <summary>
    /// ZUGFeRD XML file name
    /// </summary>
    public required string FileName { get; init; }

    /// <summary>
    /// ZUGFeRD XML content as string
    /// </summary>
    public required string XmlContent { get; init; }

    /// <summary>
    /// File size in bytes
    /// </summary>
    public long FileSize { get; init; }
}
