using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using API.Data;
using API.Data.Repositories;
using API.DTOs.CollectionTags;
using API.Entities.Metadata;
using API.Extensions;
using API.Services;
using Kavita.Common;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers;
#nullable enable
/// 
/// APIs for Collections
/// 
public class CollectionController : BaseApiController
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly ICollectionTagService _collectionService;
    private readonly ILocalizationService _localizationService;
    /// 
    public CollectionController(IUnitOfWork unitOfWork, ICollectionTagService collectionService,
        ILocalizationService localizationService)
    {
        _unitOfWork = unitOfWork;
        _collectionService = collectionService;
        _localizationService = localizationService;
    }
    /// 
    /// Return a list of all collection tags on the server for the logged in user.
    /// 
    /// 
    [HttpGet]
    public async Task>> GetAllTags()
    {
        var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
        if (user == null) return Unauthorized();
        var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
        if (isAdmin)
        {
            return Ok(await _unitOfWork.CollectionTagRepository.GetAllTagDtosAsync());
        }
        return Ok(await _unitOfWork.CollectionTagRepository.GetAllPromotedTagDtosAsync(user.Id));
    }
    /// 
    /// Searches against the collection tags on the DB and returns matches that meet the search criteria.
    /// Search strings will be cleaned of certain fields, like %
    /// 
    /// Search term
    /// 
    [Authorize(Policy = "RequireAdminRole")]
    [HttpGet("search")]
    public async Task>> SearchTags(string? queryString)
    {
        queryString ??= string.Empty;
        queryString = queryString.Replace(@"%", string.Empty);
        if (queryString.Length == 0) return await GetAllTags();
        return Ok(await _unitOfWork.CollectionTagRepository.SearchTagDtosAsync(queryString, User.GetUserId()));
    }
    /// 
    /// Checks if a collection exists with the name
    /// 
    /// If empty or null, will return true as that is invalid
    /// 
    [Authorize(Policy = "RequireAdminRole")]
    [HttpGet("name-exists")]
    public async Task> DoesNameExists(string name)
    {
        return Ok(await _collectionService.TagExistsByName(name));
    }
    /// 
    /// Updates an existing tag with a new title, promotion status, and summary.
    /// UI does not contain controls to update title
    /// 
    /// 
    /// 
    [Authorize(Policy = "RequireAdminRole")]
    [HttpPost("update")]
    public async Task UpdateTag(CollectionTagDto updatedTag)
    {
        try
        {
            if (await _collectionService.UpdateTag(updatedTag)) return Ok(await _localizationService.Translate(User.GetUserId(), "collection-updated-successfully"));
        }
        catch (KavitaException ex)
        {
            return BadRequest(await _localizationService.Translate(User.GetUserId(), ex.Message));
        }
        return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-error"));
    }
    /// 
    /// Adds a collection tag onto multiple Series. If tag id is 0, this will create a new tag.
    /// 
    /// 
    /// 
    [Authorize(Policy = "RequireAdminRole")]
    [HttpPost("update-for-series")]
    public async Task AddToMultipleSeries(CollectionTagBulkAddDto dto)
    {
        // Create a new tag and save
        var tag = await _collectionService.GetTagOrCreate(dto.CollectionTagId, dto.CollectionTagTitle);
        if (await _collectionService.AddTagToSeries(tag, dto.SeriesIds)) return Ok();
        return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-error"));
    }
    /// 
    /// For a given tag, update the summary if summary has changed and remove a set of series from the tag.
    /// 
    /// 
    /// 
    [Authorize(Policy = "RequireAdminRole")]
    [HttpPost("update-series")]
    public async Task RemoveTagFromMultipleSeries(UpdateSeriesForTagDto updateSeriesForTagDto)
    {
        try
        {
            var tag = await _unitOfWork.CollectionTagRepository.GetTagAsync(updateSeriesForTagDto.Tag.Id, CollectionTagIncludes.SeriesMetadata);
            if (tag == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "collection-doesnt-exist"));
            if (await _collectionService.RemoveTagFromSeries(tag, updateSeriesForTagDto.SeriesIdsToRemove))
                return Ok(await _localizationService.Translate(User.GetUserId(), "collection-updated"));
        }
        catch (Exception)
        {
            await _unitOfWork.RollbackAsync();
        }
        return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-error"));
    }
    /// 
    /// Removes the collection tag from all Series it was attached to
    /// 
    /// 
    /// 
    [Authorize(Policy = "RequireAdminRole")]
    [HttpDelete]
    public async Task DeleteTag(int tagId)
    {
        try
        {
            var tag = await _unitOfWork.CollectionTagRepository.GetTagAsync(tagId, CollectionTagIncludes.SeriesMetadata);
            if (tag == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "collection-doesnt-exist"));
            if (await _collectionService.DeleteTag(tag))
                return Ok(await _localizationService.Translate(User.GetUserId(), "collection-deleted"));
        }
        catch (Exception)
        {
            await _unitOfWork.RollbackAsync();
        }
        return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-error"));
    }
}