using Ablera.Serdica.DBModels.Serdica;
using Ablera.Serdica.Microservice.Consumer.Services;
using Ablera.Serdica.Authority.Models;
using Ablera.Serdica.Authority.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Ablera.Serdica.Authority.HostServices;
public class RoutesTreeBuilderHostedService(
    ILogger<RoutesTreeBuilderHostedService> logger,
    IServiceScopeFactory scopeFactory,
    RoutesTreeProvider routesTreeProvider)
        : IHostedService, IDisposable
{
    private Timer? _timer;
    private readonly TimeSpan _updateInterval = TimeSpan.FromMinutes(2);

    public Task StartAsync(CancellationToken cancellationToken)
    {
        logger.LogInformation($"{nameof(RoutesTreeBuilderHostedService)} starting...");
        // Initial snapshot update.
        UpdateSnapshot();
        // Set timer to update every 2 minutes.
        _timer = new Timer(state => UpdateSnapshot(), null, _updateInterval, _updateInterval);
        return Task.CompletedTask;
    }

    private void UpdateSnapshot()
    {
        try
        {
            using var scope = scopeFactory.CreateScope();
            using var requestProfiler = scope.ServiceProvider.GetRequiredService<RequestProfiler>();
            requestProfiler.BeginStage("RoutesTreeBuilderService.UpdateSnapshot");
            using var dbContext = scope.ServiceProvider.GetRequiredService<SerdicaDbContext>();

            // Retrieve flat routes with minimal projection.
            var flatRoutes = dbContext.Routes.AsNoTracking()
                .OrderBy(x => x.SortOrder)
                .Select(x => new RouteEntity
                {
                    Id = x.Id,
                    ParentId = x.ParentId,
                    ViewConfigId = x.ViewConfigId,
                    Type = x.Type,
                    Title = x.Title,
                    Disabled = x.Disabled,
                    IsMenuItem = x.IsMenuItem,
                    IsDashboardItem = x.IsDashboardItem,
                    Path = x.Path,
                    SortOrder = x.SortOrder,
                    Icon = x.Icon,
                    SvgIcon = x.SvgIcon,
                    Breadcrumbs = x.Breadcrumbs,
                    Translate = x.Translate,
                    ExternalUrl = x.ExternalUrl,
                    Url = x.Url,
                    Function = x.Function,
                    OpenInNewTab = x.OpenInNewTab,
                    ExactMatch = x.ExactMatch,
                    ProductCode = x.ProductCode,
                    ProcessBusinessKey = x.ProcessBusinessKey,
                    AllowedRoles = string.IsNullOrWhiteSpace(x.AllowedRoles)
                        ? Array.Empty<string>()
                        : x.AllowedRoles.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
                })
                .ToList();

            // Convert flat list to a tree structure.
            var tree = BuildTree(flatRoutes);

            // Atomically update the snapshot.
            routesTreeProvider.Tree = tree;

            requestProfiler.EndStage("RoutesTreeBuilderService.UpdateSnapshot");

            logger.LogInformation("Routes snapshot updated with {Count} root nodes and {Branches} branches.", tree.Count, flatRoutes.Count);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Failed to update routes snapshot.");
        }
    }

    private static IReadOnlyList<RouteEntity> BuildTree(List<RouteEntity> flatRoutes)
    {
        var lookup = flatRoutes.ToDictionary(r => r.Id);
        var roots = new List<RouteEntity>();

        // Build parent/child relationships.
        foreach (var route in flatRoutes)
        {
            if (route.ParentId.HasValue && lookup.TryGetValue(route.ParentId.Value, out var parent))
            {
                parent.Children.Add(route);
            }
            else
            {
                roots.Add(route);
            }
        }

        // Recursively sort children by SortOrder.
        void SortTree(List<RouteEntity> routes)
        {
            routes.Sort((a, b) => (a.SortOrder ?? 0).CompareTo(b.SortOrder));
            foreach (var r in routes)
            {
                if (r.Children.Any())
                {
                    SortTree(r.Children);
                }
            }
        }
        SortTree(roots);
        return roots;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        logger.LogInformation($"{nameof(RoutesTreeBuilderHostedService)} stopping...");
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}