@page "/auswertung"
@page "/auswertung/{*WorkspaceId}"
@using ZahlenAnalyse.Web.Models
@using ZahlenAnalyse.Web.Services
@inject WorkspaceService DbService
@inject IJSRuntime JSRuntime

<MudContainer MaxWidth="MaxWidth.Large" Class="mt-8 mb-8">
    <MudText Typo="Typo.h4" Class="mb-6">Daten-Auswertung</MudText>

    @if (_isLoading)
    {
        <MudProgressCircular Color="Color.Primary" Indeterminate="true" />
    }
    else
    {
        <MudPaper Class="pa-6 mb-6" Elevation="1">
            <MudSelect T="Workspace" 
                       Label="Workspace auswählen" 
                       Variant="Variant.Outlined" 
                       Value="_selectedWorkspace" 
                       ValueChanged="OnWorkspaceSelected"
                       ToStringFunc="@(w => w?.Name)">
                @foreach (var ws in _workspaces)
                {
                    <MudSelectItem Value="ws">@ws.Name</MudSelectItem>
                }
            </MudSelect>
        </MudPaper>

        @if (_selectedWorkspace != null)
        {
            <MudGrid>
                <MudItem xs="12">
                    <MudDataGrid Items="@_fakten" 
                                 Groupable="true" 
                                 Filterable="true" 
                                 Hideable="true"
                                 Elevation="1">
                        <Columns>
                            <PropertyColumn Property="x => x.Date" Title="Datum" Format="dd.MM.yyyy" />
                            
                            @foreach (var dim in _selectedWorkspace.Dimensions)
                            {
                                if (_dimensionMaxLevels.TryGetValue(dim.Name, out int maxDepth))
                                {
                                    for (int i = 0; i < maxDepth; i++)
                                    {
                                        // WICHTIG für C# Closures in Schleifen: Den Index in einer lokalen Variable fangen!
                                        int levelIndex = i; 
                                        string title = maxDepth == 1 ? dim.Name : $"{dim.Name} (Ebene {levelIndex + 1})";
                                        
                                        <PropertyColumn Property="@(x => GetDimensionLevel(x, dim.Name, levelIndex))" Title="@title" />
                                    }
                                }
                            }
                            
                            <PropertyColumn Property="x => x.Amount" Title="Betrag" AggregateDefinition="_sumAggregation" Format="N2" />
                        </Columns>
                    </MudDataGrid>
                </MudItem>

                <MudItem xs="12">
                    <MudPaper Class="pa-6 mt-4" Elevation="1">
                        <MudText Typo="Typo.h6" Class="mb-4">Geldfluss (Sankey-Diagramm)</MudText>
                        
                        <MudSelect T="string" 
                                Label="Sichtweise (Klick-Reihenfolge bestimmt den Fluss)" 
                                MultiSelection="true" 
                                SelectedValues="_selectedChartDimensions" 
                                SelectedValuesChanged="OnChartDimensionsChanged"
                                Variant="Variant.Outlined" 
                                Class="mb-6">
                            @foreach (var dim in _selectedWorkspace.Dimensions)
                            {
                                <MudSelectItem T="string" Value="@dim.Name">@dim.Name</MudSelectItem>
                            }
                        </MudSelect>

                        <div id="sankey-chart" style="width: 100%; height: 400px;"></div>
                    </MudPaper>
                </MudItem>

            </MudGrid>
        }
    }
</MudContainer>

