namespace Adapters.Driving.Api.Types;

using Application.Auth.Commands;
using Application.Enrollments.Commands;
using Application.Students.Commands;
using Application.Students.DTOs;
using HotChocolate.Authorization;
using MediatR;
using System.Security.Claims;

/// <summary>
/// GraphQL mutation operations for the student enrollment system.
/// All mutations require authentication. Some require specific roles.
/// </summary>
public class Mutation
{
    [Authorize(Roles = ["Admin"])]
    [GraphQLDescription("Create a new student with activation code (admin only)")]
    public async Task<CreateStudentWithActivationPayload> CreateStudent(
        CreateStudentInput input,
        [Service] IMediator mediator,
        CancellationToken ct)
    {
        var result = await mediator.Send(new CreateStudentCommand(input.Name, input.Email), ct);
        return new CreateStudentWithActivationPayload(result);
    }

    [Authorize(Roles = ["Admin"])]
    [GraphQLDescription("Regenerate activation code for a student (admin only)")]
    public async Task<CreateStudentWithActivationPayload?> RegenerateActivationCode(
        int studentId,
        [Service] IMediator mediator,
        CancellationToken ct)
    {
        var result = await mediator.Send(new RegenerateActivationCodeCommand(studentId), ct);
        return result != null ? new CreateStudentWithActivationPayload(result) : null;
    }

    [GraphQLDescription("Activate a student account using activation code (public)")]
    public async Task<ActivateAccountPayload> ActivateAccount(
        ActivateAccountInput input,
        [Service] IMediator mediator,
        CancellationToken ct)
    {
        var result = await mediator.Send(
            new ActivateAccountCommand(input.ActivationCode, input.Username, input.Password), ct);
        return new ActivateAccountPayload(result.Success, result.Token, result.RecoveryCode, result.Error);
    }

    [Authorize]
    [GraphQLDescription("Update an existing student (owner or admin only)")]
    public async Task<UpdateStudentPayload> UpdateStudent(
        int id,
        UpdateStudentInput input,
        ClaimsPrincipal user,
        [Service] IMediator mediator,
        CancellationToken ct)
    {
        // Check if user can modify this student
        if (!CanModifyStudent(user, id))
            return new UpdateStudentPayload(null, [new MutationError("FORBIDDEN", "No tienes permiso para modificar este estudiante")]);

        var result = await mediator.Send(new UpdateStudentCommand(id, input.Name, input.Email), ct);
        return new UpdateStudentPayload(result);
    }

    [Authorize(Roles = ["Admin"])]
    [GraphQLDescription("Delete a student (admin only)")]
    public async Task<DeleteStudentPayload> DeleteStudent(
        int id,
        [Service] IMediator mediator,
        CancellationToken ct)
    {
        var success = await mediator.Send(new DeleteStudentCommand(id), ct);
        return new DeleteStudentPayload(success);
    }

    [Authorize]
    [GraphQLDescription("Enroll a student in a subject (owner or admin only)")]
    public async Task<EnrollStudentPayload> EnrollStudent(
        EnrollStudentInput input,
        ClaimsPrincipal user,
        [Service] IMediator mediator,
        CancellationToken ct)
    {
        // Check if user can enroll this student
        if (!CanModifyStudent(user, input.StudentId))
            return new EnrollStudentPayload(null, [new MutationError("FORBIDDEN", "No tienes permiso para inscribir a este estudiante")]);

        var result = await mediator.Send(new EnrollStudentCommand(input.StudentId, input.SubjectId), ct);
        return new EnrollStudentPayload(result);
    }

