using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.AIHITRelationshipsSyncer.Models;
using PARR.AIHITRelationshipsSyncer.Services.Interfaces;
using PARR.AIHITRelationshipsSyncer.Settings;
using PARR.Core.Repositories.Interfaces.Unit;
using PARR.Domain.Entities.Unit;

namespace PARR.AIHITRelationshipsSyncer.Services.Implementations
{
    internal class RelationshipsSyncService : IRelationshipsSyncService
    {
        private readonly ILogger<RelationshipsSyncService> logger;
        private readonly IUnitRepository unitService;
        private readonly WorkerSettings workerSettings;

        public RelationshipsSyncService(
            ILogger<RelationshipsSyncService> logger,
            IUnitRepository unitService,
            WorkerSettings workerSettings
            )
        {
            this.logger = logger;
            this.unitService = unitService;
            this.workerSettings = workerSettings;
        }


        public async Task SyncAsync(List<AihitData> aihitdata)
        {
            var validPairs = ValidateInput(aihitdata);
            if (!validPairs.Any()) return;

            var sourceUnitNames = ExtractUnitNames(validPairs);
            var existingUnits = await GetOrCreateUnitsAsync(sourceUnitNames);

            var (toRemove, toAdd) = await GetDeltaAsync(validPairs);

            await RemoveRelationshipsAsync(toRemove);
            await AddRelationshipsAsync(toAdd, existingUnits);

            await FinalCommitAsync(toRemove.Count, toAdd.Count);
        }

        private List<(string Parent, string Child)> ValidateInput(List<AihitData> data)
        {
            if (data == null || data.Count == 0)
            {
                logger.LogWarning("Получены пустые или null данные из АИХ ИТ. Пропускаю синхронизацию");
                return null!;
            }

            var validPairs = data
                            .Where(t => !string.IsNullOrWhiteSpace(t.ParentName) && !string.IsNullOrWhiteSpace(t.ChildName))
                            .Where(t => t.ParentName != t.ChildName)
                            .Select(t => (
                                Parent: t.ParentName!.Trim().ToUpperInvariant(),
                                Child: t.ChildName!.Trim().ToUpperInvariant()
                            ))
                            .Distinct()
                            .ToList();
            if (validPairs.Count == 0)
            {
                logger.LogWarning("После нормализации не осталось валидных связей. Пропускаю синхронизацию");
                return new();
            }

            logger.LogInformation("Получено {Count} валидных связей", validPairs.Count);
            return validPairs;
        }


        private static List<string> ExtractUnitNames(List<(string Parent, string Child)> pairs)
        {
            return pairs
                    .SelectMany(x => new[] { x.Parent, x.Child })
                    .Distinct()
                    .ToList();
        }


        private async Task<Dictionary<string, Unit>> GetOrCreateUnitsAsync(List<string> names)
        {
            var existing = await unitService.Get()
                                    .AsNoTracking()
                                    .ToDictionaryAsync(u => u.Name, StringComparer.Ordinal);

            var missing = names.Except(existing.Keys).ToList();
            if (!missing.Any()) return existing;

            logger.LogInformation("Создание отсутствующих Unit:{Count}", missing.Count);
            foreach (var name in missing)
            {
                var unit = new Unit
                {
                    Id = Guid.NewGuid(),
                    Name = name,
                    DateCreated = DateTimeOffset.UtcNow
                };

                if (await unitService.CreateAsync(unit))
                {
                    logger.LogInformation("Создан Unit: {Name}", name);
                    existing[name] = unit;
                }
                else
                    logger.LogError("Не удалось создать Unit: {Name}", name);
            }

            await unitService.CommitAsync();
            return existing;
        }