@code {
    [Parameter] public string? WorkspaceId { get; set; }

    private List<Workspace> _workspaces = new();
    private Workspace? _selectedWorkspace;
    private List<AnalysisFakt> _fakten = new();

    private bool _isLoading = true;
    private bool _isInteractive = false;
    private bool _shouldRenderChart = false;

    private List<object[]> _sankeyData = new();
    private IReadOnlyCollection<string> _selectedChartDimensions = new List<string>();
    private Dictionary<string, int> _dimensionMaxLevels = new();
    
  
    private AggregateDefinition<AnalysisFakt> _sumAggregation = new()
    {
        Type = AggregateType.Sum,
        DisplayFormat = "Summe: {value} €"
    };

    protected override async Task OnInitializedAsync()
    {
        _workspaces = await DbService.GetWorkspacesForUserAsync();
        
        if (!string.IsNullOrWhiteSpace(WorkspaceId))
        {
            var preselected = _workspaces.FirstOrDefault(w => w.Id == WorkspaceId);
            if (preselected != null)
            {
                await OnWorkspaceSelected(preselected);
            }
        }
        
        _isLoading = false;
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            // Ab jetzt ist der Browser da und JavaScript ist erlaubt!
            _isInteractive = true;
        }

        if (_isInteractive && _shouldRenderChart && !_isLoading && _selectedWorkspace != null)
        {
            _shouldRenderChart = false; // Flag direkt wieder wegnehmen
            await DrawChartAsync();
        }
    }

   private async Task OnWorkspaceSelected(Workspace ws)
    {
        _selectedWorkspace = ws;
        _fakten = await DbService.GetFaktenForWorkspaceAsync(ws.Id!);
        
        // Berechne die maximale Hierarchie-Tiefe für jede Dimension
        _dimensionMaxLevels.Clear();
        foreach (var dim in ws.Dimensions)
        {
            int maxDepth = 1;
            foreach (var f in _fakten)
            {
                var val = GetDimensionValue(f, dim.Name);
                if (!string.IsNullOrWhiteSpace(val))
                {
                    var parts = val.Split(new[] { " / " }, StringSplitOptions.RemoveEmptyEntries);
                    if (parts.Length > maxDepth) maxDepth = parts.Length;
                }
            }
            _dimensionMaxLevels[dim.Name] = maxDepth;
        }

        _selectedChartDimensions = ws.Dimensions.Take(2).Select(d => d.Name).ToList();
        
        await UpdateChartDataAsync();
    }

    private string GetDimensionLevel(AnalysisFakt fakt, string dimName, int level)
    {
        var val = GetDimensionValue(fakt, dimName);
        if (string.IsNullOrWhiteSpace(val)) return "-";
        
        var parts = val.Split(new[] { " / " }, StringSplitOptions.RemoveEmptyEntries);
        
        // Wenn dieser spezielle Datensatz nicht so tief verschachtelt ist, gib einen Strich zurück
        return parts.Length > level ? parts[level].Trim() : "-";
    }

    private async Task OnChartDimensionsChanged(IReadOnlyCollection<string> values)
    {
        _selectedChartDimensions = values;
        await UpdateChartDataAsync();
    }

    // Hilfsmethode, um das Dictionary im DataGrid sauber darzustellen
    private string GetDimensionValue(AnalysisFakt fakt, string dimensionName)
    {
        return fakt.Dimensions.TryGetValue(dimensionName, out var value) ? value : "-";
    }

    private Dictionary<string, string> _pathLabels = new();
    private HashSet<string> _usedLabels = new();

   private async Task UpdateChartDataAsync()
    {
        _sankeyData.Clear();
        _pathLabels.Clear(); 
        _usedLabels.Clear();
        
        if (!_fakten.Any() || _selectedWorkspace == null || !_selectedChartDimensions.Any())
        {
            _shouldRenderChart = true; // NEU: Nur noch Flag setzen
            return;
        }

        var selectedDims = _selectedChartDimensions.ToList();
        var workspaceLabel = GetUniqueLabel(_selectedWorkspace.Name, _selectedWorkspace.Name);

        foreach (var fakt in _fakten)
        {
            var currentFullPath = _selectedWorkspace.Name;
            var currentSourceLabel = workspaceLabel;

            foreach (var dimName in selectedDims)
            {
                var val = GetDimensionValue(fakt, dimName);
                
                var levels = string.IsNullOrWhiteSpace(val)
                    ? new[] { $"Ohne {dimName}" }
                    : val.Split(new[] { " / " }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray();

                foreach (var level in levels)
                {
                    var targetFullPath = $"{currentFullPath} / {level}";
                    var targetLabel = GetUniqueLabel(targetFullPath, level);

                    AddSankeyLink(currentSourceLabel, targetLabel, fakt.Amount);
                    
                    currentFullPath = targetFullPath; 
                    currentSourceLabel = targetLabel;
                }
            }
        }

        _shouldRenderChart = true; // NEU: Nur noch Flag setzen
    }

    // --- NEU: Die Magie, die unsichtbare Leerzeichen für eindeutige IDs anhängt ---
    private string GetUniqueLabel(string fullPath, string shortName)
    {
        // Haben wir für exakt diesen Pfad (z.B. Italien/Maut) schon ein Label?
        if (_pathLabels.TryGetValue(fullPath, out var existingLabel))
        {
            return existingLabel;
        }

        string candidate = shortName;

        // Solange IRGENDEIN anderer Pfad dieses Label optisch schon nutzt,
        // hängen wir ein unsichtbares Leerzeichen (\u200B) an!
        while (_usedLabels.Contains(candidate))
        {
            candidate += "\u200B";
        }

        // Speichern und zurückgeben
        _pathLabels[fullPath] = candidate;
        _usedLabels.Add(candidate);
        return candidate;
    }

    // AddSankeyLink ist wieder ganz simpel und nimmt nur Strings
    private void AddSankeyLink(string source, string target, decimal amount)
    {
        var existing = _sankeyData.FirstOrDefault(d => (string)d[0] == source && (string)d[1] == target);
        if (existing != null)
        {
            existing[2] = (decimal)existing[2] + amount;
        }
        else
        {
            _sankeyData.Add(new object[] { source, target, amount });
        }
    }

    private async Task DrawChartAsync()
    {
        if (!_isInteractive) 
            return;
        // Ein winziger Delay stellt sicher, dass Blazor das UI fertig gerendert hat
        await JSRuntime.InvokeVoidAsync("drawSankeyChart", "sankey-chart", _sankeyData);
    }
}