using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using PlotLine.Hubs;
using PlotLine.Models;
using PlotLine.Services;

namespace PlotLine.Controllers;

[ApiController]
[Authorize]
[Route("api/word-companion")]
public sealed class WordCompanionController(
    IWordCompanionService wordCompanion,
    IHubContext<WordCompanionFollowHub> followHub) : ControllerBase
{
    [HttpGet("projects")]
    public Task<WordCompanionProjectsResponse> GetProjects() => wordCompanion.ListProjectsAsync();

    [HttpGet("projects/{projectId:int}/books")]
    public async Task<IActionResult> GetBooks(int projectId)
    {
        var response = await wordCompanion.ListBooksAsync(projectId);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("projects/{projectId:int}/books")]
    public async Task<IActionResult> CreateBook(int projectId, WordCompanionCreateBookRequest request)
    {
        var response = await wordCompanion.CreateBookAsync(projectId, request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpGet("books/{bookId:int}/structure")]
    public async Task<IActionResult> GetBookStructure(int bookId)
    {
        var response = await wordCompanion.GetBookStructureAsync(bookId);
        return response is null ? NotFound() : Ok(response);
    }

    [HttpGet("books/{bookId:int}/chapters")]
    public async Task<IActionResult> GetChapters(int bookId)
    {
        var response = await wordCompanion.ListChaptersAsync(bookId);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("books/{bookId:int}/resolve-scene")]
    public async Task<IActionResult> ResolveScene(int bookId, WordCompanionResolveSceneRequest request)
    {
        var response = await wordCompanion.ResolveSceneAsync(bookId, request);
        if (response is null)
        {
            return BadRequest();
        }

        return response.BookAccessible ? Ok(response) : NotFound(response);
    }

    [HttpGet("scenes/{sceneId:int}/companion")]
    public async Task<IActionResult> GetSceneCompanion(int sceneId)
    {
        var response = await wordCompanion.GetSceneCompanionAsync(sceneId);
        return response is null ? NotFound() : Ok(response);
    }

    [HttpPost("books/{bookId:int}/chapters")]
    public async Task<IActionResult> CreateChapter(int bookId, WordCompanionCreateChapterRequest request)
    {
        var response = await wordCompanion.CreateChapterAsync(bookId, request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("books/{bookId:int}/chapters/{chapterId:int}/scenes")]
    public async Task<IActionResult> CreateScene(int bookId, int chapterId, WordCompanionCreateSceneRequest request)
    {
        var response = await wordCompanion.CreateSceneAsync(bookId, chapterId, request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("books/{bookId:int}/sync-scene")]
    public async Task<IActionResult> SyncScene(int bookId, WordCompanionSyncSceneRequest request)
    {
        var response = await wordCompanion.SyncSceneAsync(bookId, request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("books/{bookId:int}/sync-manuscript")]
    public async Task<IActionResult> SyncManuscript(int bookId, WordCompanionSyncManuscriptRequest request)
    {
        var response = await wordCompanion.SyncManuscriptAsync(bookId, request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPut("scenes/{sceneId:int}/links")]
    public async Task<IActionResult> UpdateSceneLinks(int sceneId, WordCompanionUpdateSceneLinksRequest request)
    {
        var response = await wordCompanion.UpdateSceneLinksAsync(sceneId, request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPatch("scenes/{sceneId:int}/word-count")]
    public async Task<IActionResult> UpdateWordCount(int sceneId, WordCompanionUpdateWordCountRequest request)
    {
        var response = await wordCompanion.UpdateWordCountAsync(sceneId, request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpGet("runtime/book/{bookId:int}")]
    public async Task<IActionResult> GetRuntimeBook(int bookId, [FromQuery] Guid documentGuid)
    {
        var response = await wordCompanion.GetRuntimeBookAsync(bookId, documentGuid);
        return response is null ? NotFound() : Ok(response);
    }

    [HttpPost("runtime/scene-word-count")]
    public async Task<IActionResult> UpdateRuntimeSceneWordCount(WordCompanionRuntimeSceneWordCountRequest request)
    {
        var response = await wordCompanion.UpdateRuntimeSceneWordCountAsync(request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("runtime/current-scene")]
    public async Task<IActionResult> UpdateRuntimeCurrentScene(WordCompanionRuntimeCurrentSceneRequest request)
    {
        var response = await wordCompanion.ValidateRuntimeCurrentSceneAsync(request);
        if (response is null)
        {
            return BadRequest();
        }

        await BroadcastToCurrentUserAsync("wordCompanionSceneChanged", new
        {
            response.ProjectId,
            response.BookId,
            response.ChapterId,
            response.SceneId,
            response.SceneUrl
        });

        return Ok(response);
    }

    [HttpGet("runtime/characters")]
    public async Task<IActionResult> GetRuntimeCharacters([FromQuery] int projectId, [FromQuery] Guid documentGuid)
    {
        var response = await wordCompanion.ListRuntimeCharacterAliasesAsync(projectId, documentGuid);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("runtime/character-suggestions")]
    public async Task<IActionResult> CreateRuntimeCharacterSuggestions(WordCompanionRuntimeCharacterSuggestionsRequest request)
    {
        var response = await wordCompanion.CreateRuntimeCharacterSuggestionsAsync(request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpGet("runtime/assets")]
    public async Task<IActionResult> GetRuntimeAssets([FromQuery] int projectId, [FromQuery] Guid documentGuid)
    {
        var response = await wordCompanion.ListRuntimeAssetAliasesAsync(projectId, documentGuid);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("runtime/asset-suggestions")]
    public async Task<IActionResult> CreateRuntimeAssetSuggestions(WordCompanionRuntimeAssetSuggestionsRequest request)
    {
        var response = await wordCompanion.CreateRuntimeAssetSuggestionsAsync(request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpGet("runtime/locations")]
    public async Task<IActionResult> GetRuntimeLocations([FromQuery] int projectId, [FromQuery] Guid documentGuid)
    {
        var response = await wordCompanion.ListRuntimeLocationAliasesAsync(projectId, documentGuid);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("runtime/location-suggestions")]
    public async Task<IActionResult> CreateRuntimeLocationSuggestions(WordCompanionRuntimeLocationSuggestionsRequest request)
    {
        var response = await wordCompanion.CreateRuntimeLocationSuggestionsAsync(request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpGet("manuscript/book/{bookId:int}")]
    public async Task<IActionResult> GetManuscriptForBook(int bookId)
    {
        var response = await wordCompanion.GetManuscriptForBookAsync(bookId);
        return response is null ? NotFound() : Ok(response);
    }

    [HttpPost("manuscript/link")]
    public async Task<IActionResult> LinkManuscript(WordCompanionManuscriptLinkRequest request)
    {
        var response = await wordCompanion.LinkManuscriptAsync(request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("manuscript/unlink")]
    public async Task<IActionResult> UnlinkManuscript(WordCompanionManuscriptUnlinkRequest request)
    {
        var response = await wordCompanion.UnlinkManuscriptAsync(request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("manuscript/analyse")]
    public async Task<IActionResult> AnalyseManuscript(WordCompanionManuscriptAnalyseRequest request)
    {
        var response = await wordCompanion.AnalyseManuscriptAsync(request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("manuscript/import")]
    public async Task<IActionResult> ImportManuscript(WordCompanionManuscriptImportRequest request)
    {
        var response = await wordCompanion.ImportManuscriptAsync(request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("manuscript/discover-characters")]
    public async Task<IActionResult> DiscoverCharacters(WordCompanionDiscoverCharactersRequest request)
    {
        var response = await wordCompanion.DiscoverCharacterCandidatesAsync(request);
        return response is null ? BadRequest() : Ok(response);
    }

    [HttpPost("manuscript/create-discovered-characters")]
    public async Task<IActionResult> CreateDiscoveredCharacters(WordCompanionCreateDiscoveredCharactersRequest request)
    {
        var response = await wordCompanion.CreateDiscoveredCharactersAsync(request);
        if (response is null)
        {
            return BadRequest();
        }

        if (response.CreatedCount > 0)
        {
            await BroadcastToCurrentUserAsync("wordCompanionCharactersCreated", new
            {
                response.ProjectId,
                CharactersUrl = Url.Action("Index", "Characters", new { projectId = response.ProjectId }) ?? $"/Characters?projectId={response.ProjectId}"
            });
        }

        return Ok(response);
    }

    private async Task BroadcastToCurrentUserAsync(string eventName, object payload)
    {
        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (!string.IsNullOrWhiteSpace(userId))
        {
            await followHub.Clients.User(userId).SendAsync(eventName, payload);
        }
    }
}
