using System.Text.RegularExpressions;

namespace bitforum.Services
{
    public enum UploadTarget
    {
        Editor,     // 에디터
        Upload      // 직접 첨부
    }

    public enum UploadFolder
    {
        Basic,      // 기본 설정
        Document,   // 문서
        Faq,        // 자주 묻는 질문
        Popup,      // 팝업
        Banner,     // 배너
        Grade,      // 회원 등급
        Template,   // 알림 발송 양식
        Member      // 회원 사진/아이콘
    }

    public interface IFileUploadService
    {
        // 에디터의 모든 파일 삭제
        public Task CleanUpEditorImagesAsync(string? content);

        // 에디터에 첨부된 이미지를 저장
        public Task<string?> UploadEditorAsync(string? newContent, string? oldContent, UploadFolder folder, int? ID = null);

        // 직접 첨부한 이미지 저장
        public Task<string?> UploadImageAsync(IFormFile? file, UploadFolder folder, int? ID = null);

        // 물리적 파일 저장
        public Task<string?> UploadFileAsync(IFormFile? file, string[] allowedExtensions, UploadTarget target, UploadFolder folder, int? ID = null);

        // 파일 삭제
        public bool RemoveFile(string? filePath);
    }

    public class FileUploadService : IFileUploadService
    {
        private readonly ILogger<FileUploadService> _logger;
        private readonly IWebHostEnvironment _environment;

        public FileUploadService(ILogger<FileUploadService> logger, IWebHostEnvironment environment)
        {
            _logger = logger;
            _environment = environment;
        }

        // 절대 경로 생성
        private string GetAbosolutePath(UploadTarget target, UploadFolder folder, int? ID = null)
        {
            return Path.Combine(_environment.WebRootPath, GetRelativePath(target, folder, ID));
        }

        // 상대 경로 생성
        private string GetRelativePath(UploadTarget target, UploadFolder folder, int? ID = null) 
        {
            string t = target switch
            {
                UploadTarget.Editor => "editor",
                UploadTarget.Upload => "upload",
                _ => throw new ArgumentException("유효하지 않은 `Upload target` 입니다.")
            };

            string f = folder switch
            {
                UploadFolder.Basic => "basic",
                UploadFolder.Document => "document",
                UploadFolder.Faq => "faq",
                UploadFolder.Popup => "popup",
                UploadFolder.Banner => "banner",
                UploadFolder.Grade => "grade",
                UploadFolder.Template => "template",
                UploadFolder.Member => "member",
                _ => throw new ArgumentException("유효하지 않은 `Upload folder` 입니다.")
            };

            return Path.Combine(t, f, ID?.ToString() ?? string.Empty);
        }

        private async Task CleanUpUnusedImagesAsync(string uploadPath, HashSet<string> usedImages)
        {
            try
            {
                var files = Directory.GetFiles(uploadPath);

                if (files.Length == 0)
                {
                    Console.WriteLine("삭제할 미사용 이미지가 없습니다.");
                    return;
                }

                var deleteTasks = files.Select(async file =>
                {
                    var path = $"/{Path.GetRelativePath(_environment.WebRootPath, file).Replace("\\", "/")}";

                    if (!usedImages.Contains(path))
                    {
                        try
                        {
                            await Task.Run(() => File.Delete(file)); // 비동기 파일 삭제
                            Console.WriteLine($"이미지 삭제: {path}");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"이미지 삭제 실패: {path}, 오류: {ex.Message}");
                        }
                    }
                });

                await Task.WhenAll(deleteTasks); // 모든 파일 삭제를 병렬 실행
            }
            catch (Exception e)
            {
                Console.WriteLine($"미사용 파일 정리 실패: {e.Message}");
            }
        }

        private async Task CleanUpOldImagesAsync(HashSet<string> usedImages, HashSet<string> oldImages)
        {
            try
            {
                var unUsedImages = oldImages.Except(usedImages).ToList(); // 삭제할 이미지 찾기

                if (unUsedImages.Count > 0)
                {
                    var deleteTasks = unUsedImages.Select(async img =>
                    {
                        var path = Path.Combine(_environment.WebRootPath, img.TrimStart('/'));

                        if (File.Exists(path))
                        {
                            try
                            {
                                await Task.Run(() => File.Delete(path)); // 비동기 삭제
                                Console.WriteLine($"이미지 삭제: {path}");
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine($"이미지 삭제 실패: {path}, 오류: {ex.Message}");
                            }
                        }
                    });

                    await Task.WhenAll(deleteTasks); // 모든 삭제 작업 비동기 실행
                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"미사용 파일 정리 실패: {e.Message}");
            }
        }

        public async Task CleanUpEditorImagesAsync(string? content)
        {
            string? message = null;

            if (string.IsNullOrEmpty(content))
            {
                message = "삭제할 내용이 비어있습니다.";
                Console.WriteLine(message);
                _logger.LogWarning(message);
                return;
            }

            // <img> 태그에서 이미지 URL 목록 가져오기
            var matches = Regex.Matches(content, @"<img[^>]+src=""(?<src>[^""]+)""[^>]*>");

            if (matches.Count == 0)
            {
                message = "삭제할 이미지 파일이 없습니다.";
                Console.WriteLine(message);
                _logger.LogInformation(message);
                return;
            }

            var deleteTasks = matches.Select(async img =>
            {
                string filePath = Path.Combine(_environment.WebRootPath, img.Groups["src"].Value.TrimStart('/'));

                if (File.Exists(filePath))
                {
                    try
                    {
                        await Task.Run(() => File.Delete(filePath)); // 비동기 삭제
                        message = $"파일 삭제 완료: {filePath}";
                        Console.WriteLine(message);
                        _logger.LogInformation(message);
                    }
                    catch (Exception ex)
                    {
                        message = $"파일 삭제 실패: {filePath}, 오류: {ex.Message}";
                        Console.WriteLine(message);
                        _logger.LogError(message);
                    }
                }
            });

            await Task.WhenAll(deleteTasks);

            message = $"총 {matches.Count}개의 파일이 삭제됨";
            Console.WriteLine(message);
            _logger.LogInformation(message);
        }

