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<WordCompanionChaptersResponse?> ListChaptersAsync(int bookId);
    Task<WordCompanionResolveSceneResponse?> ResolveSceneAsync(int bookId, WordCompanionResolveSceneRequest request);
    Task<WordCompanionSceneCompanionResponse?> GetSceneCompanionAsync(int sceneId);
    Task<WordCompanionCreateChapterResponse?> CreateChapterAsync(int bookId, WordCompanionCreateChapterRequest request);
    Task<WordCompanionCreateSceneResponse?> CreateSceneAsync(int bookId, int chapterId, WordCompanionCreateSceneRequest request);
    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 async Task<WordCompanionChaptersResponse?> ListChaptersAsync(int bookId)
    {
        if (bookId <= 0)
        {
            return null;
        }

        var chapters = await repository.ListChaptersAsync(bookId, RequireUserId());
        return new WordCompanionChaptersResponse { Chapters = chapters };
    }

    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<WordCompanionCreateChapterResponse?> CreateChapterAsync(int bookId, WordCompanionCreateChapterRequest request)
    {
        if (bookId <= 0 || string.IsNullOrWhiteSpace(request.Title) || request.SortOrder is <= 0)
        {
            return Task.FromResult<WordCompanionCreateChapterResponse?>(null);
        }

        request.Title = request.Title.Trim();
        return repository.CreateChapterAsync(bookId, RequireUserId(), request);
    }

    public Task<WordCompanionCreateSceneResponse?> CreateSceneAsync(int bookId, int chapterId, WordCompanionCreateSceneRequest request)
    {
        if (bookId <= 0 || chapterId <= 0 || string.IsNullOrWhiteSpace(request.SceneTitle))
        {
            return Task.FromResult<WordCompanionCreateSceneResponse?>(null);
        }

        request.SceneTitle = request.SceneTitle.Trim();
        return repository.CreateSceneAsync(bookId, chapterId, RequireUserId(), request);
    }

    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
            || request.PrimaryLocationId is <= 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)
    {
        var actualWords = request.ActualWordCount ?? request.ActualWords;
        return sceneId <= 0 || actualWords is null or < 0
            ? Task.FromResult<WordCompanionUpdateWordCountResponse?>(null)
            : repository.UpdateWordCountAsync(sceneId, RequireUserId(), actualWords.Value, request.LastWorkedOn);
    }

    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);
}