        private async Task<(List<ExistingRelationship> ToRemove, List<(string, string)> ToAdd)> GetDeltaAsync(List<(string Parent, string Child)> sourcePairs)
        {
            var allExistings = await unitService.Get()
                                    .AsNoTracking()
                                    .Include(u => u.ChildUnits).ThenInclude(r => r.ChildUnit)
                                    .Where(u => u.ChildUnits.Any())
                                    .SelectMany(u => u.ChildUnits, (parent, relationship) => new ExistingRelationship
                                    {
                                        ParentName = parent.Name,
                                        ChildName = relationship.ChildUnit!.Name,
                                        Relationship = relationship
                                    }
                                    )
                                    .ToListAsync();

            var sourceSet = new HashSet<(string, string)>(sourcePairs);
            var existingSet = new HashSet<(string, string)>(allExistings.Select(x => (x.ParentName, x.ChildName)));

            var toRemove = allExistings.Where(r => !sourceSet.Contains((r.ParentName, r.ChildName))).ToList();
            var toAdd = sourcePairs.Where(p => !existingSet.Contains(p)).ToList();

            logger.LogInformation("Связей для удаления: {ToRemove}, для добавления: {ToAdd}", toRemove.Count, toAdd.Count);
            return (toRemove, toAdd);
        }


        private async Task RemoveRelationshipsAsync(List<ExistingRelationship> toRemove)
        {
            if (!toRemove.Any()) return;

            var parentNames = toRemove.Select(r => r.ParentName).Distinct().ToList();

            var parents = await unitService.Get()
                                .Include(u => u.ChildUnits)
                                .Where(u => parentNames.Contains(u.Name))
                                .ToDictionaryAsync(u => u.Name, StringComparer.Ordinal);

            foreach (var rel in toRemove)
            {
                if (!parents.TryGetValue(rel.ParentName, out var parent))
                {
                    logger.LogWarning("Родитель '{Parent}' не найден при попытке удаления связи - '{Child}'", rel.ParentName, rel.ChildName);
                    continue;
                }

                var relationshipToRemove = parent.ChildUnits
                                                .FirstOrDefault(r => r.ChildUnitId == rel.Relationship.ChildUnitId);

                if (relationshipToRemove != null)
                {
                    logger.LogInformation("Удаление связи: '{Parent}' - '{Child}'", rel.ParentName, rel.ChildName);
                    parent.ChildUnits.Remove(relationshipToRemove);
                }
                else
                {
                    logger.LogWarning("Связь '{Parent}'-'{Child}' не найдена в коллекции ChildUnits для удаления", rel.ParentName, rel.ChildName);
                }
            }
        }

        private async Task AddRelationshipsAsync(List<(string Parent, string Child)> toAdd, Dictionary<string, Unit> allUnits)
        {
            if (!toAdd.Any()) return;

            var parentNames = toAdd.Select(x => x.Parent).Distinct().ToList();
            var parents = await unitService.Get()
                                    .Include(u => u.ChildUnits)
                                    .Where(u => parentNames.Contains(u.Name))
                                    .ToDictionaryAsync(u => u.Name, StringComparer.Ordinal);

            foreach (var (Parent, Child) in toAdd)
            {
                if (!parents.TryGetValue(Parent, out var parent) ||
                    !allUnits.TryGetValue(Child, out var child))
                {
                    logger.LogWarning("Пропущена связь '{Parent}' - '{Child}': Unit не найден", Parent, Child);
                    continue;
                }

                if (parent!.ChildUnits.Any(r => r.ChildUnitId == child.Id))
                {
                    logger.LogWarning("Связь уже существует: '{Parent}' - '{Child}'", Parent, Child);
                    continue;
                }

                parent.ChildUnits.Add(new UnitInUnit
                {
                    ParentUnitId = parent.Id,
                    ChildUnitId = child.Id,
                    DateCreated = DateTimeOffset.UtcNow
                });

                logger.LogInformation("Добавлена связь: '{Parent}' - '{Child}'", Parent, Child);
            }
        }


        private async Task FinalCommitAsync(int removedCount, int addedCount)
        {
            if (removedCount > 0 || addedCount > 0)
            {
                if (await unitService.CommitAsync())
                {
                    logger.LogInformation("Синхронизация завершена. Удалено: {Removed}, добавлено: {Added}", removedCount, addedCount);
                }
                else
                    logger.LogError("Не удалось сохранить изменения в БД");
            }
            else
                logger.LogInformation("Изменений не обнаружено");
        }

        private sealed class ExistingRelationship
        {
            public string ParentName { get; init; }
            public string ChildName { get; init; }
            public UnitInUnit Relationship { get; init; }
        }

    }
}

