using System.Reflection;
using FluentAssertions;
using SatelliteProvider.DataAccess.Repositories;

namespace SatelliteProvider.Tests;

public class RepositoryRefactorTests
{
    [Fact]
    public void TileRepository_DoesNotExposeFindExistingTileAsync_AZ376_AC1()
    {
        // Arrange
        var interfaceType = typeof(ITileRepository);
        var concreteType = typeof(TileRepository);

        // Assert
        interfaceType.GetMethod("FindExistingTileAsync").Should().BeNull(
            "AZ-376 deletes the unused FindExistingTileAsync method from ITileRepository");
        concreteType.GetMethod("FindExistingTileAsync").Should().BeNull(
            "AZ-376 deletes the unused FindExistingTileAsync implementation from TileRepository");
    }

    [Fact]
    public void TileRepository_KeepsAndUsesLogger_AZ378_AC1()
    {
        // Arrange
        var path = LocateRepoFile(Path.Combine("SatelliteProvider.DataAccess", "Repositories", "TileRepository.cs"));
        path.Should().NotBeNull();
        var content = File.ReadAllText(path!);

        // Assert
        content.Should().Contain("ILogger<TileRepository>",
            "TileRepository keeps the logger field per AZ-378 recommended split");
        content.Should().Contain("_logger.LogWarning",
            "AZ-378 AC-1 requires the kept logger to actually be used (slow-query warning)");
        content.Should().Contain("SlowQueryThresholdMs",
            "Slow-query threshold is a named const per the task spec");
    }

    [Fact]
    public void RegionRepository_HasNoUnusedLoggerParameter_AZ378_AC2()
    {
        // Arrange
        var ctors = typeof(RegionRepository).GetConstructors(BindingFlags.Public | BindingFlags.Instance);

        // Assert
        ctors.Should().HaveCount(1);
        ctors[0].GetParameters().Should().OnlyContain(p => p.ParameterType == typeof(string),
            "AZ-378 removes the unused ILogger<RegionRepository> parameter; only the connection string remains");
    }

    [Fact]
    public void RouteRepository_HasNoUnusedLoggerParameter_AZ378_AC2()
    {
        // Arrange
        var ctors = typeof(RouteRepository).GetConstructors(BindingFlags.Public | BindingFlags.Instance);

        // Assert
        ctors.Should().HaveCount(1);
        ctors[0].GetParameters().Should().OnlyContain(p => p.ParameterType == typeof(string),
            "AZ-378 removes the unused ILogger<RouteRepository> parameter; only the connection string remains");
    }

    [Fact]
    public void TileRepository_DefinesColumnListConstantOnce_AZ379_AC1()
    {
        // Arrange
        var path = LocateRepoFile(Path.Combine("SatelliteProvider.DataAccess", "Repositories", "TileRepository.cs"));
        path.Should().NotBeNull();
        var content = File.ReadAllText(path!);

        // Assert
        CountOccurrences(content, "private const string ColumnList").Should().Be(1,
            "AZ-379 AC-1 requires exactly one ColumnList constant per repository");
        CountOccurrences(content, "{ColumnList}").Should().BeGreaterThanOrEqualTo(3,
            "TileRepository has at least 3 SELECTs that should reuse {ColumnList} (GetById, GetByTileCoordinates, GetTilesByRegion)");
    }

    [Fact]
    public void RegionRepository_DefinesColumnListConstantOnce_AZ379_AC1()
    {
        // Arrange
        var path = LocateRepoFile(Path.Combine("SatelliteProvider.DataAccess", "Repositories", "RegionRepository.cs"));
        path.Should().NotBeNull();
        var content = File.ReadAllText(path!);

        // Assert
        CountOccurrences(content, "private const string ColumnList").Should().Be(1);
        CountOccurrences(content, "{ColumnList}").Should().BeGreaterThanOrEqualTo(2,
            "RegionRepository has 2 SELECTs that should reuse {ColumnList} (GetById, GetByStatus)");
    }

