using System.Data;
using Dapper;
using PlotLine.Models;

namespace PlotLine.Data;

public interface IIllustrationLibraryRepository
{
    Task<IReadOnlyList<IllustrationLibraryItem>> ListAsync(IllustrationLibraryFilter filter);
    Task<IllustrationLibrarySummary> GetSummaryAsync();
    Task<IllustrationLibraryItem?> GetAsync(int illustrationLibraryItemId);
    Task<IllustrationLibraryItem> UpsertPlannedAsync(IllustrationGenerationSpecification specification, IllustrationPromptBuildResult prompt);
    Task<int> QueueAsync(int? illustrationLibraryItemId, string? category);
    Task<IllustrationLibraryItem?> ClaimNextAsync();
    Task MarkGeneratedAsync(int illustrationLibraryItemId, IllustrationStoredImage image, IllustrationImageGenerationResult providerResult, string prompt, string templateVersion);
    Task MarkFailedAsync(int illustrationLibraryItemId, string errorMessage);
    Task ApproveAsync(int illustrationLibraryItemId, int userId);
    Task RejectAsync(int illustrationLibraryItemId, int userId, string? reason);
    Task<IllustrationLibraryItem?> CreateRegenerationAsync(int illustrationLibraryItemId, string? adminNotes);
    Task DisableAsync(int illustrationLibraryItemId);
    Task UpdateMetadataAsync(int illustrationLibraryItemId, string displayTitle, string? shortDescription, string? metadataJson, string? adminNotes);
    Task<IReadOnlyList<IllustrationLibraryItem>> GetApprovedAsync();
    Task<int> SupersedeRecoveredFailuresAsync();
}

public sealed class IllustrationLibraryRepository(ISqlConnectionFactory connectionFactory) : IIllustrationLibraryRepository
{
    public async Task<IReadOnlyList<IllustrationLibraryItem>> ListAsync(IllustrationLibraryFilter filter)
    {
        using var connection = connectionFactory.CreateConnection();
        var rows = await connection.QueryAsync<IllustrationLibraryItem>(
            "dbo.IllustrationLibraryItem_List",
            new
            {
                Category = string.IsNullOrWhiteSpace(filter.Category) ? null : filter.Category,
                Status = string.IsNullOrWhiteSpace(filter.Status) ? null : filter.Status,
                Search = string.IsNullOrWhiteSpace(filter.Search) ? null : filter.Search
            },
            commandType: CommandType.StoredProcedure);
        return rows.ToList();
    }

    public async Task<IllustrationLibrarySummary> GetSummaryAsync()
    {
        using var connection = connectionFactory.CreateConnection();
        return await connection.QuerySingleAsync<IllustrationLibrarySummary>(
            "dbo.IllustrationLibraryItem_Summary",
            commandType: CommandType.StoredProcedure);
    }

    public async Task<IllustrationLibraryItem?> GetAsync(int illustrationLibraryItemId)
    {
        using var connection = connectionFactory.CreateConnection();
        return await connection.QuerySingleOrDefaultAsync<IllustrationLibraryItem>(
            "dbo.IllustrationLibraryItem_Get",
            new { IllustrationLibraryItemID = illustrationLibraryItemId },
            commandType: CommandType.StoredProcedure);
    }

    public async Task<IllustrationLibraryItem> UpsertPlannedAsync(IllustrationGenerationSpecification specification, IllustrationPromptBuildResult prompt)
    {
        using var connection = connectionFactory.CreateConnection();
        return await connection.QuerySingleAsync<IllustrationLibraryItem>(
            "dbo.IllustrationLibraryItem_UpsertPlanned",
            new
            {
                Category = specification.Category,
                StableCode = specification.Code,
                DisplayTitle = specification.Title,
                ShortDescription = specification.Description,
                MetadataJson = prompt.MetadataJson,
                SpecificationJson = prompt.SpecificationJson
            },
            commandType: CommandType.StoredProcedure);
    }

    public async Task<int> QueueAsync(int? illustrationLibraryItemId, string? category)
    {
        using var connection = connectionFactory.CreateConnection();
        return await connection.QuerySingleAsync<int>(
            "dbo.IllustrationLibraryItem_Queue",
            new { IllustrationLibraryItemID = illustrationLibraryItemId, Category = category },
            commandType: CommandType.StoredProcedure);
    }

    public async Task<IllustrationLibraryItem?> ClaimNextAsync()
    {
        using var connection = connectionFactory.CreateConnection();
        return await connection.QuerySingleOrDefaultAsync<IllustrationLibraryItem>(
            "dbo.IllustrationLibraryItem_ClaimNext",
            commandType: CommandType.StoredProcedure);
    }

