using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Kavita.API.Database;
using Kavita.API.Services;
using Kavita.API.Services.Reading;
using Kavita.Common;
using Kavita.Models.Constants;
using Kavita.Models.DTOs;
using Kavita.Server.Attributes;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace Kavita.Server.Controllers;

public record BulkSetSeriesProfiles(List<int> ProfileIds, List<int> SeriesIds);

[Route("api/reading-profile")]
public class ReadingProfileController(ILogger<ReadingProfileController> logger, IUnitOfWork unitOfWork,
    IReadingProfileService readingProfileService, IClientInfoAccessor clientInfoAccessor): BaseApiController
{

    /// <summary>
    /// Gets all non-implicit reading profiles for a user
    /// </summary>
    /// <returns></returns>
    [HttpGet("all")]
    public async Task<ActionResult<IList<UserReadingProfileDto>>> GetAllReadingProfiles()
    {
        return Ok(await unitOfWork.AppUserReadingProfileRepository.GetProfilesDtoForUser(UserId, true));
    }

    /// <summary>
    /// Returns the ReadingProfile that should be applied to the given series, walks up the tree.
    /// Series -> Library -> Default
    /// </summary>
    /// <param name="libraryId"></param>
    /// <param name="seriesId"></param>
    /// <param name="skipImplicit"></param>
    /// <param name="deviceId">Defaults to currently active device</param>
    /// <returns></returns>
    [SeriesAccess]
    [HttpGet("{libraryId:int}/{seriesId:int}")]
    public async Task<ActionResult<UserReadingProfileDto>> GetProfileForSeries(int libraryId, int seriesId, [FromQuery] bool skipImplicit, [FromQuery] int? deviceId = null)
    {
        deviceId ??= clientInfoAccessor.CurrentDeviceId;

        return Ok(await readingProfileService.GetReadingProfileDtoForSeries(UserId, libraryId, seriesId, deviceId, skipImplicit));
    }

    /// <summary>
    /// Returns all Reading Profiles bound to a series
    /// </summary>
    /// <param name="seriesId"></param>
    /// <returns></returns>
    [SeriesAccess]
    [HttpGet("series")]
    public async Task<ActionResult<List<UserReadingProfileDto>>> GetProfilesForSeries(int seriesId)
    {
        return Ok(await readingProfileService.GetReadingProfileDtosForSeries(UserId, seriesId));
    }

    /// <summary>
    /// Returns all the Reading rofiles bound to the library
    /// </summary>
    /// <param name="libraryId"></param>
    /// <returns></returns>
    [LibraryAccess]
    [HttpGet("library")]
    public async Task<ActionResult<List<UserReadingProfileDto>>> GetProfilesForLibrary(int libraryId)
    {
        return Ok(await readingProfileService.GetReadingProfileDtosForLibrary(UserId, libraryId));
    }

    /// <summary>
    /// Creates a new reading profile for the current user
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpPost("create")]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<ActionResult<UserReadingProfileDto>> CreateReadingProfile([FromBody] UserReadingProfileDto dto)
    {
        return Ok(await readingProfileService.CreateReadingProfile(UserId, dto));
    }

    /// <summary>
    /// Promotes the implicit profile to a user profile. Removes the series from other profiles
    /// </summary>
    /// <param name="profileId"></param>
    /// <param name="deviceId">Defaults to the currently active device</param>
    /// <returns></returns>
    [HttpPost("promote")]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<ActionResult<UserReadingProfileDto>> PromoteImplicitReadingProfile([FromQuery] int profileId, [FromQuery] int? deviceId = null)
    {
        deviceId ??= clientInfoAccessor.CurrentDeviceId;

        return Ok(await readingProfileService.PromoteImplicitProfile(UserId, profileId, deviceId));
    }

    /// <summary>
    /// Update the implicit reading profile for a series, creates one if none exists
    /// </summary>
    /// <remarks>Any modification to the reader settings during reading will create an implicit profile. Use "update-parent" to save to the bound series profile.</remarks>
    /// <param name="dto"></param>
    /// <param name="libraryId"></param>
    /// <param name="seriesId"></param>
    /// <param name="deviceId">Defaults to the currently active device</param>
    /// <returns></returns>
    [SeriesAccess]
    [HttpPost("series")]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<ActionResult<UserReadingProfileDto>> UpdateReadingProfileForSeries(
        [FromBody] UserReadingProfileDto dto, [FromQuery] int libraryId, [FromQuery] int seriesId, [FromQuery] int? deviceId = null)
    {
        deviceId ??= clientInfoAccessor.CurrentDeviceId;

        var updatedProfile = await readingProfileService.UpdateImplicitReadingProfile(UserId, libraryId, seriesId, dto, deviceId);
        return Ok(updatedProfile);
    }

    /// <summary>
    /// Updates the non-implicit reading profile for the given series, and removes implicit profiles
    /// </summary>
    /// <param name="dto"></param>
    /// <param name="libraryId"></param>
    /// <param name="seriesId"></param>
    /// <param name="deviceId">Defaults to currently active device</param>
    /// <returns></returns>
    [HttpPost("update-parent")]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<ActionResult<UserReadingProfileDto>> UpdateParentProfileForSeries(
        [FromBody] UserReadingProfileDto dto, [FromQuery] int libraryId, [FromQuery] int seriesId, [FromQuery] int? deviceId = null)
    {
        deviceId ??= clientInfoAccessor.CurrentDeviceId;

        var newParentProfile = await readingProfileService.UpdateParent(UserId, libraryId, seriesId, dto, deviceId);
        return Ok(newParentProfile);
    }

    /// <summary>
    /// Updates the given reading profile, must belong to the current user
    /// </summary>
    /// <param name="dto"></param>
    /// <returns>The updated reading profile</returns>
    /// <remarks>
    /// This does not update connected series and libraries.
    /// </remarks>
    [HttpPost]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<ActionResult<UserReadingProfileDto>> UpdateReadingProfile(UserReadingProfileDto dto)
    {
        return Ok(await readingProfileService.UpdateReadingProfile(UserId, dto));
    }

    /// <summary>
    /// Deletes the given profile, requires the profile to belong to the logged-in user
    /// </summary>
    /// <param name="profileId"></param>
    /// <returns></returns>
    /// <exception cref="KavitaException"></exception>
    /// <exception cref="UnauthorizedAccessException"></exception>
    [HttpDelete]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<IActionResult> DeleteReadingProfile([FromQuery] int profileId)
    {
        await readingProfileService.DeleteReadingProfile(UserId, profileId);
        return Ok();
    }

    /// <summary>
    /// Sets the reading profile for a given series, removes the old one
    /// </summary>
    /// <param name="seriesId"></param>
    /// <param name="profileIds"></param>
    /// <returns></returns>
    [SeriesAccess]
    [HttpPost("series/{seriesId:int}")]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<IActionResult> SetSeriesProfiles(int seriesId, List<int> profileIds)
    {
        await readingProfileService.SetSeriesProfiles(UserId, profileIds, seriesId);
        return Ok();
    }

    /// <summary>
    /// Clears the reading profile for the given series for the currently logged-in user
    /// </summary>
    /// <param name="seriesId"></param>
    /// <returns></returns>
    [SeriesAccess]
    [HttpDelete("series/{seriesId:int}")]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<IActionResult> ClearSeriesProfile(int seriesId)
    {
        await readingProfileService.ClearSeriesProfile(UserId, seriesId);
        return Ok();
    }

    /// <summary>
    /// Sets the reading profile for a given library, removes the old one
    /// </summary>
    /// <param name="libraryId"></param>
    /// <param name="profileIds"></param>
    /// <returns></returns>
    [LibraryAccess]
    [HttpPost("library/{libraryId:int}")]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<IActionResult> SetLibraryProfiles(int libraryId, List<int> profileIds)
    {
        await readingProfileService.SetLibraryProfiles(UserId, profileIds, libraryId);
        return Ok();
    }

    /// <summary>
    /// Clears the reading profile for the given library for the currently logged-in user
    /// </summary>
    /// <param name="libraryId"></param>
    /// <returns></returns>
    [LibraryAccess]
    [HttpDelete("library/{libraryId:int}")]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<IActionResult> ClearLibraryProfile(int libraryId)
    {
        await readingProfileService.ClearLibraryProfile(UserId, libraryId);
        return Ok();
    }

    /// <summary>
    /// Assigns the reading profile to all passes series, and deletes their implicit profiles
    /// </summary>
    /// <param name="body"></param>
    /// <returns></returns>
    [HttpPost("bulk")]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<IActionResult> BulkAddReadingProfile(BulkSetSeriesProfiles body)
    {
        await readingProfileService.BulkSetSeriesProfiles(UserId, body.ProfileIds, body.SeriesIds);
        return Ok();
    }

    /// <summary>
    /// Set the assigned devices for a reading profile
    /// </summary>
    /// <param name="profileId"></param>
    /// <param name="deviceIds"></param>
    /// <returns></returns>
    [HttpPost("set-devices")]
    [DisallowRole(PolicyConstants.ReadOnlyRole)]
    public async Task<IActionResult> SetProfileDevices([FromQuery] int profileId, [FromBody] List<int> deviceIds)
    {
        await readingProfileService.SetProfileDevices(UserId, profileId, deviceIds);

        return Ok();

    }

}