    [Fact]
    public void RouteRepository_DefinesColumnListConstantOnce_AZ379_AC1()
    {
        // Arrange
        var path = LocateRepoFile(Path.Combine("SatelliteProvider.DataAccess", "Repositories", "RouteRepository.cs"));
        path.Should().NotBeNull();
        var content = File.ReadAllText(path!);

        // Assert
        CountOccurrences(content, "private const string ColumnList").Should().Be(1);
        CountOccurrences(content, "{ColumnList}").Should().BeGreaterThanOrEqualTo(2,
            "RouteRepository has 2 routes-table SELECTs that should reuse {ColumnList} (GetById, GetRoutesWithPendingMaps)");
    }

    [Fact]
    public void RepositoryColumnLists_ContainExpectedColumns_AZ379_AC2()
    {
        // Arrange
        var tilePath = LocateRepoFile(Path.Combine("SatelliteProvider.DataAccess", "Repositories", "TileRepository.cs"));
        var regionPath = LocateRepoFile(Path.Combine("SatelliteProvider.DataAccess", "Repositories", "RegionRepository.cs"));
        var routePath = LocateRepoFile(Path.Combine("SatelliteProvider.DataAccess", "Repositories", "RouteRepository.cs"));
        tilePath.Should().NotBeNull();
        regionPath.Should().NotBeNull();
        routePath.Should().NotBeNull();

        var tileContent = File.ReadAllText(tilePath!);
        var regionContent = File.ReadAllText(regionPath!);
        var routeContent = File.ReadAllText(routePath!);

        var tileColumns = new[]
        {
            "id", "tile_zoom as TileZoom", "tile_x as TileX", "tile_y as TileY",
            "latitude", "longitude",
            "tile_size_meters as TileSizeMeters", "tile_size_pixels as TileSizePixels",
            "image_type as ImageType", "maps_version as MapsVersion", "version",
            "file_path as FilePath", "source", "captured_at as CapturedAt",
            "created_at as CreatedAt", "updated_at as UpdatedAt"
        };
        var regionColumns = new[]
        {
            "id", "latitude", "longitude", "size_meters as SizeMeters",
            "zoom_level as ZoomLevel", "status",
            "csv_file_path as CsvFilePath", "summary_file_path as SummaryFilePath",
            "tiles_downloaded as TilesDownloaded", "tiles_reused as TilesReused",
            "stitch_tiles as StitchTiles",
            "created_at as CreatedAt", "updated_at as UpdatedAt"
        };
        var routeColumns = new[]
        {
            "id", "name", "description", "region_size_meters as RegionSizeMeters",
            "zoom_level as ZoomLevel", "total_distance_meters as TotalDistanceMeters",
            "total_points as TotalPoints", "request_maps as RequestMaps",
            "maps_ready as MapsReady", "create_tiles_zip as CreateTilesZip",
            "csv_file_path as CsvFilePath",
            "summary_file_path as SummaryFilePath", "stitched_image_path as StitchedImagePath",
            "tiles_zip_path as TilesZipPath",
            "created_at as CreatedAt", "updated_at as UpdatedAt"
        };

        // Assert
        foreach (var column in tileColumns)
        {
            tileContent.Should().Contain(column,
                $"TileRepository ColumnList must include '{column}' to keep generated SQL semantically identical");
        }
        foreach (var column in regionColumns)
        {
            regionContent.Should().Contain(column,
                $"RegionRepository ColumnList must include '{column}' to keep generated SQL semantically identical");
        }
        foreach (var column in routeColumns)
        {
            routeContent.Should().Contain(column,
                $"RouteRepository ColumnList must include '{column}' to keep generated SQL semantically identical");
        }
    }

    private static int CountOccurrences(string haystack, string needle)
    {
        var count = 0;
        var index = 0;
        while ((index = haystack.IndexOf(needle, index, StringComparison.Ordinal)) != -1)
        {
            count++;
            index += needle.Length;
        }
        return count;
    }

    private static string? LocateRepoFile(string relativePath)
    {
        var dir = new DirectoryInfo(Directory.GetCurrentDirectory());
        while (dir is not null)
        {
            var candidate = Path.Combine(dir.FullName, relativePath);
            if (File.Exists(candidate))
            {
                return candidate;
            }
            dir = dir.Parent;
        }
        return null;
    }
}
