128 lines
5.5 KiB
C#
128 lines
5.5 KiB
C#
using PlotLine.Data;
|
|
using PlotLine.Models;
|
|
|
|
namespace PlotLine.Services;
|
|
|
|
public interface IWordCompanionService
|
|
{
|
|
Task<WordCompanionProjectsResponse> ListProjectsAsync();
|
|
Task<WordCompanionBooksResponse?> ListBooksAsync(int projectId);
|
|
Task<WordCompanionBookStructureResponse?> GetBookStructureAsync(int bookId);
|
|
Task<WordCompanionResolveSceneResponse?> ResolveSceneAsync(int bookId, WordCompanionResolveSceneRequest request);
|
|
Task<WordCompanionSceneCompanionResponse?> GetSceneCompanionAsync(int sceneId);
|
|
Task<WordCompanionSyncSceneResponse?> SyncSceneAsync(int bookId, WordCompanionSyncSceneRequest request);
|
|
Task<WordCompanionSyncManuscriptResponse?> SyncManuscriptAsync(int bookId, WordCompanionSyncManuscriptRequest request);
|
|
Task<WordCompanionUpdateSceneLinksResponse?> UpdateSceneLinksAsync(int sceneId, WordCompanionUpdateSceneLinksRequest request);
|
|
Task<WordCompanionUpdateWordCountResponse?> UpdateWordCountAsync(int sceneId, WordCompanionUpdateWordCountRequest request);
|
|
}
|
|
|
|
public sealed class WordCompanionService(IWordCompanionRepository repository, ICurrentUserService currentUser) : IWordCompanionService
|
|
{
|
|
public async Task<WordCompanionProjectsResponse> ListProjectsAsync()
|
|
{
|
|
var userId = RequireUserId();
|
|
return new WordCompanionProjectsResponse { Projects = await repository.ListProjectsAsync(userId) };
|
|
}
|
|
|
|
public async Task<WordCompanionBooksResponse?> ListBooksAsync(int projectId)
|
|
{
|
|
if (projectId <= 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var books = await repository.ListBooksAsync(projectId, RequireUserId());
|
|
return new WordCompanionBooksResponse { Books = books };
|
|
}
|
|
|
|
public Task<WordCompanionBookStructureResponse?> GetBookStructureAsync(int bookId)
|
|
{
|
|
return bookId <= 0 ? Task.FromResult<WordCompanionBookStructureResponse?>(null) : repository.GetBookStructureAsync(bookId, RequireUserId());
|
|
}
|
|
|
|
public Task<WordCompanionResolveSceneResponse?> ResolveSceneAsync(int bookId, WordCompanionResolveSceneRequest request)
|
|
{
|
|
if (bookId <= 0 || string.IsNullOrWhiteSpace(request.ChapterTitle) || string.IsNullOrWhiteSpace(request.SceneTitle))
|
|
{
|
|
return Task.FromResult<WordCompanionResolveSceneResponse?>(null);
|
|
}
|
|
|
|
return ResolveSceneCoreAsync(bookId, request);
|
|
}
|
|
|
|
public Task<WordCompanionSceneCompanionResponse?> GetSceneCompanionAsync(int sceneId)
|
|
{
|
|
return sceneId <= 0 ? Task.FromResult<WordCompanionSceneCompanionResponse?>(null) : repository.GetSceneCompanionAsync(sceneId, RequireUserId());
|
|
}
|
|
|
|
public Task<WordCompanionSyncSceneResponse?> SyncSceneAsync(int bookId, WordCompanionSyncSceneRequest request)
|
|
{
|
|
if (bookId <= 0
|
|
|| string.IsNullOrWhiteSpace(request.ChapterTitle)
|
|
|| string.IsNullOrWhiteSpace(request.SceneTitle)
|
|
|| request.ActualWords < 0
|
|
|| request.ChapterSortOrder is <= 0
|
|
|| request.SceneSortOrder is <= 0)
|
|
{
|
|
return Task.FromResult<WordCompanionSyncSceneResponse?>(null);
|
|
}
|
|
|
|
request.ChapterTitle = request.ChapterTitle.Trim();
|
|
request.SceneTitle = request.SceneTitle.Trim();
|
|
return repository.SyncSceneAsync(bookId, RequireUserId(), request);
|
|
}
|
|
|
|
public Task<WordCompanionSyncManuscriptResponse?> SyncManuscriptAsync(int bookId, WordCompanionSyncManuscriptRequest request)
|
|
{
|
|
if (bookId <= 0 || request.Chapters.Any(chapter =>
|
|
string.IsNullOrWhiteSpace(chapter.Title)
|
|
|| chapter.WordCount < 0
|
|
|| chapter.SortOrder is <= 0
|
|
|| chapter.Scenes.Any(scene => string.IsNullOrWhiteSpace(scene.Title) || scene.WordCount < 0 || scene.SortOrder is <= 0)))
|
|
{
|
|
return Task.FromResult<WordCompanionSyncManuscriptResponse?>(null);
|
|
}
|
|
|
|
foreach (var chapter in request.Chapters)
|
|
{
|
|
chapter.Title = chapter.Title?.Trim();
|
|
foreach (var scene in chapter.Scenes)
|
|
{
|
|
scene.Title = scene.Title?.Trim();
|
|
}
|
|
}
|
|
|
|
return repository.SyncManuscriptAsync(bookId, RequireUserId(), request);
|
|
}
|
|
|
|
public Task<WordCompanionUpdateSceneLinksResponse?> UpdateSceneLinksAsync(int sceneId, WordCompanionUpdateSceneLinksRequest request)
|
|
{
|
|
if (sceneId <= 0 || HasInvalidIds(request.CharacterIds) || HasInvalidIds(request.AssetIds) || HasInvalidIds(request.LocationIds))
|
|
{
|
|
return Task.FromResult<WordCompanionUpdateSceneLinksResponse?>(null);
|
|
}
|
|
|
|
return repository.UpdateSceneLinksAsync(sceneId, RequireUserId(), request);
|
|
}
|
|
|
|
public Task<WordCompanionUpdateWordCountResponse?> UpdateWordCountAsync(int sceneId, WordCompanionUpdateWordCountRequest request)
|
|
{
|
|
return sceneId <= 0 || request.ActualWords < 0
|
|
? Task.FromResult<WordCompanionUpdateWordCountResponse?>(null)
|
|
: repository.UpdateWordCountAsync(sceneId, RequireUserId(), request.ActualWords);
|
|
}
|
|
|
|
private async Task<WordCompanionResolveSceneResponse?> ResolveSceneCoreAsync(int bookId, WordCompanionResolveSceneRequest request)
|
|
{
|
|
return await repository.ResolveSceneAsync(bookId, RequireUserId(), request.ChapterTitle!.Trim(), request.SceneTitle!.Trim());
|
|
}
|
|
|
|
private int RequireUserId()
|
|
{
|
|
return currentUser.UserId ?? throw new InvalidOperationException("An authenticated user is required.");
|
|
}
|
|
|
|
private static bool HasInvalidIds(IEnumerable<int> ids) => ids.Any(id => id <= 0);
|
|
}
|
|
|