        public async Task<string?> UploadEditorAsync(string? newContent, string? oldContent, UploadFolder folder, int? ID = null)
        {
            string path = GetRelativePath(UploadTarget.Editor, folder, ID).Replace("\\", "/");
            string uploadPath = GetAbosolutePath(UploadTarget.Editor, folder, ID);

            var usedImages = new HashSet<string>(); // 새로운 content에서 사용된 이미지 목록
            var oldImages = new HashSet<string>();  // 기존 content에서 사용된 이미지 목록

            if (!Directory.Exists(uploadPath))
            {
                Directory.CreateDirectory(uploadPath);
            }

            // oldContent에서 기존 이미지 URL 목록
            if (!string.IsNullOrEmpty(oldContent))
            {
                var oldMatches = Regex.Matches(oldContent, @"<img[^>]+src=""(?<src>[^""]+)""[^>]*>");
                foreach (Match img in oldMatches)
                {
                    oldImages.Add(img.Groups["src"].Value);
                }
            }

            // <img> 태그에서 현재 사용 중인 이미지 URL 목록 가져오기
            if (!string.IsNullOrEmpty(newContent))
            {
                var matches = Regex.Matches(newContent, @"<img[^>]+src=""(?<src>[^""]+)""[^>]*>");

                foreach (Match img in matches)
                {
                    var src = img.Groups["src"].Value;

                    if (src.StartsWith("data:image/"))
                    {
                        try
                        {
                            var match = Regex.Match(src, @"^data:image/(?<extension>.+?);base64,(?<data>.+)$");
                            if (!match.Success)
                            {
                                throw new Exception("Base64 이미지가 아닙니다.");
                            }

                            var extension = match.Groups["extension"].Value;
                            var data = match.Groups["data"].Value;

                            // 확장자와 파일명 생성
                            var fileName = $"{Guid.NewGuid()}.{extension}";
                            var filePath = Path.Combine(uploadPath, fileName);
                            var webPath = $"/{path}/{fileName}";

                            await File.WriteAllBytesAsync(filePath, Convert.FromBase64String(data)); // Base64 데이터 디코딩 및 파일 저장

                            newContent = newContent.Replace(src, webPath); // HTML에서 Base64 데이터를 실제 이미지 URL로 대체
                            usedImages.Add(webPath); // 사용한 이미지 목록에 추가
                        }
                        catch (Exception e)
                        {
                            _logger.LogWarning($"파일을 생성할 수 없음: {e.Message}");
                            Console.WriteLine(e.Message);
                            continue;
                        }
                    }
                    else if (src.StartsWith($"/{path}"))
                    {
                        usedImages.Add(src);
                    }
                }
            }

            // 사용하지 않는 이미지 파일 삭제
            await CleanUpOldImagesAsync(usedImages, oldImages);

            if (ID.HasValue)
            {
                await CleanUpUnusedImagesAsync(uploadPath, usedImages);
            }

            return newContent;
        }

        public async Task<string?> UploadImageAsync(IFormFile? file, UploadFolder folder, int? ID = null)
        {
            return await UploadFileAsync(file, [".jpg", ".jpeg", ".png", ".gif"], UploadTarget.Upload, folder, ID);
        }

        public async Task<string?> UploadFileAsync(IFormFile? file, string[] allowedExtensions, UploadTarget target, UploadFolder folder, int ?ID = null)
        {
            if (file == null || file.Length == 0)
            {
                return null;
            }

            var extension = Path.GetExtension(file.FileName).ToLower();
            if (!Array.Exists(allowedExtensions, ext => ext == extension))
            {
                throw new ArgumentException("허용되지 않는 파일 형식입니다.");
            }

            var uploadPath = GetAbosolutePath(target, folder, ID);
            if (!Directory.Exists(uploadPath))
            {
                Directory.CreateDirectory(uploadPath);
            }

            var fileName = $"{Guid.NewGuid()}{extension}";
            var filePath = Path.Combine(uploadPath, fileName);
            var webPath =  $"/{GetRelativePath(target, folder, ID)}/{fileName}".Replace("\\", "/");

            // 파일 저장
            using (var stream = new FileStream(filePath, FileMode.Create))
            {
                await file.CopyToAsync(stream);
            }

            return webPath;
        }

        public bool RemoveFile(string? filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                _logger.LogWarning("삭제 요청된 파일 경로가 비어 있습니다.");
                return false;
            }

            var fullPath = Path.Combine(_environment.WebRootPath, filePath.TrimStart('/'));

            if (File.Exists(fullPath))
            {
                try
                {
                    File.Delete(fullPath);
                    _logger.LogInformation($"파일 삭제 성공: {fullPath}");
                    return true;
                }
                catch (Exception e)
                {
                    _logger.LogError($"파일 삭제 실패: {fullPath}, 오류: {e.Message}");
                    return false;
                }
            }

            _logger.LogWarning($"파일이 존재하지 않음: {fullPath}");
            return false;
        }
    }
}
