using System.Collections.Concurrent;
using Documents.Interfaces;
using Documents.Models;
using Domain.Dtos;          // QuoteDto
using Domain.Dtos.Sales;    // DeliveryNoteDto
using Domain.Dtos.Stock;   // ExpeditionDto
using Transversal.Interfaces;

public class DocumentTemplateService : IDocumentTemplateService
{
    private readonly ITemplateRenderer _templateRenderer;
    private readonly IPdfGeneratorService _pdfGeneratorService;

    // Cache simple para no leer el logo del disco en cada render
    private static readonly ConcurrentDictionary<string, string> _imageCacheBase64 = new();

    public DocumentTemplateService(ITemplateRenderer templateRenderer, IPdfGeneratorService pdfGeneratorService)
    {
        _templateRenderer = templateRenderer;
        _pdfGeneratorService = pdfGeneratorService;
    }

    public async Task<byte[]> GenerateDocumentAsync(DocumentGenerationRequest request)
    {
        if (request is null) throw new ArgumentNullException(nameof(request));
        if (request.Model is null) throw new ArgumentNullException(nameof(request.Model));

        string? templatePath = null;

        try
        {
            // 1) Elegir plantilla por tipo de documento
            templatePath = ResolveTemplate(request.DocumentType);

            // 2) Inyectar logo (si el DTO lo soporta)
            var logoBase64 = GetImageBase64Cached(
                Path.Combine(Directory.GetCurrentDirectory(), "Resources", "logo.png"));
            InjectLogoIfSupported(request.Model, logoBase64);

            // 3) Render + PDF
            var html = await _templateRenderer.RenderAsync(templatePath, request.Model);
            return await _pdfGeneratorService.GeneratePdfFromHtmlAsync(html);
        }
        catch (Exception ex)
        {
            // Envolvemos con contexto para facilitar el diagnóstico
            var wrapped = new Exception(
                $"Document generation failed (DocumentType={request.DocumentType}, Template='{templatePath ?? "?"}', ModelType={request.Model.GetType().FullName}). See inner exception.",
                ex
            );
            wrapped.Data["DocumentType"] = request.DocumentType.ToString();
            if (!string.IsNullOrEmpty(templatePath)) wrapped.Data["TemplatePath"] = templatePath;
            wrapped.Data["ModelType"] = request.Model.GetType().FullName;
            throw wrapped;
        }
    }

    private static string ResolveTemplate(DocumentType type) => type switch
    {
        DocumentType.Quote => "Quotes/Template_v1.cshtml",
        DocumentType.DeliveryNote => "DeliveryNotes/Template_v1.cshtml",
        DocumentType.Expedition => "Expeditions/Template_v1.cshtml",
        _ => "Shared/Template_Generic.cshtml"
    };

    private static void InjectLogoIfSupported(object model, string base64)
    {
        // Inyección “segura”: si el modelo expone LogoBase64, lo seteamos.
        switch (model)
        {
            case QuoteDto q:
                q.LogoBase64 = base64;
                break;
            case ExpeditionDto e:
                e.LogoBase64 = base64;
                break;
            case DeliveryNoteDto d:
                d.LogoBase64 = base64;
                break;
            default:
                // Si no tiene LogoBase64, no hacemos nada.
                break;
        }
    }

    private static string GetImageBase64Cached(string imagePath)
    {
        if (_imageCacheBase64.TryGetValue(imagePath, out var cached))
            return cached;

        if (!File.Exists(imagePath))
        {
            _imageCacheBase64[imagePath] = "";
            return "";
        }

        var base64 = Convert.ToBase64String(File.ReadAllBytes(imagePath));
        _imageCacheBase64[imagePath] = base64;
        return base64;
    }
}
