using System.IO.Compression;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SatelliteProvider.Common.Configs;

namespace SatelliteProvider.Services.RouteManagement;

// AZ-364 / C11: extracted from RouteProcessingService.CreateTilesZipAsync.
// Owns the route_<id>_tiles.zip path computation and the entry-name
// resolution (relative to StorageConfig.TilesDirectory when the tile lives
// under that root, or by file name otherwise). Behavior preserved verbatim:
// existing zip is overwritten, missing tiles are logged but not fatal.
public class TilesZipBuilder
{
    private readonly StorageConfig _storageConfig;
    private readonly ILogger<TilesZipBuilder> _logger;

    public TilesZipBuilder(IOptions<StorageConfig> storageConfig, ILogger<TilesZipBuilder> logger)
    {
        ArgumentNullException.ThrowIfNull(storageConfig);
        _storageConfig = storageConfig.Value;
        _logger = logger;
    }

    public Task<string> BuildAsync(
        Guid routeId,
        IEnumerable<TileInfo> tiles,
        CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(tiles);

        Directory.CreateDirectory(_storageConfig.ReadyDirectory);
        var zipFilePath = Path.Combine(_storageConfig.ReadyDirectory, $"route_{routeId}_tiles.zip");

        return Task.Run(() =>
        {
            if (File.Exists(zipFilePath))
            {
                File.Delete(zipFilePath);
            }

            using var zipArchive = ZipFile.Open(zipFilePath, ZipArchiveMode.Create);
            int addedFiles = 0;
            int missingFiles = 0;

            var tilesBasePath = _storageConfig.TilesDirectory;
            var normalizedBasePath = Path.GetFullPath(tilesBasePath)
                .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

            foreach (var tile in tiles)
            {
                if (cancellationToken.IsCancellationRequested)
                    break;

                if (File.Exists(tile.FilePath))
                {
                    try
                    {
                        var fullPath = Path.GetFullPath(tile.FilePath);
                        string entryName;

                        if (fullPath.StartsWith(normalizedBasePath, StringComparison.OrdinalIgnoreCase))
                        {
                            var relativePath = fullPath.Substring(normalizedBasePath.Length + 1);
                            relativePath = relativePath.Replace(Path.DirectorySeparatorChar, '/');
                            entryName = "tiles/" + relativePath;
                        }
                        else
                        {
                            entryName = "tiles/" + Path.GetFileName(tile.FilePath);
                        }

                        zipArchive.CreateEntryFromFile(tile.FilePath, entryName, CompressionLevel.Optimal);
                        addedFiles++;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogWarning(ex, "Failed to add tile to zip: {FilePath}", tile.FilePath);
                    }
                }
                else
                {
                    _logger.LogWarning("Tile file not found for zip: {FilePath}", tile.FilePath);
                    missingFiles++;
                }
            }

            _logger.LogInformation(
                "Tiles zip created: {ZipPath} with {AddedFiles} tiles ({MissingFiles} missing)",
                zipFilePath, addedFiles, missingFiles);

            return zipFilePath;
        }, cancellationToken);
    }
}
