using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using UniVerse.Application.DTOs.Common;
using UniVerse.Application.DTOs.Users;
using UniVerse.Application.Interfaces;
using UniVerse.Domain.Enums;
using System.Security.Claims;
using System.Text;

namespace UniVerse.Api.Controllers;

/// <summary>Управление пользователями, профилями и геймификацией.</summary>
[ApiController]
[Route("api/v1/users")]
[Authorize]
[Produces("application/json")]
public class UsersController : ControllerBase
{
    private readonly IUserService         _users;
    private readonly IReviewService       _reviews;
    private readonly IGamificationService _gamification;

    public UsersController(IUserService users, IReviewService reviews, IGamificationService gamification)
    {
        _users = users; _reviews = reviews; _gamification = gamification;
    }

    private int CurrentUserId => int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub") ?? "0");

    private static CurrentUserDto ToCurrentUserDto(UserDto user) => new(
        user.Id,
        user.Email,
        user.DisplayName,
        user.AvatarUrl,
        user.Roles,
        user.Xp,
        user.Coins,
        user.Level,
        user.CreatedAt);

    /// <summary>Получить профиль текущего пользователя.</summary>
    /// <response code="200">Данные текущего пользователя.</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="404">Пользователь не найден.</response>
    [HttpGet("me")]
    [ProducesResponseType(typeof(CurrentUserDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<CurrentUserDto>> GetMe() =>
        Ok(ToCurrentUserDto(await _users.GetByIdAsync(CurrentUserId)));

    /// <summary>Обновить профиль текущего пользователя (displayName, avatarUrl).</summary>
    /// <param name="req">Обновляемые поля профиля.</param>
    /// <response code="200">Обновлённые данные текущего пользователя.</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="404">Пользователь не найден.</response>
    [HttpPut("me")]
    [ProducesResponseType(typeof(CurrentUserDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<CurrentUserDto>> UpdateMe([FromBody] UpdateUserRequest req) =>
        Ok(ToCurrentUserDto(await _users.UpdateProfileAsync(CurrentUserId, req)));

    /// <summary>Получить статистику текущего пользователя.</summary>
    /// <response code="200">Статистика текущего пользователя.</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="404">Пользователь не найден.</response>
    [HttpGet("me/stats")]
    [ProducesResponseType(typeof(UserStatsDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<UserStatsDto>> MyStats() =>
        Ok(await _users.GetStatsAsync(CurrentUserId));

    /// <summary>Получить список записей текущего пользователя на лекции.</summary>
    /// <param name="pagination">Параметры пагинации.</param>
    /// <response code="200">Список записей (пагинированный).</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="404">Пользователь не найден.</response>
    [HttpGet("me/enrollments")]
    [ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Lectures.LectureDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult> MyEnrollments([FromQuery] PaginationRequest pagination) =>
        Ok(await _users.GetEnrollmentsAsync(CurrentUserId, pagination));

    [HttpGet("me/enrollments/calendar-subscription")]
    [ProducesResponseType(typeof(CalendarSubscriptionDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<ActionResult<CalendarSubscriptionDto>> CalendarSubscription()
    {
        var token = await _users.GetCalendarSubscriptionTokenAsync(CurrentUserId);
        var feedUrl = Url.Action(
            nameof(CalendarEnrollmentsIcs),
            null,
            new { token },
            Request.Scheme)
            ?? $"{Request.Scheme}://{Request.Host}/api/v1/users/calendar/enrollments/{token}.ics";

        return Ok(new CalendarSubscriptionDto(feedUrl));
    }

    [AllowAnonymous]
    [HttpGet("calendar/enrollments/{token}.ics")]
    [Produces("text/calendar")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<FileContentResult> CalendarEnrollmentsIcs(string token)
    {
        var ics = await _users.GetEnrollmentsIcsBySubscriptionTokenAsync(token);
        return File(Encoding.UTF8.GetBytes(ics), "text/calendar; charset=utf-8", "my-lectures.ics");
    }

    [HttpGet("me/enrollments.ics")]
    [Produces("text/calendar")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public async Task<FileContentResult> MyEnrollmentsIcs()
    {
        var ics = await _users.GetMyEnrollmentsIcsAsync(CurrentUserId);
        return File(Encoding.UTF8.GetBytes(ics), "text/calendar; charset=utf-8", "my-lectures.ics");
    }

    [HttpGet("me/enrollments/{lectureId:int}.ics")]
    [Produces("text/calendar")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<FileContentResult> EnrollmentIcs(int lectureId)
    {
        var ics = await _users.GetEnrollmentIcsAsync(CurrentUserId, lectureId);
        return File(Encoding.UTF8.GetBytes(ics), "text/calendar; charset=utf-8", $"lecture-{lectureId}.ics");
    }

    /// <summary>Получить отзывы текущего пользователя.</summary>
    /// <param name="pagination">Параметры пагинации.</param>
    /// <response code="200">Список отзывов (пагинированный).</response>
    /// <response code="401">Требуется аутентификация.</response>
    [HttpGet("me/reviews")]
    [ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Reviews.ReviewDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<ActionResult> MyReviews([FromQuery] PaginationRequest pagination) =>
        Ok(await _reviews.GetByUserAsync(CurrentUserId, pagination));

    /// <summary>Получить достижения текущего пользователя.</summary>
    /// <response code="200">Список полученных достижений.</response>
    /// <response code="401">Требуется аутентификация.</response>
    [HttpGet("me/achievements")]
    [ProducesResponseType(typeof(List<UniVerse.Application.DTOs.Achievements.UserAchievementDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<ActionResult> MyAchievements() =>
        Ok(await _gamification.GetUserAchievementsAsync(CurrentUserId));

    /// <summary>Получить историю транзакций монет текущего пользователя.</summary>
    /// <param name="pagination">Параметры пагинации.</param>
    /// <response code="200">История транзакций (пагинированная).</response>
    /// <response code="401">Требуется аутентификация.</response>
    [HttpGet("me/transactions")]
    [ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Gamification.CoinTransactionDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<ActionResult> MyTransactions([FromQuery] PaginationRequest pagination) =>
        Ok(await _gamification.GetTransactionsAsync(CurrentUserId, pagination));

    /// <summary>Получить профиль пользователя по ID.</summary>
    /// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me.</remarks>
    /// <param name="id">ID пользователя.</param>
    /// <response code="200">Данные пользователя.</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="403">Требуется роль Admin.</response>
    /// <response code="404">Пользователь не найден.</response>
    [Authorize(Roles = "Admin")]
    [HttpGet("{id:int}")]
    [ProducesResponseType(typeof(UserDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<UserDto>> Get(int id) => Ok(await _users.GetByIdAsync(id));

    /// <summary>Обновить профиль пользователя (displayName, avatarUrl).</summary>
    /// <remarks>Только Admin. Для текущего пользователя используйте PUT /api/v1/users/me.</remarks>
    /// <param name="id">ID пользователя.</param>
    /// <param name="req">Обновляемые поля профиля.</param>
    /// <response code="200">Обновлённые данные пользователя.</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="403">Требуется роль Admin.</response>
    /// <response code="404">Пользователь не найден.</response>
    [Authorize(Roles = "Admin")]
    [HttpPut("{id:int}")]
    [ProducesResponseType(typeof(UserDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<UserDto>> Update(int id, [FromBody] UpdateUserRequest req) =>
        Ok(await _users.UpdateProfileAsync(id, req));

    /// <summary>Получить статистику пользователя (XP, монеты, уровень, посещения).</summary>
    /// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/stats.</remarks>
    /// <param name="id">ID пользователя.</param>
    /// <response code="200">Статистика пользователя.</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="403">Требуется роль Admin.</response>
    /// <response code="404">Пользователь не найден.</response>
    [Authorize(Roles = "Admin")]
    [HttpGet("{id:int}/stats")]
    [ProducesResponseType(typeof(UserStatsDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<UserStatsDto>> Stats(int id) => Ok(await _users.GetStatsAsync(id));

    /// <summary>Получить статистику для админского дашборда.</summary>
    /// <remarks>Только Admin.</remarks>
    /// <response code="200">Агрегированная статистика дашборда.</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="403">Требуется роль Admin.</response>
    [Authorize(Roles = "Admin")]
    [HttpGet("admin/stats")]
    [ProducesResponseType(typeof(AdminDashboardStatsDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<ActionResult<AdminDashboardStatsDto>> AdminStats() =>
        Ok(await _users.GetAdminDashboardStatsAsync());

    /// <summary>Получить список записей пользователя на лекции.</summary>
    /// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/enrollments.</remarks>
    /// <param name="id">ID пользователя.</param>
    /// <param name="pagination">Параметры пагинации.</param>
    /// <response code="200">Список записей (пагинированный).</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="403">Требуется роль Admin.</response>
    /// <response code="404">Пользователь не найден.</response>
    [Authorize(Roles = "Admin")]
    [HttpGet("{id:int}/enrollments")]
    [ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Lectures.LectureDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult> Enrollments(int id, [FromQuery] PaginationRequest pagination) =>
        Ok(await _users.GetEnrollmentsAsync(id, pagination));

    /// <summary>Получить отзывы пользователя.</summary>
    /// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/reviews.</remarks>
    /// <param name="id">ID пользователя.</param>
    /// <param name="pagination">Параметры пагинации.</param>
    /// <response code="200">Список отзывов (пагинированный).</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="403">Требуется роль Admin.</response>
    [Authorize(Roles = "Admin")]
    [HttpGet("{id:int}/reviews")]
    [ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Reviews.ReviewDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<ActionResult> Reviews(int id, [FromQuery] PaginationRequest pagination) =>
        Ok(await _reviews.GetByUserAsync(id, pagination));

    /// <summary>Получить достижения пользователя.</summary>
    /// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/achievements.</remarks>
    /// <param name="id">ID пользователя.</param>
    /// <response code="200">Список полученных достижений.</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="403">Требуется роль Admin.</response>
    [Authorize(Roles = "Admin")]
    [HttpGet("{id:int}/achievements")]
    [ProducesResponseType(typeof(List<UniVerse.Application.DTOs.Achievements.UserAchievementDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<ActionResult> Achievements(int id) =>
        Ok(await _gamification.GetUserAchievementsAsync(id));

    /// <summary>Получить историю транзакций монет пользователя.</summary>
    /// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/transactions.</remarks>
    /// <param name="id">ID пользователя.</param>
    /// <param name="pagination">Параметры пагинации.</param>
    /// <response code="200">История транзакций (пагинированная).</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="403">Требуется роль Admin.</response>
    [Authorize(Roles = "Admin")]
    [HttpGet("{id:int}/transactions")]
    [ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Gamification.CoinTransactionDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<ActionResult> Transactions(int id, [FromQuery] PaginationRequest pagination) =>
        Ok(await _gamification.GetTransactionsAsync(id, pagination));

    /// <summary>Получить список всех пользователей с фильтрацией и пагинацией.</summary>
    /// <remarks>Только Admin.</remarks>
    /// <param name="filter">Параметры фильтрации (поиск, роль, активность) и пагинации.</param>
    /// <response code="200">Список пользователей (пагинированный).</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="403">Требуется роль Admin.</response>
    [Authorize(Roles = "Admin")]
    [HttpGet]
    [ProducesResponseType(typeof(PagedResult<UserDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<ActionResult> GetAll([FromQuery] UserFilterRequest filter) =>
        Ok(await _users.GetAllAsync(filter));

    /// <summary>Изменить набор ролей пользователя.</summary>
    /// <remarks>Только Admin. Доступные роли: Student, Teacher, Admin.</remarks>
    /// <param name="id">ID пользователя.</param>
    /// <param name="roles">Новый набор ролей пользователя.</param>
    /// <response code="204">Роли успешно изменены.</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="403">Требуется роль Admin.</response>
    /// <response code="404">Пользователь не найден.</response>
    [Authorize(Roles = "Admin")]
    [HttpPatch("{id:int}/role")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> SetRole(int id, [FromBody] IReadOnlyCollection<UserRole> roles)
    {
        if (roles.Count == 0)
            return BadRequest("At least one role is required.");
        await _users.SetRolesAsync(id, roles);
        return NoContent();
    }

    /// <summary>Активировать или деактивировать аккаунт пользователя.</summary>
    /// <remarks>Только Admin. Деактивированный пользователь не может войти в систему.</remarks>
    /// <param name="id">ID пользователя.</param>
    /// <param name="isActive">true — активировать, false — деактивировать.</param>
    /// <response code="204">Статус успешно изменён.</response>
    /// <response code="401">Требуется аутентификация.</response>
    /// <response code="403">Требуется роль Admin.</response>
    /// <response code="404">Пользователь не найден.</response>
    [Authorize(Roles = "Admin")]
    [HttpPatch("{id:int}/active")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> SetActive(int id, [FromBody] bool isActive)
    {
        await _users.SetActiveAsync(id, isActive);
        return NoContent();
    }
}
