using DoliMiddlewareApi.Dtos.command;
using DoliMiddlewareApi.Dtos.Dolibarr;
using DoliMiddlewareApi.Exceptions;
using DoliMiddlewareApi.Services.Clients;
using System.Text.Json;

namespace DoliMiddlewareApi.Services;

public class DocumentService(IDolibarrApiClient apiClient)
{
    public async Task<(byte[] content, string filename)> BuildInvoicePdfAsync(string invoiceRef)
    {
        var request = new BuildDocumentRequest
        {
            modulepart = "invoice",
            original_file = $"{invoiceRef}/{invoiceRef}.pdf",
            doctemplate = "crabe",
            langcode = "es_ES"
        };

        var response = await apiClient.PutAsync("documents/builddoc", request);

        var result = JsonSerializer.Deserialize<DolibarrPdfResponse>(response);

        if (result == null || string.IsNullOrEmpty(result.content))
            throw new Exception("Dolibarr no devolvió PDF");

        var bytes = Convert.FromBase64String(result.content);

        return (bytes, result.filename);
    }

    public async Task<List<DocumentItem>> GetDocumentsAsync(string modulePart, int id)
    {
        try
        {
            var endpoint = $"documents?modulepart={modulePart}&id={id}";
            var responseString = await apiClient.GetAsyncRaw(endpoint);

            var response = JsonSerializer.Deserialize<List<Dictionary<string, JsonElement>>>(responseString);
            if (response == null) return [];

            return response.Select(d =>
            {
                // Dolibarr's ECM array_merge overwrites 'name' with null (inherited CommonObject property).
                // Fall back to 'filename' which the ECM record keeps correctly.
                var name = d.TryGetValue("name", out var nameProp) && nameProp.ValueKind == JsonValueKind.String
                    ? nameProp.GetString() : null;
                if (string.IsNullOrEmpty(name))
                    name = d.TryGetValue("filename", out var fnProp) && fnProp.ValueKind == JsonValueKind.String
                        ? fnProp.GetString() : null;
                var path = d.TryGetValue("path", out var pathProp) && pathProp.ValueKind == JsonValueKind.String
                    ? pathProp.GetString() : null;
                var size = d.TryGetValue("size", out var sizeProp) && sizeProp.ValueKind == JsonValueKind.Number
                    ? sizeProp.GetInt64() : 0;

                // level1name is the invoice ref directory (e.g. "IN2605-0001").
                // Dolibarr's download endpoint needs a relative path like "IN2605-0001/IN2605-0001.pdf",
                // not the absolute filesystem path stored in 'path'.
                var level1name = d.TryGetValue("level1name", out var l1Prop) && l1Prop.ValueKind == JsonValueKind.String
                    ? l1Prop.GetString() : null;
                var relativePath = (!string.IsNullOrEmpty(level1name) && !string.IsNullOrEmpty(name))
                    ? $"{level1name}/{name}"
                    : name;

                return new DocumentItem { Name = name, Path = path, Size = size, RelativePath = relativePath };
            }).ToList();
        }
        catch
        {
            // Module may not be active in Dolibarr — return empty list gracefully
            return [];
        }
    }

    public async Task<(byte[] content, string filename, string contentType)> DownloadDocumentAsync(string modulePart, string fileRef)
    {
        var endpoint = $"documents/download?modulepart={modulePart}&original_file={Uri.EscapeDataString(fileRef)}";
        var responseString = await apiClient.GetAsyncRaw(endpoint);

        var response = JsonSerializer.Deserialize<DolibarrPdfResponse>(responseString);

        if (response == null || string.IsNullOrEmpty(response.content))
            throw new NotFoundException("Documento no encontrado");

        var bytes = Convert.FromBase64String(response.content);
        var filename = response.filename ?? "document";
        var contentType = filename.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)
            ? "application/pdf"
            : "application/octet-stream";

        return (bytes, filename, contentType);
    }
}

public class DocumentItem
{
    public string? Name { get; set; }
    public string? Path { get; set; }
    public string? RelativePath { get; set; }
    public long Size { get; set; }
}