    [Authorize]
    [GraphQLDescription("Unenroll a student from a subject (owner or admin only)")]
    public async Task<UnenrollStudentPayload> UnenrollStudent(
        int enrollmentId,
        int studentId,
        ClaimsPrincipal user,
        [Service] IMediator mediator,
        CancellationToken ct)
    {
        // Check if user can modify this student's enrollments
        if (!CanModifyStudent(user, studentId))
            return new UnenrollStudentPayload(false, [new MutationError("FORBIDDEN", "No tienes permiso para desinscribir a este estudiante")]);

        var success = await mediator.Send(new UnenrollStudentCommand(enrollmentId), ct);
        return new UnenrollStudentPayload(success);
    }

    /// <summary>
    /// Checks if the current user can modify the specified student.
    /// Admins can modify any student. Students can only modify their own data.
    /// </summary>
    private static bool CanModifyStudent(ClaimsPrincipal user, int studentId)
    {
        // Admins can modify anyone
        if (user.IsInRole("Admin"))
            return true;

        // Check if this is the user's own student record
        var studentIdClaim = user.FindFirst("studentId")?.Value;
        if (studentIdClaim != null && int.TryParse(studentIdClaim, out var userStudentId))
            return userStudentId == studentId;

        return false;
    }
}

// Inputs
public record CreateStudentInput(string Name, string Email);
public record UpdateStudentInput(string Name, string Email);
public record EnrollStudentInput(int StudentId, int SubjectId);
public record ActivateAccountInput(string ActivationCode, string Username, string Password);

// Error Types
/// <summary>
/// Represents an error that occurred during a mutation operation.
/// </summary>
/// <param name="Code">Machine-readable error code for client handling.</param>
/// <param name="Message">Human-readable error message.</param>
/// <param name="Field">Optional field name that caused the error.</param>
public record MutationError(string Code, string Message, string? Field = null);

// Payloads with structured errors
/// <summary>
/// Payload for CreateStudent mutation.
/// </summary>
public record CreateStudentPayload(
    StudentDto? Student,
    IReadOnlyList<MutationError>? Errors = null)
{
    public CreateStudentPayload(StudentDto student) : this(student, null) { }
    public bool Success => Student is not null && (Errors is null || Errors.Count == 0);
}

/// <summary>
/// Payload for UpdateStudent mutation.
/// </summary>
public record UpdateStudentPayload(
    StudentDto? Student,
    IReadOnlyList<MutationError>? Errors = null)
{
    public UpdateStudentPayload(StudentDto student) : this(student, null) { }
    public bool Success => Student is not null && (Errors is null || Errors.Count == 0);
}

/// <summary>
/// Payload for DeleteStudent mutation.
/// </summary>
public record DeleteStudentPayload(
    bool Success,
    IReadOnlyList<MutationError>? Errors = null)
{
    public DeleteStudentPayload(bool success) : this(success, null) { }
}

/// <summary>
/// Payload for EnrollStudent mutation.
/// </summary>
public record EnrollStudentPayload(
    EnrollmentDto? Enrollment,
    IReadOnlyList<MutationError>? Errors = null)
{
    public EnrollStudentPayload(EnrollmentDto enrollment) : this(enrollment, null) { }
    public bool Success => Enrollment is not null && (Errors is null || Errors.Count == 0);
}

/// <summary>
/// Payload for UnenrollStudent mutation.
/// </summary>
public record UnenrollStudentPayload(
    bool Success,
    IReadOnlyList<MutationError>? Errors = null)
{
    public UnenrollStudentPayload(bool success) : this(success, null) { }
}

/// <summary>
/// Payload for CreateStudent mutation with activation code.
/// </summary>
public record CreateStudentWithActivationPayload(
    StudentDto Student,
    string ActivationCode,
    string ActivationUrl,
    DateTime ExpiresAt)
{
    public CreateStudentWithActivationPayload(CreateStudentResult result)
        : this(result.Student, result.ActivationCode, result.ActivationUrl, result.ExpiresAt) { }
}

/// <summary>
/// Payload for ActivateAccount mutation.
/// </summary>
public record ActivateAccountPayload(
    bool Success,
    string? Token,
    string? RecoveryCode,
    string? Error);
