using Apollinare.API.Services.Reports;
using Apollinare.Domain.Entities;
using Apollinare.Infrastructure.Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Apollinare.API.Controllers;

[ApiController]
[Route("api/reports")]
public class ReportsController : ControllerBase
{
    private readonly ReportGeneratorService _reportGenerator;
    private readonly AppollinareDbContext _context;

    public ReportsController(ReportGeneratorService reportGenerator, AppollinareDbContext context)
    {
        _reportGenerator = reportGenerator;
        _context = context;
    }

    /// <summary>
    /// Genera un PDF da un template con i dati forniti
    /// </summary>
    [HttpPost("generate")]
    public async Task<IActionResult> Generate([FromBody] GenerateReportRequest request)
    {
        try
        {
            var pdf = await _reportGenerator.GeneratePdfAsync(request.TemplateId, request.DataContext);
            return File(pdf, "application/pdf", "report.pdf");
        }
        catch (ArgumentException ex)
        {
            return NotFound(ex.Message);
        }
        catch (Exception ex)
        {
            return BadRequest($"Error generating report: {ex.Message}");
        }
    }

    /// <summary>
    /// Genera il PDF di un evento usando il template predefinito o specificato
    /// </summary>
    [HttpGet("evento/{eventoId}")]
    public async Task<IActionResult> GenerateEvento(int eventoId, [FromQuery] int? templateId = null)
    {
        try
        {
            var pdf = await _reportGenerator.GenerateEventoPdfAsync(eventoId, templateId);
            return File(pdf, "application/pdf", $"evento_{eventoId}.pdf");
        }
        catch (ArgumentException ex)
        {
            return NotFound(ex.Message);
        }
        catch (Exception ex)
        {
            return BadRequest($"Error generating report: {ex.Message}");
        }
    }

    /// <summary>
    /// Debug endpoint to test binding resolution
    /// </summary>
    [HttpPost("debug-binding")]
    public async Task<IActionResult> DebugBinding([FromBody] DebugBindingRequest request)
    {
        var dataContext = await BuildDataContextAsync(request.DataSources);

        var results = new Dictionary<string, object?>();
        results["dataContextKeys"] = dataContext.Keys.ToList();

        foreach (var kvp in dataContext)
        {
            var type = kvp.Value?.GetType();
            results[$"dataset_{kvp.Key}_type"] = type?.Name;

            // Try to get the property
            if (kvp.Value != null && !string.IsNullOrEmpty(request.PropertyName))
            {
                var prop = type?.GetProperty(request.PropertyName,
                    System.Reflection.BindingFlags.Public |
                    System.Reflection.BindingFlags.Instance |
                    System.Reflection.BindingFlags.IgnoreCase);

                results[$"dataset_{kvp.Key}_property_found"] = prop != null;
                if (prop != null)
                {
                    var value = prop.GetValue(kvp.Value);
                    results[$"dataset_{kvp.Key}_value"] = value?.ToString();
                    results[$"dataset_{kvp.Key}_value_type"] = value?.GetType().Name;
                }
            }
        }

        return Ok(results);
    }

    /// <summary>
    /// Genera un'anteprima del PDF con dati reali
    /// </summary>
    [HttpPost("preview")]
    public async Task<IActionResult> Preview([FromBody] PreviewReportRequest request)
    {
        try
        {
            var dataContext = await BuildDataContextAsync(request.DataSources);
            var pdf = await _reportGenerator.GeneratePdfAsync(request.TemplateId, dataContext);
            return File(pdf, "application/pdf");
        }
        catch (ArgumentException ex)
        {
            return NotFound(ex.Message);
        }
        catch (Exception ex)
        {
            return BadRequest($"Error generating preview: {ex.Message}");
        }
    }

    /// <summary>
    /// Ottiene la lista dei tipi di dataset disponibili (inclusi Virtual Dataset)
    /// </summary>
    [HttpGet("datasets")]
    public async Task<ActionResult<List<DatasetTypeDto>>> GetAvailableDatasets()
    {
        var datasets = new List<DatasetTypeDto>
        {
            // Dataset principali
            new() { Id = "evento", Name = "Evento", Description = "Dati evento completi con cliente, location, ospiti, costi e risorse", Icon = "event", Category = "Principale" },
            new() { Id = "cliente", Name = "Cliente", Description = "Anagrafica clienti completa", Icon = "people", Category = "Principale" },
            new() { Id = "location", Name = "Location", Description = "Sedi e location eventi", Icon = "place", Category = "Principale" },
            new() { Id = "articolo", Name = "Articolo", Description = "Catalogo articoli e materiali", Icon = "inventory", Category = "Principale" },
            new() { Id = "risorsa", Name = "Risorsa", Description = "Staff e personale", Icon = "person", Category = "Principale" },

            // Dataset di lookup/configurazione
            new() { Id = "tipoEvento", Name = "Tipo Evento", Description = "Tipologie di evento (matrimonio, compleanno, etc.)", Icon = "category", Category = "Configurazione" },
            new() { Id = "tipoOspite", Name = "Tipo Ospite", Description = "Tipologie di ospiti (adulti, bambini, etc.)", Icon = "groups", Category = "Configurazione" },
            new() { Id = "categoria", Name = "Categoria Articoli", Description = "Categorie articoli con coefficienti di calcolo", Icon = "folder", Category = "Configurazione" },
            new() { Id = "tipoRisorsa", Name = "Tipo Risorsa", Description = "Tipologie di risorse (cameriere, cuoco, etc.)", Icon = "badge", Category = "Configurazione" },
            new() { Id = "tipoMateriale", Name = "Tipo Materiale", Description = "Tipologie di materiali", Icon = "category", Category = "Configurazione" },

            // Dataset lista (per report con elenchi)
            new() { Id = "listaEventi", Name = "Lista Eventi", Description = "Elenco eventi per report multipli", Icon = "list", Category = "Liste" },
            new() { Id = "listaArticoli", Name = "Lista Articoli", Description = "Elenco articoli per catalogo", Icon = "list", Category = "Liste" },
        };

        // Aggiungi Virtual Dataset dal database
        var virtualDatasets = await _context.VirtualDatasets
            .Where(vd => vd.Attivo)
            .OrderBy(vd => vd.DisplayName)
            .Select(vd => new DatasetTypeDto
            {
                Id = $"virtual:{vd.Nome}",  // Prefisso per identificare i virtual dataset
                Name = vd.DisplayName,
                Description = vd.Descrizione ?? "Dataset virtuale personalizzato",
                Icon = vd.Icon,
                Category = vd.Categoria,
                IsVirtual = true
            })
            .ToListAsync();

        datasets.AddRange(virtualDatasets);

        return datasets;
    }