    public async Task MarkGeneratedAsync(int illustrationLibraryItemId, IllustrationStoredImage image, IllustrationImageGenerationResult providerResult, string prompt, string templateVersion)
    {
        using var connection = connectionFactory.CreateConnection();
        await connection.ExecuteAsync(
            "dbo.IllustrationLibraryItem_MarkGenerated",
            new
            {
                IllustrationLibraryItemID = illustrationLibraryItemId,
                image.FilePath,
                image.ThumbnailPath,
                image.Width,
                image.Height,
                image.FileSizeBytes,
                image.MimeType,
                Provider = providerResult.Provider,
                Model = providerResult.Model,
                PromptText = prompt,
                PromptTemplateVersion = templateVersion,
                ProviderRequestID = providerResult.ProviderRequestID
            },
            commandType: CommandType.StoredProcedure);
    }

    public async Task MarkFailedAsync(int illustrationLibraryItemId, string errorMessage)
    {
        using var connection = connectionFactory.CreateConnection();
        await connection.ExecuteAsync(
            "dbo.IllustrationLibraryItem_MarkFailed",
            new { IllustrationLibraryItemID = illustrationLibraryItemId, LastError = errorMessage },
            commandType: CommandType.StoredProcedure);
    }

    public async Task ApproveAsync(int illustrationLibraryItemId, int userId)
    {
        using var connection = connectionFactory.CreateConnection();
        await connection.ExecuteAsync(
            "dbo.IllustrationLibraryItem_Approve",
            new { IllustrationLibraryItemID = illustrationLibraryItemId, ApprovedByUserID = userId },
            commandType: CommandType.StoredProcedure);
    }

    public async Task RejectAsync(int illustrationLibraryItemId, int userId, string? reason)
    {
        using var connection = connectionFactory.CreateConnection();
        await connection.ExecuteAsync(
            "dbo.IllustrationLibraryItem_Reject",
            new { IllustrationLibraryItemID = illustrationLibraryItemId, RejectedByUserID = userId, RejectionReason = reason },
            commandType: CommandType.StoredProcedure);
    }

    public async Task<IllustrationLibraryItem?> CreateRegenerationAsync(int illustrationLibraryItemId, string? adminNotes)
    {
        using var connection = connectionFactory.CreateConnection();
        return await connection.QuerySingleOrDefaultAsync<IllustrationLibraryItem>(
            "dbo.IllustrationLibraryItem_CreateRegeneration",
            new { IllustrationLibraryItemID = illustrationLibraryItemId, AdminNotes = adminNotes },
            commandType: CommandType.StoredProcedure);
    }

    public async Task DisableAsync(int illustrationLibraryItemId)
    {
        using var connection = connectionFactory.CreateConnection();
        await connection.ExecuteAsync(
            "dbo.IllustrationLibraryItem_Disable",
            new { IllustrationLibraryItemID = illustrationLibraryItemId },
            commandType: CommandType.StoredProcedure);
    }

    public async Task UpdateMetadataAsync(int illustrationLibraryItemId, string displayTitle, string? shortDescription, string? metadataJson, string? adminNotes)
    {
        using var connection = connectionFactory.CreateConnection();
        await connection.ExecuteAsync(
            "dbo.IllustrationLibraryItem_UpdateMetadata",
            new
            {
                IllustrationLibraryItemID = illustrationLibraryItemId,
                DisplayTitle = displayTitle,
                ShortDescription = shortDescription,
                MetadataJson = metadataJson,
                AdminNotes = adminNotes
            },
            commandType: CommandType.StoredProcedure);
    }

    public async Task<IReadOnlyList<IllustrationLibraryItem>> GetApprovedAsync()
    {
        using var connection = connectionFactory.CreateConnection();
        var rows = await connection.QueryAsync<IllustrationLibraryItem>(
            "dbo.IllustrationLibraryItem_GetApproved",
            commandType: CommandType.StoredProcedure);
        return rows.ToList();
    }

    public async Task<int> SupersedeRecoveredFailuresAsync()
    {
        using var connection = connectionFactory.CreateConnection();
        return await connection.ExecuteAsync(
            """
            SET ANSI_NULLS ON;
            SET QUOTED_IDENTIFIER ON;

            UPDATE failed
            SET Status = N'Superseded',
                IsActive = 0,
                UpdatedUtc = SYSUTCDATETIME(),
                AdminNotes = CONCAT(ISNULL(NULLIF(failed.AdminNotes, N''), N''), CASE WHEN NULLIF(failed.AdminNotes, N'') IS NULL THEN N'' ELSE N' ' END, N'Superseded after a replacement illustration generated successfully.')
            FROM dbo.IllustrationLibraryItems failed
            WHERE failed.Status = N'Failed'
                AND failed.IsActive = 1
                AND EXISTS
                (
                    SELECT 1
                    FROM dbo.IllustrationLibraryItems replacement
                    WHERE replacement.IllustrationLibraryItemID <> failed.IllustrationLibraryItemID
                        AND replacement.StableCode = failed.StableCode
                        AND replacement.IsActive = 1
                        AND replacement.Status IN (N'Generated', N'Approved')
                );
            """);
    }
}
