using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AngleSharp.Dom;
using AngleSharp.Html.Parser;
using Knot.Contracts.Settings.Application.Abstractions;
using Knot.Contracts.Settings.Application.DTOs;
using Knot.Modules.TelegramImport.Application.Abstractions;
using Knot.Shared.Kernel;
using MediatR;

namespace Knot.Modules.TelegramImport.Application.TelegramImport;

public static class TelegramImportState
{
    public static readonly ConcurrentDictionary<Guid, string> TempZips = new();
}

public record ImportConflictDto(string Type, string Message, bool Blocked);

public record AnalyzeImportResponseDto(
    Guid Token,
    List<string> Names,
    List<ImportConflictDto> Conflicts,
    int TotalMessages,
    string? GroupName);

public record AnalyzeImportCommand(Stream FileStream, string FileName) : ICommand<AnalyzeImportResponseDto>;

internal sealed class AnalyzeImportCommandHandler : ICommandHandler<AnalyzeImportCommand, AnalyzeImportResponseDto>
{
    private readonly ISettingsService _settingsService;
    private readonly ITelegramHtmlParser _htmlParser;

    public AnalyzeImportCommandHandler(ISettingsService settingsService, ITelegramHtmlParser htmlParser)
    {
        _settingsService = settingsService;
        _htmlParser = htmlParser;
    }

    public async Task<Result<AnalyzeImportResponseDto>> Handle(AnalyzeImportCommand request, CancellationToken cancellationToken)
    {
        if (request.FileStream == null || request.FileStream.Length == 0)
        {
            return Result.Failure<AnalyzeImportResponseDto>(TelegramImportErrors.FileEmpty);
        }

        if (!request.FileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
        {
            return Result.Failure<AnalyzeImportResponseDto>(TelegramImportErrors.FileInvalidExtension);
        }

        var token = Guid.NewGuid();
        var tempPath = Path.Combine(Path.GetTempPath(), $"{token}.zip");

        try
        {
            await using (var fs = new FileStream(tempPath, FileMode.Create))
            {
                await request.FileStream.CopyToAsync(fs, cancellationToken);
            }

            var names = new HashSet<string>();
            int totalMessages = 0;
            string? groupName = null;

            using (var archive = ZipFile.OpenRead(tempPath))
            {
                var htmlEntries = archive.Entries
                    .Where(e => e.FullName.EndsWith(".html", StringComparison.OrdinalIgnoreCase) && 
                               (e.Name.StartsWith("messages", StringComparison.OrdinalIgnoreCase) || e.Name == "export_results.html"))
                    .OrderBy(e => e.FullName.Length)
                    .ThenBy(e => e.FullName)
                    .ToList();

                foreach (var entry in htmlEntries)
                {
                    using var stream = entry.Open();
                    
                    if (string.IsNullOrEmpty(groupName))
                    {
                        groupName = await _htmlParser.ExtractGroupNameAsync(stream, cancellationToken);
                        // Reset stream position if possible? No, entry.Open() returns a new stream.
                        // But wait! ExtractGroupNameAsync consumess the stream!
                        // I'll reopen it for messages if it's the same entry.
                    }

                    using var stream2 = entry.Open(); 
                    var messages = await _htmlParser.ParseMessagesAsync(stream2, "", cancellationToken);
                    totalMessages += messages.Count;

                    foreach (var m in messages)
                    {
                        if (!string.IsNullOrEmpty(m.SenderName))
                            names.Add(m.SenderName);
                    }
                }
            }

            if (names.Count == 0 && totalMessages == 0)
            {
                return Result.Failure<AnalyzeImportResponseDto>(new Error("TelegramImport.NoMessagesFound", "В архиве не найдено сообщений Telegram или формат HTML не распознан."));
            }

            // Анализ конфликтов политик
            var settings = await _settingsService.GetSettingsAsync(cancellationToken);
            var conflicts = new List<ImportConflictDto>();

            if (!settings.Messages.AllowMedia)
                conflicts.Add(new ImportConflictDto("Media", "Медиафайлы (фото/видео) отключены на сервере. Они не будут импортированы.", true));

            if (!settings.Messages.AllowVoiceMessages)
                conflicts.Add(new ImportConflictDto("Voice", "Голосовые сообщения запрещены администратором. Будут пропущены.", true));

            if (!settings.Messages.AllowPolls)
                conflicts.Add(new ImportConflictDto("Polls", "Опросы не поддерживаются текущими настройками сервера.", true));

            TelegramImportState.TempZips[token] = tempPath;

            return Result.Success(new AnalyzeImportResponseDto(token, names.OrderBy(x => x).ToList(), conflicts, totalMessages, groupName));
        }
        catch (Exception ex)
        {
            if (File.Exists(tempPath)) File.Delete(tempPath);
            return Result.Failure<AnalyzeImportResponseDto>(new Error("TelegramImport.ProcessError", $"Ошибка при обработке архива: {ex.Message}"));
        }
    }
}