    /// <summary>
    /// Ottiene le categorie dei dataset (incluse quelle dei Virtual Dataset)
    /// </summary>
    [HttpGet("datasets/categories")]
    public async Task<ActionResult<List<string>>> GetDatasetCategories()
    {
        var baseCategories = new List<string> { "Principale", "Configurazione", "Liste" };

        // Aggiungi categorie dai Virtual Dataset
        var virtualCategories = await _context.VirtualDatasets
            .Where(vd => vd.Attivo)
            .Select(vd => vd.Categoria)
            .Distinct()
            .ToListAsync();

        foreach (var cat in virtualCategories)
        {
            if (!baseCategories.Contains(cat))
                baseCategories.Add(cat);
        }

        return baseCategories;
    }

    /// <summary>
    /// Ottiene lo schema dei dati per un dataset (inclusi Virtual Dataset)
    /// </summary>
    [HttpGet("schema/{datasetId}")]
    public async Task<ActionResult<DataSchemaDto>> GetSchema(string datasetId)
    {
        // Check se è un Virtual Dataset
        if (datasetId.StartsWith("virtual:"))
        {
            var virtualName = datasetId.Substring("virtual:".Length);
            var schema = await GetVirtualDatasetSchemaAsync(virtualName);
            if (schema == null)
                return NotFound($"Virtual Dataset '{virtualName}' not found");
            return schema;
        }

        var staticSchema = GetSchemaForDataset(datasetId);
        if (staticSchema == null)
            return NotFound($"Dataset '{datasetId}' not found");
        return staticSchema;
    }

    /// <summary>
    /// Ottiene la lista delle entità disponibili per un dataset (per la preview)
    /// </summary>
    [HttpGet("datasets/{datasetId}/entities")]
    public async Task<ActionResult<List<EntityListItemDto>>> GetEntitiesForDataset(
        string datasetId,
        [FromQuery] string? search = null,
        [FromQuery] int limit = 50,
        [FromQuery] int offset = 0)
    {
        // Virtual Dataset - restituisce le entità del dataset primario
        if (datasetId.StartsWith("virtual:"))
        {
            var virtualName = datasetId.Substring("virtual:".Length);
            return await GetVirtualDatasetEntities(virtualName, search, limit, offset);
        }

        var entities = datasetId.ToLower() switch
        {
            "evento" => await GetEventiEntities(search, limit, offset),
            "cliente" => await GetClientiEntities(search, limit, offset),
            "location" => await GetLocationEntities(search, limit, offset),
            "articolo" => await GetArticoliEntities(search, limit, offset),
            "risorsa" => await GetRisorseEntities(search, limit, offset),
            "tipoevento" => await GetTipiEventoEntities(search, limit, offset),
            "tipoospite" => await GetTipiOspiteEntities(search, limit, offset),
            "categoria" => await GetCategorieEntities(search, limit, offset),
            "tiporisorsa" => await GetTipiRisorsaEntities(search, limit, offset),
            "tipomateriale" => await GetTipiMaterialeEntities(search, limit, offset),
            _ => new List<EntityListItemDto>()
        };

        return entities;
    }

    /// <summary>
    /// Conta le entità disponibili per un dataset
    /// </summary>
    [HttpGet("datasets/{datasetId}/count")]
    public async Task<ActionResult<int>> GetEntityCount(string datasetId, [FromQuery] string? search = null)
    {
        var count = datasetId.ToLower() switch
        {
            "evento" => await CountEventi(search),
            "cliente" => await CountClienti(search),
            "location" => await CountLocation(search),
            "articolo" => await CountArticoli(search),
            "risorsa" => await CountRisorse(search),
            "tipoevento" => await _context.TipiEvento.CountAsync(t => t.Attivo),
            "tipoospite" => await _context.TipiOspite.CountAsync(t => t.Attivo),
            "categoria" => await _context.CodiciCategoria.CountAsync(c => c.Attivo),
            "tiporisorsa" => await _context.TipiRisorsa.CountAsync(t => t.Attivo),
            "tipomateriale" => await _context.TipiMateriale.CountAsync(t => t.Attivo),
            _ => 0
        };

        return count;
    }

    #region Entity Queries

    private async Task<List<EntityListItemDto>> GetEventiEntities(string? search, int limit, int offset)
    {
        var query = _context.Eventi
            .Include(e => e.Cliente)
            .Include(e => e.Location)
            .Include(e => e.TipoEvento)
            .AsQueryable();

        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(e =>
                (e.Codice != null && e.Codice.ToLower().Contains(search)) ||
                (e.Cliente != null && e.Cliente.RagioneSociale.ToLower().Contains(search)) ||
                (e.Location != null && e.Location.Nome.ToLower().Contains(search)));
        }

