using Dapper; using PlotLine.Models; namespace PlotLine.Data; public interface IStoryIntelligenceIllustrationMatchingRepository { Task> ListAssignmentsAsync(int projectId); Task UpsertAssignmentAsync(StoryIntelligenceCharacterIllustrationAssignmentSave request); Task UpsertDemandAsync(StoryIntelligenceIllustrationDemandRequest request); } public sealed class StoryIntelligenceIllustrationMatchingRepository(ISqlConnectionFactory connectionFactory) : IStoryIntelligenceIllustrationMatchingRepository { public async Task> ListAssignmentsAsync(int projectId) { using var connection = connectionFactory.CreateConnection(); var rows = await connection.QueryAsync( """ SELECT StoryIntelligenceCharacterIllustrationAssignmentID, ProjectID, CharacterKey, CharacterName, IllustrationLibraryItemID, StableCode, MatchConfidence, MatchingScore, CAST(NULL AS nvarchar(max)) AS EvidenceJson, CAST(NULL AS nvarchar(max)) AS CandidateDiagnosticsJson, ReasonChosen, ReasonReassigned, IsActive, CreatedUtc, UpdatedUtc, AssignmentStatus, AssignedUtc, LastValidatedUtc, EvidenceVersion, IsFallback FROM dbo.StoryIntelligenceCharacterIllustrationAssignments WHERE ProjectID = @ProjectID AND IsActive = 1; """, new { ProjectID = projectId }); return rows.ToList(); } public async Task UpsertAssignmentAsync(StoryIntelligenceCharacterIllustrationAssignmentSave request) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleAsync( """ SET XACT_ABORT ON; MERGE dbo.StoryIntelligenceCharacterIllustrationAssignments WITH (HOLDLOCK) AS target USING (SELECT @ProjectID AS ProjectID, @CharacterKey AS CharacterKey) AS source ON target.ProjectID = source.ProjectID AND target.CharacterKey = source.CharacterKey AND target.IsActive = 1 WHEN MATCHED THEN UPDATE SET CharacterName = @CharacterName, IllustrationLibraryItemID = @IllustrationLibraryItemID, StableCode = @StableCode, MatchConfidence = @MatchConfidence, MatchingScore = @MatchingScore, EvidenceJson = @EvidenceJson, CandidateDiagnosticsJson = @CandidateDiagnosticsJson, ReasonChosen = @ReasonChosen, ReasonReassigned = @ReasonReassigned, AssignmentStatus = CASE WHEN @IllustrationLibraryItemID IS NULL THEN N'Fallback' ELSE N'Assigned' END, LastValidatedUtc = SYSUTCDATETIME(), EvidenceVersion = N'21K', IsFallback = CASE WHEN @IllustrationLibraryItemID IS NULL THEN 1 ELSE 0 END, UpdatedUtc = SYSUTCDATETIME() WHEN NOT MATCHED THEN INSERT (ProjectID, CharacterKey, CharacterName, IllustrationLibraryItemID, StableCode, MatchConfidence, MatchingScore, EvidenceJson, CandidateDiagnosticsJson, ReasonChosen, ReasonReassigned, AssignmentStatus, AssignedUtc, LastValidatedUtc, EvidenceVersion, IsFallback) VALUES (@ProjectID, @CharacterKey, @CharacterName, @IllustrationLibraryItemID, @StableCode, @MatchConfidence, @MatchingScore, @EvidenceJson, @CandidateDiagnosticsJson, @ReasonChosen, @ReasonReassigned, CASE WHEN @IllustrationLibraryItemID IS NULL THEN N'Fallback' ELSE N'Assigned' END, SYSUTCDATETIME(), SYSUTCDATETIME(), N'21K', CASE WHEN @IllustrationLibraryItemID IS NULL THEN 1 ELSE 0 END); SELECT * FROM dbo.StoryIntelligenceCharacterIllustrationAssignments WHERE ProjectID = @ProjectID AND CharacterKey = @CharacterKey AND IsActive = 1; """, request); } public async Task UpsertDemandAsync(StoryIntelligenceIllustrationDemandRequest request) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleAsync( """ SET XACT_ABORT ON; MERGE dbo.StoryIntelligenceIllustrationDemands WITH (HOLDLOCK) AS target USING (SELECT @ProjectID AS ProjectID, @DemandKey AS DemandKey) AS source ON ISNULL(target.ProjectID, -1) = ISNULL(source.ProjectID, -1) AND target.DemandKey = source.DemandKey WHEN MATCHED THEN UPDATE SET RequestCount = RequestCount + 1, LastRequestedUtc = SYSUTCDATETIME(), UpdatedUtc = SYSUTCDATETIME() WHEN NOT MATCHED THEN INSERT (ProjectID, DemandKey, AgeBand, Presentation, HairColour, SkinTone, HairLength, Glasses, FacialHair) VALUES (@ProjectID, @DemandKey, @AgeBand, @Presentation, @HairColour, @SkinTone, @HairLength, @Glasses, @FacialHair); SELECT * FROM dbo.StoryIntelligenceIllustrationDemands WHERE ISNULL(ProjectID, -1) = ISNULL(@ProjectID, -1) AND DemandKey = @DemandKey; """, request); } }