using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using SharedKernel.Storage;

namespace Infrastructure.Storage
{
    public sealed class LocalFileStorage : IFileStorage
    {
        private readonly IWebHostEnvironment _env;
        private readonly ILogger<LocalFileStorage> _log;

        public LocalFileStorage(IWebHostEnvironment env, ILogger<LocalFileStorage> log)
        {
            _env = env;
            _log = log;
        }

        public async Task<FileUploadResult?> SaveFileAsync(IFormFile? file, FileStoragePath path, string[] allowedExtensions, CancellationToken ct = default)
        {
            if (file is null || file.Length <= 0)
            {
                return null;
            }

            var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
            if (!allowedExtensions.Contains(ext))
            {
                throw new ArgumentException("허용되지 않는 파일 확장자입니다.");
            }

            var rel = path.ToRelativePath();
            var dir = CombineUnderWebRoot(rel);

            Directory.CreateDirectory(dir); // 경로 생성(없으면)

            var fileName = $"{Guid.NewGuid():N}{ext}";
            var fullPath = Path.Combine(dir, fileName);

            await using (var fs = new FileStream(fullPath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
            {
                await file.CopyToAsync(fs, ct);
            }

            var url = "/" + $"{rel}/{fileName}".Replace("\\", "/");

            short? width = null, height = null;
            if (IsImageExtension(ext))
            {
                using var readStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Read);
                var dim = ImageDimensionHelper.GetDimensions(readStream);
                if (dim.HasValue)
                {
                    width = dim.Value.Width;
                    height = dim.Value.Height;
                }
            }

            return new FileUploadResult(url, fileName, file.Length, ext, width, height);
        }

        public async Task<FileUploadResult?> SaveBytesAsync(ReadOnlyMemory<byte> bytes, string extension, FileStoragePath path, CancellationToken ct = default)
        {
            if (bytes.Length <= 0)
            {
                return null;
            }

            var ext = FileUtils.NormalizeExtension(extension);
            if (ext is not (".jpg" or ".png" or ".gif" or ".bmp" or ".webp"))
            {
                throw new ArgumentException("허용되지 않는 파일 확장자입니다.");
            }

            var rel = path.ToRelativePath();
            var dir = CombineUnderWebRoot(rel);

            Directory.CreateDirectory(dir); // 경로 생성(없으면)

            var fileName = $"{Guid.NewGuid():N}{ext}";
            var fullPath = Path.Combine(dir, fileName);

            await File.WriteAllBytesAsync(fullPath, bytes.ToArray(), ct);

            var url = "/" + $"{rel}/{fileName}".Replace("\\", "/");

            short? width = null, height = null;
            var dim = ImageDimensionHelper.GetDimensions(bytes);
            if (dim.HasValue)
            {
                width = dim.Value.Width;
                height = dim.Value.Height;
            }

            return new FileUploadResult(url, fileName, bytes.Length, ext, width, height);
        }

        public void DeleteByUrl(string? url)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                return;
            }

            if (!FileUtils.IsLocalPublicUrl(url))
            {
                _log.LogWarning("삭제가 불가한 주소입니다. {0}", url);
                return;
            }

            var fullPath = CombineUnderWebRoot(url.TrimStart('/'));

            if (!File.Exists(fullPath))
            {
                _log.LogWarning("파일을 찾을 수 없습니다. {0}", fullPath);
                return;
            }

            try
            {
                File.Delete(fullPath);

                _log.LogInformation("파일이 삭제됨 : {0}", fullPath);
            }
            catch (Exception e)
            {
                _log.LogError(e, "파일 삭제 중 오류가 발생했습니다. {0}", fullPath);
            }
        }

        private static bool IsImageExtension(string ext)
        {
            return ext is ".jpg" or ".jpeg" or ".png" or ".gif" or ".webp" or ".bmp";
        }

        private string CombineUnderWebRoot(string relative)
        {
            var webRoot = Path.GetFullPath(_env.WebRootPath);
            var fullPath = Path.GetFullPath(Path.Combine(_env.WebRootPath, relative));

            if (!fullPath.StartsWith(webRoot, StringComparison.OrdinalIgnoreCase))
            {
                throw new UnauthorizedAccessException("경로를 찾을 수 없습니다.");
            }

            return fullPath;
        }
    }
}