        return await query
            .OrderByDescending(e => e.DataEvento)
            .Skip(offset)
            .Take(limit)
            .Select(e => new EntityListItemDto
            {
                Id = e.Id,
                Label = $"{e.Codice ?? $"EVT-{e.Id}"} - {e.DataEvento:dd/MM/yyyy}",
                Description = $"{e.Cliente!.RagioneSociale ?? "N/D"} @ {e.Location!.Nome ?? "N/D"}",
                SecondaryInfo = e.TipoEvento != null ? e.TipoEvento.Descrizione : null,
                Status = e.Stato.ToString()
            })
            .ToListAsync();
    }

    private async Task<int> CountEventi(string? search)
    {
        var query = _context.Eventi.AsQueryable();
        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(e =>
                (e.Codice != null && e.Codice.ToLower().Contains(search)) ||
                (e.Cliente != null && e.Cliente.RagioneSociale.ToLower().Contains(search)));
        }
        return await query.CountAsync();
    }

    private async Task<List<EntityListItemDto>> GetClientiEntities(string? search, int limit, int offset)
    {
        var query = _context.Clienti.Where(c => c.Attivo);

        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(c =>
                c.RagioneSociale.ToLower().Contains(search) ||
                (c.Citta != null && c.Citta.ToLower().Contains(search)) ||
                (c.Email != null && c.Email.ToLower().Contains(search)));
        }

        return await query
            .OrderBy(c => c.RagioneSociale)
            .Skip(offset)
            .Take(limit)
            .Select(c => new EntityListItemDto
            {
                Id = c.Id,
                Label = c.RagioneSociale,
                Description = $"{c.Citta ?? "N/D"} - {c.Telefono ?? "N/D"}",
                SecondaryInfo = c.Email
            })
            .ToListAsync();
    }

    private async Task<int> CountClienti(string? search)
    {
        var query = _context.Clienti.Where(c => c.Attivo);
        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(c => c.RagioneSociale.ToLower().Contains(search));
        }
        return await query.CountAsync();
    }

    private async Task<List<EntityListItemDto>> GetLocationEntities(string? search, int limit, int offset)
    {
        var query = _context.Location.Where(l => l.Attivo);

        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(l =>
                l.Nome.ToLower().Contains(search) ||
                (l.Citta != null && l.Citta.ToLower().Contains(search)));
        }

        return await query
            .OrderBy(l => l.Nome)
            .Skip(offset)
            .Take(limit)
            .Select(l => new EntityListItemDto
            {
                Id = l.Id,
                Label = l.Nome,
                Description = $"{l.Citta ?? "N/D"} ({l.Provincia ?? "N/D"})",
                SecondaryInfo = l.DistanzaKm.HasValue ? $"{l.DistanzaKm} km" : null
            })
            .ToListAsync();
    }

    private async Task<int> CountLocation(string? search)
    {
        var query = _context.Location.Where(l => l.Attivo);
        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(l => l.Nome.ToLower().Contains(search));
        }
        return await query.CountAsync();
    }

    private async Task<List<EntityListItemDto>> GetArticoliEntities(string? search, int limit, int offset)
    {
        var query = _context.Articoli
            .Include(a => a.Categoria)
            .Where(a => a.Attivo);

        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(a =>
                a.Codice.ToLower().Contains(search) ||
                a.Descrizione.ToLower().Contains(search));
        }

        return await query
            .OrderBy(a => a.Descrizione)
            .Skip(offset)
            .Take(limit)
            .Select(a => new EntityListItemDto
            {
                Id = a.Id,
                Label = $"{a.Codice} - {a.Descrizione}",
                Description = $"Disponibile: {a.QtaDisponibile ?? 0}",
                SecondaryInfo = a.Categoria != null ? a.Categoria.Descrizione : null
            })
            .ToListAsync();
    }

    private async Task<int> CountArticoli(string? search)
    {
        var query = _context.Articoli.Where(a => a.Attivo);
        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(a =>
                a.Codice.ToLower().Contains(search) ||
                a.Descrizione.ToLower().Contains(search));
        }
        return await query.CountAsync();
    }

    private async Task<List<EntityListItemDto>> GetRisorseEntities(string? search, int limit, int offset)
    {
        var query = _context.Risorse
            .Include(r => r.TipoRisorsa)
            .Where(r => r.Attivo);

        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(r =>
                r.Nome.ToLower().Contains(search) ||
                (r.Cognome != null && r.Cognome.ToLower().Contains(search)));
        }

        return await query
            .OrderBy(r => r.Cognome).ThenBy(r => r.Nome)
            .Skip(offset)
            .Take(limit)
            .Select(r => new EntityListItemDto
            {
                Id = r.Id,
                Label = $"{r.Nome} {r.Cognome ?? ""}".Trim(),
                Description = r.Telefono ?? "",
                SecondaryInfo = r.TipoRisorsa != null ? r.TipoRisorsa.Descrizione : null
            })
            .ToListAsync();
    }

    private async Task<int> CountRisorse(string? search)
    {
        var query = _context.Risorse.Where(r => r.Attivo);
        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(r =>
                r.Nome.ToLower().Contains(search) ||
                (r.Cognome != null && r.Cognome.ToLower().Contains(search)));
        }
        return await query.CountAsync();
    }

    private async Task<List<EntityListItemDto>> GetTipiEventoEntities(string? search, int limit, int offset)
    {
        var query = _context.TipiEvento
            .Include(t => t.TipoPasto)
            .Where(t => t.Attivo);

        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(t =>
                t.Codice.ToLower().Contains(search) ||
                t.Descrizione.ToLower().Contains(search));
        }

        return await query
            .OrderBy(t => t.Descrizione)
            .Skip(offset)
            .Take(limit)
            .Select(t => new EntityListItemDto
            {
                Id = t.Id,
                Label = t.Descrizione,
                Description = $"Codice: {t.Codice}",
                SecondaryInfo = t.TipoPasto != null ? t.TipoPasto.Descrizione : null
            })
            .ToListAsync();
    }

    private async Task<List<EntityListItemDto>> GetTipiOspiteEntities(string? search, int limit, int offset)
    {
        var query = _context.TipiOspite.Where(t => t.Attivo);

        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(t =>
                t.Codice.ToLower().Contains(search) ||
                t.Descrizione.ToLower().Contains(search));
        }

        return await query
            .OrderBy(t => t.Descrizione)
            .Skip(offset)
            .Take(limit)
            .Select(t => new EntityListItemDto
            {
                Id = t.Id,
                Label = t.Descrizione,
                Description = $"Codice: {t.Codice}"
            })
            .ToListAsync();
    }

    private async Task<List<EntityListItemDto>> GetCategorieEntities(string? search, int limit, int offset)
    {
        var query = _context.CodiciCategoria.Where(c => c.Attivo);

        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(c =>
                c.Codice.ToLower().Contains(search) ||
                c.Descrizione.ToLower().Contains(search));
        }

        return await query
            .OrderBy(c => c.Descrizione)
            .Skip(offset)
            .Take(limit)
            .Select(c => new EntityListItemDto
            {
                Id = c.Id,
                Label = c.Descrizione,
                Description = $"Codice: {c.Codice}",
                SecondaryInfo = $"Coeff: A={c.CoeffA:F2}, B={c.CoeffB:F2}, S={c.CoeffS:F2}"
            })
            .ToListAsync();
    }

    private async Task<List<EntityListItemDto>> GetTipiRisorsaEntities(string? search, int limit, int offset)
    {
        var query = _context.TipiRisorsa.Where(t => t.Attivo);

        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(t =>
                t.Codice.ToLower().Contains(search) ||
                t.Descrizione.ToLower().Contains(search));
        }

        return await query
            .OrderBy(t => t.Descrizione)
            .Skip(offset)
            .Take(limit)
            .Select(t => new EntityListItemDto
            {
                Id = t.Id,
                Label = t.Descrizione,
                Description = $"Codice: {t.Codice}"
            })
            .ToListAsync();
    }

    private async Task<List<EntityListItemDto>> GetTipiMaterialeEntities(string? search, int limit, int offset)
    {
        var query = _context.TipiMateriale.Where(t => t.Attivo);

        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.ToLower();
            query = query.Where(t =>
                t.Codice.ToLower().Contains(search) ||
                t.Descrizione.ToLower().Contains(search));
        }

        return await query
            .OrderBy(t => t.Descrizione)
            .Skip(offset)
            .Take(limit)
            .Select(t => new EntityListItemDto
            {
                Id = t.Id,
                Label = t.Descrizione,
                Description = $"Codice: {t.Codice}"
            })
            .ToListAsync();
    }

    #endregion

    private async Task<Dictionary<string, object>> BuildDataContextAsync(List<DataSourceSelection> dataSources)
    {
        var context = new Dictionary<string, object>();

        foreach (var ds in dataSources)
        {
            object? data;

            // Check se è un Virtual Dataset
            if (ds.DatasetId.StartsWith("virtual:"))
            {
                var virtualName = ds.DatasetId.Substring("virtual:".Length);
                data = await LoadVirtualDatasetAsync(virtualName, ds.EntityId);
            }
            else
            {
                data = await LoadEntityDataAsync(ds.DatasetId, ds.EntityId);
            }

            if (data != null)
            {
                // Usa l'alias se fornito, altrimenti usa l'ID del dataset
                var key = ds.Alias ?? ds.DatasetId;
                // Per i virtual dataset, rimuovi il prefisso "virtual:" dalla chiave
                if (key.StartsWith("virtual:"))
                    key = key.Substring("virtual:".Length);
                context[key] = data;
            }
        }

        return context;
    }

    private async Task<object?> LoadEntityDataAsync(string datasetId, int entityId)
    {
        return datasetId.ToLower() switch
        {
            "evento" => await _context.Eventi
                .Include(e => e.Cliente)
                .Include(e => e.Location)
                .Include(e => e.TipoEvento).ThenInclude(t => t!.TipoPasto)
                .Include(e => e.DettagliOspiti).ThenInclude(d => d.TipoOspite)
                .Include(e => e.DettagliPrelievo).ThenInclude(d => d.Articolo).ThenInclude(a => a!.Categoria)
                .Include(e => e.DettagliRisorse).ThenInclude(d => d.Risorsa).ThenInclude(r => r!.TipoRisorsa)
                .Include(e => e.Acconti)
                .Include(e => e.AltriCosti)
                .Include(e => e.Degustazioni)
                .FirstOrDefaultAsync(e => e.Id == entityId),

            "cliente" => await _context.Clienti.FindAsync(entityId),
            "location" => await _context.Location.FindAsync(entityId),
            "articolo" => await _context.Articoli
                .Include(a => a.Categoria)
                .Include(a => a.TipoMateriale)
                .FirstOrDefaultAsync(a => a.Id == entityId),
            "risorsa" => await _context.Risorse
                .Include(r => r.TipoRisorsa)
                .FirstOrDefaultAsync(r => r.Id == entityId),
            "tipoevento" => await _context.TipiEvento
                .Include(t => t.TipoPasto)
                .FirstOrDefaultAsync(t => t.Id == entityId),
            "tipoospite" => await _context.TipiOspite.FindAsync(entityId),
            "categoria" => await _context.CodiciCategoria.FindAsync(entityId),
            "tiporisorsa" => await _context.TipiRisorsa.FindAsync(entityId),
            "tipomateriale" => await _context.TipiMateriale.FindAsync(entityId),
            _ => null
        };
    }

    #region Virtual Dataset Support

    /// <summary>
    /// Genera lo schema per un Virtual Dataset basato sulla sua configurazione
    /// </summary>
    private async Task<DataSchemaDto?> GetVirtualDatasetSchemaAsync(string virtualName)
    {
        var virtualDataset = await _context.VirtualDatasets
            .FirstOrDefaultAsync(vd => vd.Nome == virtualName && vd.Attivo);

        if (virtualDataset == null) return null;

        var config = System.Text.Json.JsonSerializer.Deserialize<VirtualDatasetConfiguration>(
            virtualDataset.ConfigurationJson,
            new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }
        );

        if (config == null) return null;

        var fields = new List<DataFieldDto>();

        // Se ci sono OutputFields definiti, usa quelli
        if (config.OutputFields.Any(f => f.Included))
        {
            foreach (var outputField in config.OutputFields.Where(f => f.Included).OrderBy(f => f.Order))
            {
                var source = config.Sources.FirstOrDefault(s => s.Id == outputField.SourceId);
                if (source == null) continue;

                // Ottieni lo schema del dataset sorgente per determinare il tipo
                var sourceSchema = GetSchemaForDataset(source.DatasetId);
                var sourceField = sourceSchema?.Fields.FirstOrDefault(f =>
                    f.Name.Equals(outputField.FieldName, StringComparison.OrdinalIgnoreCase));

                fields.Add(new DataFieldDto
                {
                    Name = outputField.Alias ?? $"{source.Alias}.{outputField.FieldName}",
                    Label = outputField.Label ?? sourceField?.Label ?? outputField.FieldName,
                    Type = sourceField?.Type ?? "string",
                    Group = outputField.Group ?? source.Alias
                });
            }
        }
        else
        {
            // Se non ci sono OutputFields, includi tutti i campi di tutte le sorgenti
            foreach (var source in config.Sources)
            {
                var sourceSchema = GetSchemaForDataset(source.DatasetId);
                if (sourceSchema == null) continue;

                foreach (var field in sourceSchema.Fields)
                {
                    fields.Add(new DataFieldDto
                    {
                        Name = $"{source.Alias}.{field.Name}",
                        Label = $"{source.Alias} - {field.Label}",
                        Type = field.Type,
                        Group = source.Alias
                    });
                }
            }
        }

        return new DataSchemaDto
        {
            EntityType = virtualDataset.DisplayName,
            DatasetId = $"virtual:{virtualDataset.Nome}",
            Fields = fields,
            ChildCollections = new List<DataCollectionDto>()
        };
    }

    /// <summary>
    /// Ottiene le entità disponibili per un Virtual Dataset (basato sul dataset primario)
    /// </summary>
    private async Task<List<EntityListItemDto>> GetVirtualDatasetEntities(string virtualName, string? search, int limit, int offset)
    {
        var virtualDataset = await _context.VirtualDatasets
            .FirstOrDefaultAsync(vd => vd.Nome == virtualName && vd.Attivo);

        if (virtualDataset == null) return new List<EntityListItemDto>();

        var config = System.Text.Json.JsonSerializer.Deserialize<VirtualDatasetConfiguration>(
            virtualDataset.ConfigurationJson,
            new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }
        );

        if (config == null) return new List<EntityListItemDto>();

        // Trova il dataset primario
        var primarySource = config.Sources.FirstOrDefault(s => s.IsPrimary)
            ?? config.Sources.FirstOrDefault();

        if (primarySource == null) return new List<EntityListItemDto>();

        // Restituisce le entità del dataset primario
        return primarySource.DatasetId.ToLower() switch
        {
            "evento" => await GetEventiEntities(search, limit, offset),
            "cliente" => await GetClientiEntities(search, limit, offset),
            "location" => await GetLocationEntities(search, limit, offset),
            "articolo" => await GetArticoliEntities(search, limit, offset),
            "risorsa" => await GetRisorseEntities(search, limit, offset),
            _ => new List<EntityListItemDto>()
        };
    }

    /// <summary>
    /// Carica i dati per un Virtual Dataset applicando JOIN e filtri
    /// </summary>
    private async Task<object?> LoadVirtualDatasetAsync(string virtualName, int primaryEntityId)
    {
        var virtualDataset = await _context.VirtualDatasets
            .FirstOrDefaultAsync(vd => vd.Nome == virtualName && vd.Attivo);

        if (virtualDataset == null) return null;

        var config = System.Text.Json.JsonSerializer.Deserialize<VirtualDatasetConfiguration>(
            virtualDataset.ConfigurationJson,
            new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }
        );

        if (config == null) return null;

        // Carica tutti i dataset sorgente
        var loadedData = new Dictionary<string, object?>();

        foreach (var source in config.Sources)
        {
            if (source.IsPrimary)
            {
                // Carica l'entità primaria
                loadedData[source.Id] = await LoadEntityDataAsync(source.DatasetId, primaryEntityId);
            }
        }

        // Applica le relazioni per caricare i dati correlati
        foreach (var relationship in config.Relationships)
        {
            var fromData = loadedData.GetValueOrDefault(relationship.FromSourceId);
            if (fromData == null) continue;

            var toSource = config.Sources.FirstOrDefault(s => s.Id == relationship.ToSourceId);
            if (toSource == null) continue;

            // Ottieni il valore della chiave esterna
            var fromKeyValue = GetPropertyValue(fromData, relationship.FromField);
            if (fromKeyValue == null) continue;

            // Carica l'entità correlata
            if (int.TryParse(fromKeyValue.ToString(), out int relatedId))
            {
                loadedData[relationship.ToSourceId] = await LoadEntityDataAsync(toSource.DatasetId, relatedId);
            }
        }

        // Costruisci l'oggetto risultante con tutti i dati
        var result = new Dictionary<string, object?>();

        foreach (var source in config.Sources)
        {
            var data = loadedData.GetValueOrDefault(source.Id);
            if (data != null)
            {
                result[source.Alias] = data;
            }
        }

        // Se ci sono OutputFields, costruisci un oggetto piatto
        if (config.OutputFields.Any(f => f.Included))
        {
            var flatResult = new Dictionary<string, object?>();

            foreach (var outputField in config.OutputFields.Where(f => f.Included))
            {
                var source = config.Sources.FirstOrDefault(s => s.Id == outputField.SourceId);
                if (source == null) continue;

                var sourceData = loadedData.GetValueOrDefault(outputField.SourceId);
                if (sourceData == null) continue;

                var value = GetPropertyValue(sourceData, outputField.FieldName);
                var fieldName = outputField.Alias ?? $"{source.Alias}_{outputField.FieldName}";
                flatResult[fieldName] = value;
            }

            return flatResult;
        }

        return result;
    }

    private static object? GetPropertyValue(object obj, string propertyName)
    {
        var type = obj.GetType();
        var prop = type.GetProperty(propertyName,
            System.Reflection.BindingFlags.Public |
            System.Reflection.BindingFlags.Instance |
            System.Reflection.BindingFlags.IgnoreCase);
        return prop?.GetValue(obj);
    }

    #endregion

    private static DataSchemaDto? GetSchemaForDataset(string datasetId)
    {
        return datasetId.ToLower() switch
        {
            "evento" => GetEventoSchema(),
            "cliente" => GetClienteSchema(),
            "location" => GetLocationSchema(),
            "articolo" => GetArticoloSchema(),
            "risorsa" => GetRisorsaSchema(),
            "tipoevento" => GetTipoEventoSchema(),
            "tipoospite" => GetTipoOspiteSchema(),
            "categoria" => GetCategoriaSchema(),
            "tiporisorsa" => GetTipoRisorsaSchema(),
            "tipomateriale" => GetTipoMaterialeSchema(),
            _ => null
        };
    }

    #region Schema Definitions

    private static DataSchemaDto GetEventoSchema() => new()
    {
        EntityType = "Evento",
        DatasetId = "evento",
        Fields = new List<DataFieldDto>
        {
            // Campi base
            new() { Name = "id", Type = "number", Label = "ID", Group = "Base" },
            new() { Name = "codice", Type = "string", Label = "Codice Evento", Group = "Base" },
            new() { Name = "dataEvento", Type = "date", Label = "Data Evento", Group = "Base" },
            new() { Name = "oraInizio", Type = "time", Label = "Ora Inizio", Group = "Base" },
            new() { Name = "oraFine", Type = "time", Label = "Ora Fine", Group = "Base" },
            new() { Name = "descrizione", Type = "string", Label = "Descrizione", Group = "Base" },
            new() { Name = "stato", Type = "number", Label = "Stato (0=Scheda, 10=Preventivo, 20=Confermato)", Group = "Base" },
            new() { Name = "confermato", Type = "boolean", Label = "Confermato", Group = "Base" },

            // Ospiti
            new() { Name = "numeroOspiti", Type = "number", Label = "Numero Ospiti Totale", Group = "Ospiti" },
            new() { Name = "numeroOspitiAdulti", Type = "number", Label = "Ospiti Adulti", Group = "Ospiti" },
            new() { Name = "numeroOspitiBambini", Type = "number", Label = "Ospiti Bambini", Group = "Ospiti" },
            new() { Name = "numeroOspitiSeduti", Type = "number", Label = "Ospiti Seduti", Group = "Ospiti" },
            new() { Name = "numeroOspitiBuffet", Type = "number", Label = "Ospiti Buffet", Group = "Ospiti" },

            // Economici
            new() { Name = "costoTotale", Type = "currency", Label = "Costo Totale", Group = "Economici" },
            new() { Name = "costoPersona", Type = "currency", Label = "Costo per Persona", Group = "Economici" },
            new() { Name = "totaleAcconti", Type = "currency", Label = "Totale Acconti", Group = "Economici" },
            new() { Name = "saldo", Type = "currency", Label = "Saldo da Pagare", Group = "Economici" },
            new() { Name = "dataScadenzaPreventivo", Type = "date", Label = "Scadenza Preventivo", Group = "Economici" },

            // Note
            new() { Name = "noteCliente", Type = "string", Label = "Note Cliente", Group = "Note" },
            new() { Name = "noteInterne", Type = "string", Label = "Note Interne", Group = "Note" },
            new() { Name = "noteCucina", Type = "string", Label = "Note Cucina", Group = "Note" },
            new() { Name = "noteAllestimento", Type = "string", Label = "Note Allestimento", Group = "Note" },

            // Cliente (relazione)
            new() { Name = "cliente.id", Type = "number", Label = "ID Cliente", Group = "Cliente" },
            new() { Name = "cliente.ragioneSociale", Type = "string", Label = "Ragione Sociale", Group = "Cliente" },
            new() { Name = "cliente.indirizzo", Type = "string", Label = "Indirizzo Cliente", Group = "Cliente" },
            new() { Name = "cliente.cap", Type = "string", Label = "CAP Cliente", Group = "Cliente" },
            new() { Name = "cliente.citta", Type = "string", Label = "Città Cliente", Group = "Cliente" },
            new() { Name = "cliente.provincia", Type = "string", Label = "Provincia Cliente", Group = "Cliente" },
            new() { Name = "cliente.telefono", Type = "string", Label = "Telefono Cliente", Group = "Cliente" },
            new() { Name = "cliente.email", Type = "string", Label = "Email Cliente", Group = "Cliente" },
            new() { Name = "cliente.pec", Type = "string", Label = "PEC Cliente", Group = "Cliente" },
            new() { Name = "cliente.codiceFiscale", Type = "string", Label = "Codice Fiscale", Group = "Cliente" },
            new() { Name = "cliente.partitaIva", Type = "string", Label = "Partita IVA", Group = "Cliente" },
            new() { Name = "cliente.codiceDestinatario", Type = "string", Label = "Codice SDI", Group = "Cliente" },

            // Location (relazione)
            new() { Name = "location.id", Type = "number", Label = "ID Location", Group = "Location" },
            new() { Name = "location.nome", Type = "string", Label = "Nome Location", Group = "Location" },
            new() { Name = "location.indirizzo", Type = "string", Label = "Indirizzo Location", Group = "Location" },
            new() { Name = "location.cap", Type = "string", Label = "CAP Location", Group = "Location" },
            new() { Name = "location.citta", Type = "string", Label = "Città Location", Group = "Location" },
            new() { Name = "location.provincia", Type = "string", Label = "Provincia Location", Group = "Location" },
            new() { Name = "location.telefono", Type = "string", Label = "Telefono Location", Group = "Location" },
            new() { Name = "location.email", Type = "string", Label = "Email Location", Group = "Location" },
            new() { Name = "location.referente", Type = "string", Label = "Referente Location", Group = "Location" },
            new() { Name = "location.distanzaKm", Type = "number", Label = "Distanza (km)", Group = "Location" },

            // Tipo Evento (relazione)
            new() { Name = "tipoEvento.codice", Type = "string", Label = "Codice Tipo Evento", Group = "Tipo Evento" },
            new() { Name = "tipoEvento.descrizione", Type = "string", Label = "Tipo Evento", Group = "Tipo Evento" },
            new() { Name = "tipoEvento.tipoPasto.descrizione", Type = "string", Label = "Tipo Pasto", Group = "Tipo Evento" },
        },
        ChildCollections = new List<DataCollectionDto>
        {
            new()
            {
                Name = "dettagliOspiti",
                Label = "Dettaglio Ospiti",
                Description = "Breakdown ospiti per tipologia",
                Fields = new List<DataFieldDto>
                {
                    new() { Name = "tipoOspite.codice", Type = "string", Label = "Codice Tipo" },
                    new() { Name = "tipoOspite.descrizione", Type = "string", Label = "Tipo Ospite" },
                    new() { Name = "numero", Type = "number", Label = "Numero" },
                    new() { Name = "costoUnitario", Type = "currency", Label = "Costo Unitario" },
                    new() { Name = "sconto", Type = "percent", Label = "Sconto %" },
                    new() { Name = "totale", Type = "currency", Label = "Totale" },
                    new() { Name = "note", Type = "string", Label = "Note" }
                }
            },
            new()
            {
                Name = "altriCosti",
                Label = "Altri Costi",
                Description = "Costi aggiuntivi dell'evento",
                Fields = new List<DataFieldDto>
                {
                    new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
                    new() { Name = "costoUnitario", Type = "currency", Label = "Costo Unitario" },
                    new() { Name = "quantita", Type = "number", Label = "Quantità" },
                    new() { Name = "aliquotaIva", Type = "percent", Label = "Aliquota IVA" },
                    new() { Name = "totale", Type = "currency", Label = "Totale" }
                }
            },
            new()
            {
                Name = "dettagliRisorse",
                Label = "Risorse Assegnate",
                Description = "Personale assegnato all'evento",
                Fields = new List<DataFieldDto>
                {
                    new() { Name = "risorsa.nome", Type = "string", Label = "Nome" },
                    new() { Name = "risorsa.cognome", Type = "string", Label = "Cognome" },
                    new() { Name = "risorsa.telefono", Type = "string", Label = "Telefono" },
                    new() { Name = "risorsa.tipoRisorsa.descrizione", Type = "string", Label = "Ruolo" },
                    new() { Name = "oraInizio", Type = "time", Label = "Ora Inizio" },
                    new() { Name = "oraFine", Type = "time", Label = "Ora Fine" },
                    new() { Name = "oreLavoro", Type = "number", Label = "Ore Lavoro" },
                    new() { Name = "costo", Type = "currency", Label = "Costo" },
                    new() { Name = "note", Type = "string", Label = "Note" }
                }
            },
            new()
            {
                Name = "acconti",
                Label = "Acconti",
                Description = "Pagamenti anticipati ricevuti",
                Fields = new List<DataFieldDto>
                {
                    new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
                    new() { Name = "importo", Type = "currency", Label = "Importo" },
                    new() { Name = "dataPagamento", Type = "date", Label = "Data Pagamento" },
                    new() { Name = "metodoPagamento", Type = "string", Label = "Metodo Pagamento" },
                    new() { Name = "note", Type = "string", Label = "Note" }
                }
            },
            new()
            {
                Name = "dettagliPrelievo",
                Label = "Lista Prelievo",
                Description = "Articoli necessari per l'evento",
                Fields = new List<DataFieldDto>
                {
                    new() { Name = "articolo.codice", Type = "string", Label = "Codice Articolo" },
                    new() { Name = "articolo.descrizione", Type = "string", Label = "Descrizione" },
                    new() { Name = "articolo.categoria.descrizione", Type = "string", Label = "Categoria" },
                    new() { Name = "articolo.unitaMisura", Type = "string", Label = "U.M." },
                    new() { Name = "qtaRichiesta", Type = "number", Label = "Qtà Richiesta" },
                    new() { Name = "qtaCalcolata", Type = "number", Label = "Qtà Calcolata" },
                    new() { Name = "qtaEffettiva", Type = "number", Label = "Qtà Effettiva" },
                    new() { Name = "note", Type = "string", Label = "Note" }
                }
            },
            new()
            {
                Name = "degustazioni",
                Label = "Degustazioni",
                Description = "Degustazioni programmate",
                Fields = new List<DataFieldDto>
                {
                    new() { Name = "data", Type = "date", Label = "Data" },
                    new() { Name = "ora", Type = "time", Label = "Ora" },
                    new() { Name = "numeroPartecipanti", Type = "number", Label = "Partecipanti" },
                    new() { Name = "note", Type = "string", Label = "Note" }
                }
            }
        }
    };

    private static DataSchemaDto GetClienteSchema() => new()
    {
        EntityType = "Cliente",
        DatasetId = "cliente",
        Fields = new List<DataFieldDto>
        {
            new() { Name = "id", Type = "number", Label = "ID", Group = "Base" },
            new() { Name = "ragioneSociale", Type = "string", Label = "Ragione Sociale", Group = "Base" },
            new() { Name = "indirizzo", Type = "string", Label = "Indirizzo", Group = "Indirizzo" },
            new() { Name = "cap", Type = "string", Label = "CAP", Group = "Indirizzo" },
            new() { Name = "citta", Type = "string", Label = "Città", Group = "Indirizzo" },
            new() { Name = "provincia", Type = "string", Label = "Provincia", Group = "Indirizzo" },
            new() { Name = "telefono", Type = "string", Label = "Telefono", Group = "Contatti" },
            new() { Name = "email", Type = "string", Label = "Email", Group = "Contatti" },
            new() { Name = "pec", Type = "string", Label = "PEC", Group = "Contatti" },
            new() { Name = "codiceFiscale", Type = "string", Label = "Codice Fiscale", Group = "Fiscale" },
            new() { Name = "partitaIva", Type = "string", Label = "Partita IVA", Group = "Fiscale" },
            new() { Name = "codiceDestinatario", Type = "string", Label = "Codice Destinatario SDI", Group = "Fiscale" },
            new() { Name = "note", Type = "string", Label = "Note", Group = "Base" },
        },
        ChildCollections = new List<DataCollectionDto>()
    };

    private static DataSchemaDto GetLocationSchema() => new()
    {
        EntityType = "Location",
        DatasetId = "location",
        Fields = new List<DataFieldDto>
        {
            new() { Name = "id", Type = "number", Label = "ID", Group = "Base" },
            new() { Name = "nome", Type = "string", Label = "Nome", Group = "Base" },
            new() { Name = "indirizzo", Type = "string", Label = "Indirizzo", Group = "Indirizzo" },
            new() { Name = "cap", Type = "string", Label = "CAP", Group = "Indirizzo" },
            new() { Name = "citta", Type = "string", Label = "Città", Group = "Indirizzo" },
            new() { Name = "provincia", Type = "string", Label = "Provincia", Group = "Indirizzo" },
            new() { Name = "telefono", Type = "string", Label = "Telefono", Group = "Contatti" },
            new() { Name = "email", Type = "string", Label = "Email", Group = "Contatti" },
            new() { Name = "referente", Type = "string", Label = "Referente", Group = "Contatti" },
            new() { Name = "distanzaKm", Type = "number", Label = "Distanza (km)", Group = "Base" },
            new() { Name = "note", Type = "string", Label = "Note", Group = "Base" },
        },
        ChildCollections = new List<DataCollectionDto>()
    };

    private static DataSchemaDto GetArticoloSchema() => new()
    {
        EntityType = "Articolo",
        DatasetId = "articolo",
        Fields = new List<DataFieldDto>
        {
            new() { Name = "id", Type = "number", Label = "ID", Group = "Base" },
            new() { Name = "codice", Type = "string", Label = "Codice", Group = "Base" },
            new() { Name = "descrizione", Type = "string", Label = "Descrizione", Group = "Base" },
            new() { Name = "unitaMisura", Type = "string", Label = "Unità di Misura", Group = "Base" },
            new() { Name = "qtaDisponibile", Type = "number", Label = "Quantità Disponibile", Group = "Quantità" },
            new() { Name = "qtaStdA", Type = "number", Label = "Qtà Standard Adulti", Group = "Quantità" },
            new() { Name = "qtaStdB", Type = "number", Label = "Qtà Standard Buffet", Group = "Quantità" },
            new() { Name = "qtaStdS", Type = "number", Label = "Qtà Standard Seduti", Group = "Quantità" },
            new() { Name = "categoria.codice", Type = "string", Label = "Codice Categoria", Group = "Categoria" },
            new() { Name = "categoria.descrizione", Type = "string", Label = "Categoria", Group = "Categoria" },
            new() { Name = "categoria.coeffA", Type = "number", Label = "Coefficiente Adulti", Group = "Categoria" },
            new() { Name = "categoria.coeffB", Type = "number", Label = "Coefficiente Buffet", Group = "Categoria" },
            new() { Name = "categoria.coeffS", Type = "number", Label = "Coefficiente Seduti", Group = "Categoria" },
            new() { Name = "tipoMateriale.codice", Type = "string", Label = "Codice Tipo Materiale", Group = "Materiale" },
            new() { Name = "tipoMateriale.descrizione", Type = "string", Label = "Tipo Materiale", Group = "Materiale" },
            new() { Name = "note", Type = "string", Label = "Note", Group = "Base" },
        },
        ChildCollections = new List<DataCollectionDto>()
    };

    private static DataSchemaDto GetRisorsaSchema() => new()
    {
        EntityType = "Risorsa",
        DatasetId = "risorsa",
        Fields = new List<DataFieldDto>
        {
            new() { Name = "id", Type = "number", Label = "ID", Group = "Base" },
            new() { Name = "nome", Type = "string", Label = "Nome", Group = "Base" },
            new() { Name = "cognome", Type = "string", Label = "Cognome", Group = "Base" },
            new() { Name = "telefono", Type = "string", Label = "Telefono", Group = "Contatti" },
            new() { Name = "email", Type = "string", Label = "Email", Group = "Contatti" },
            new() { Name = "tipoRisorsa.codice", Type = "string", Label = "Codice Tipo", Group = "Tipo" },
            new() { Name = "tipoRisorsa.descrizione", Type = "string", Label = "Tipo Risorsa", Group = "Tipo" },
            new() { Name = "note", Type = "string", Label = "Note", Group = "Base" },
        },
        ChildCollections = new List<DataCollectionDto>()
    };

    private static DataSchemaDto GetTipoEventoSchema() => new()
    {
        EntityType = "Tipo Evento",
        DatasetId = "tipoEvento",
        Fields = new List<DataFieldDto>
        {
            new() { Name = "id", Type = "number", Label = "ID" },
            new() { Name = "codice", Type = "string", Label = "Codice" },
            new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
            new() { Name = "tipoPasto.codice", Type = "string", Label = "Codice Tipo Pasto" },
            new() { Name = "tipoPasto.descrizione", Type = "string", Label = "Tipo Pasto" },
        },
        ChildCollections = new List<DataCollectionDto>()
    };

    private static DataSchemaDto GetTipoOspiteSchema() => new()
    {
        EntityType = "Tipo Ospite",
        DatasetId = "tipoOspite",
        Fields = new List<DataFieldDto>
        {
            new() { Name = "id", Type = "number", Label = "ID" },
            new() { Name = "codice", Type = "string", Label = "Codice" },
            new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
        },
        ChildCollections = new List<DataCollectionDto>()
    };

    private static DataSchemaDto GetCategoriaSchema() => new()
    {
        EntityType = "Categoria Articoli",
        DatasetId = "categoria",
        Fields = new List<DataFieldDto>
        {
            new() { Name = "id", Type = "number", Label = "ID" },
            new() { Name = "codice", Type = "string", Label = "Codice" },
            new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
            new() { Name = "coeffA", Type = "number", Label = "Coefficiente Adulti" },
            new() { Name = "coeffB", Type = "number", Label = "Coefficiente Buffet" },
            new() { Name = "coeffS", Type = "number", Label = "Coefficiente Seduti" },
        },
        ChildCollections = new List<DataCollectionDto>()
    };

    private static DataSchemaDto GetTipoRisorsaSchema() => new()
    {
        EntityType = "Tipo Risorsa",
        DatasetId = "tipoRisorsa",
        Fields = new List<DataFieldDto>
        {
            new() { Name = "id", Type = "number", Label = "ID" },
            new() { Name = "codice", Type = "string", Label = "Codice" },
            new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
        },
        ChildCollections = new List<DataCollectionDto>()
    };

    private static DataSchemaDto GetTipoMaterialeSchema() => new()
    {
        EntityType = "Tipo Materiale",
        DatasetId = "tipoMateriale",
        Fields = new List<DataFieldDto>
        {
            new() { Name = "id", Type = "number", Label = "ID" },
            new() { Name = "codice", Type = "string", Label = "Codice" },
            new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
        },
        ChildCollections = new List<DataCollectionDto>()
    };

    #endregion
}

// DTOs
public class DebugBindingRequest
{
    public List<DataSourceSelection> DataSources { get; set; } = new();
    public string? PropertyName { get; set; }
}

public class PreviewReportRequest
{
    public int TemplateId { get; set; }
    public List<DataSourceSelection> DataSources { get; set; } = new();
}

public class DataSourceSelection
{
    public string DatasetId { get; set; } = string.Empty;
    public int EntityId { get; set; }
    public string? Alias { get; set; }
}

public class DatasetTypeDto
{
    public string Id { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public string Icon { get; set; } = string.Empty;
    public string Category { get; set; } = "Principale";
    public bool IsVirtual { get; set; } = false;
}

public class EntityListItemDto
{
    public int Id { get; set; }
    public string Label { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public string? SecondaryInfo { get; set; }
    public string? Status { get; set; }
}

public class DataSchemaDto
{
    public string EntityType { get; set; } = string.Empty;
    public string DatasetId { get; set; } = string.Empty;
    public List<DataFieldDto> Fields { get; set; } = new();
    public List<DataCollectionDto> ChildCollections { get; set; } = new();
}

public class DataFieldDto
{
    public string Name { get; set; } = string.Empty;
    public string Type { get; set; } = "string";
    public string Label { get; set; } = string.Empty;
    public string? Group { get; set; }
}

public class DataCollectionDto
{
    public string Name { get; set; } = string.Empty;
    public string Label { get; set; } = string.Empty;
    public string? Description { get; set; }
    public List<DataFieldDto> Fields { get; set; } = new();
}
