using System.ComponentModel.DataAnnotations;
using Jiaowu.Api.Contracts;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Jiaowu.Api.Controllers;

[ApiController]
[Authorize]
[Route("api/teacher-course-applications")]
public sealed class TeacherCourseApplicationsController(
    AppDbContext db,
    ICurrentUserDataScope currentUserDataScope) : ControllerBase
{
    private const string ReviewRoles =
        SystemRoles.SuperAdmin + "," +
        SystemRoles.AcademicAdmin + "," +
        SystemRoles.CollegeAdmin;

    [HttpGet("course-options")]
    [Authorize(Roles = SystemRoles.Teacher)]
    public async Task<ActionResult> GetCourseOptions(CancellationToken cancellationToken)
    {
        return Ok(await db.Courses.AsNoTracking()
            .Where(x => x.IsEnabled)
            .OrderBy(x => x.SortOrder)
            .ThenBy(x => x.Code)
            .Select(x => new
            {
                x.Id,
                x.Code,
                x.Name,
                x.CollegeId,
                CollegeName = x.College!.Name,
                x.Credits,
                x.Nature
            })
            .ToListAsync(cancellationToken));
    }

    [HttpGet("mine")]
    [Authorize(Roles = SystemRoles.Teacher)]
    public async Task<ActionResult> GetMine(
        Guid? academicTermId,
        int page = 1,
        int pageSize = 20,
        CancellationToken cancellationToken = default)
    {
        if (page < 1 || pageSize is < 1 or > 100)
            return ValidationProblem("页码必须大于 0，且每页条数应在 1 至 100 之间。");

        var teacher = await CurrentTeacherAsync(cancellationToken);
        if (teacher is null) return ConflictProblem("当前账号尚未关联在职教师档案。");
        var source = db.TeacherCourseApplications.AsNoTracking()
            .Where(x => x.TeacherId == teacher.Id);
        if (academicTermId.HasValue)
            source = source.Where(x => x.AcademicTermId == academicTermId);
        var total = await source.CountAsync(cancellationToken);
        var items = await source
            .OrderByDescending(x => x.AcademicTerm!.StartDate)
            .ThenBy(x => x.Course!.Code)
            .ThenBy(x => x.Id)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Select(x => new
            {
                x.Id,
                x.AcademicTermId,
                TermName = x.AcademicTerm!.Name,
                x.CourseId,
                CourseCode = x.Course!.Code,
                CourseName = x.Course.Name,
                CollegeName = x.Course.College!.Name,
                x.Status,
                x.Statement,
                x.ReviewComment,
                x.SubmittedAt,
                x.ReviewedAt
            })
            .ToListAsync(cancellationToken);
        return Ok(new PagedResult<object>(items, total, page, pageSize));
    }

    [HttpPost("mine")]
    [Authorize(Roles = SystemRoles.Teacher)]
    public async Task<ActionResult> Submit(
        TeacherCourseApplicationRequest request,
        CancellationToken cancellationToken)
    {
        var teacher = await CurrentTeacherAsync(cancellationToken);
        if (teacher is null) return ConflictProblem("当前账号尚未关联在职教师档案。");
        if (!await db.AcademicTerms.AnyAsync(
                x => x.Id == request.AcademicTermId && x.IsEnabled,
                cancellationToken))
            return ValidationProblem("所选学期不存在或已停用。");
        if (!await db.Courses.AnyAsync(
                x => x.Id == request.CourseId && x.IsEnabled,
                cancellationToken))
            return ValidationProblem("所选课程不存在或已停用。");

        var application = await db.TeacherCourseApplications
            .FirstOrDefaultAsync(x =>
                x.AcademicTermId == request.AcademicTermId &&
                x.TeacherId == teacher.Id &&
                x.CourseId == request.CourseId,
                cancellationToken);
        if (application is not null &&
            application.Status is TeacherCourseApplicationStatus.Pending or
                TeacherCourseApplicationStatus.Approved)
            return ConflictProblem("该学期的课程申报已提交或已审核通过。");
        if (application is null)
        {
            application = new TeacherCourseApplication
            {
                AcademicTermId = request.AcademicTermId,
                TeacherId = teacher.Id,
                CourseId = request.CourseId
            };
            db.TeacherCourseApplications.Add(application);
        }
        application.Status = TeacherCourseApplicationStatus.Pending;
        application.Statement = Normalize(request.Statement);
        application.ReviewComment = null;
        application.SubmittedAt = DateTime.UtcNow;
        application.ReviewedAt = null;
        application.ReviewedByUserId = null;
        await db.SaveChangesAsync(cancellationToken);
        return Created(string.Empty, new { application.Id });
    }

    [HttpDelete("mine/{id:guid}")]
    [Authorize(Roles = SystemRoles.Teacher)]
    public async Task<ActionResult> Withdraw(Guid id, CancellationToken cancellationToken)
    {
        var teacher = await CurrentTeacherAsync(cancellationToken);
        if (teacher is null) return ConflictProblem("当前账号尚未关联在职教师档案。");
        var application = await db.TeacherCourseApplications.FirstOrDefaultAsync(
            x => x.Id == id && x.TeacherId == teacher.Id,
            cancellationToken);
        if (application is null) return NotFound();
        if (application.Status != TeacherCourseApplicationStatus.Pending)
            return ConflictProblem("只有待审核申报可以撤回。");
        application.Status = TeacherCourseApplicationStatus.Withdrawn;
        await db.SaveChangesAsync(cancellationToken);
        return NoContent();
    }

    [HttpGet("reviews")]
    [Authorize(Roles = ReviewRoles)]
    public async Task<ActionResult> GetReviews(
        Guid? academicTermId,
        TeacherCourseApplicationStatus? status,
        int page = 1,
        int pageSize = 20,
        CancellationToken cancellationToken = default)
    {
        if (page < 1 || pageSize is < 1 or > 100)
            return ValidationProblem("页码必须大于 0，且每页条数应在 1 至 100 之间。");

        var source = ScopedApplications().AsNoTracking();
        if (academicTermId.HasValue)
            source = source.Where(x => x.AcademicTermId == academicTermId);
        if (status.HasValue) source = source.Where(x => x.Status == status);
        var total = await source.CountAsync(cancellationToken);
        var items = await source
            .OrderBy(x => x.Status)
            .ThenByDescending(x => x.SubmittedAt)
            .ThenBy(x => x.Id)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Select(x => new
            {
                x.Id,
                x.AcademicTermId,
                TermName = x.AcademicTerm!.Name,
                x.TeacherId,
                x.Teacher!.TeacherNumber,
                TeacherName = x.Teacher.Name,
                TeacherCollegeName = x.Teacher.College!.Name,
                x.CourseId,
                CourseCode = x.Course!.Code,
                CourseName = x.Course.Name,
                CourseCollegeName = x.Course.College!.Name,
                x.Status,
                x.Statement,
                x.ReviewComment,
                x.SubmittedAt,
                x.ReviewedAt
            })
            .ToListAsync(cancellationToken);
        return Ok(new PagedResult<object>(items, total, page, pageSize));
    }

    [HttpGet("assignment-options")]
    [Authorize(Roles = ReviewRoles)]
    public async Task<ActionResult> GetAssignmentOptions(
        CancellationToken cancellationToken)
    {
        var teachers = db.Teachers.AsNoTracking()
            .Where(x => x.Status == TeacherStatus.Active);
        var collegeId = currentUserDataScope.Current.RestrictedCollegeId;
        if (collegeId.HasValue)
            teachers = teachers.Where(x => x.CollegeId == collegeId.Value);

        return Ok(new
        {
            Teachers = await teachers
                .OrderBy(x => x.TeacherNumber)
                .Select(x => new
                {
                    x.Id,
                    x.TeacherNumber,
                    x.Name,
                    x.Title,
                    x.CollegeId,
                    CollegeName = x.College!.Name
                })
                .ToListAsync(cancellationToken),
            Courses = await db.Courses.AsNoTracking()
                .Where(x => x.IsEnabled)
                .OrderBy(x => x.SortOrder)
                .ThenBy(x => x.Code)
                .Select(x => new
                {
                    x.Id,
                    x.Code,
                    x.Name,
                    x.CollegeId,
                    CollegeName = x.College!.Name,
                    x.Credits,
                    x.Nature
                })
                .ToListAsync(cancellationToken)
        });
    }

    [HttpPost("assign")]
    [Authorize(Roles = ReviewRoles)]
    public async Task<ActionResult> Assign(
        TeacherCourseAssignmentRequest request,
        CancellationToken cancellationToken)
    {
        if (!await db.AcademicTerms.AnyAsync(
                x => x.Id == request.AcademicTermId && x.IsEnabled,
                cancellationToken))
            return ValidationProblem("所选学期不存在或已停用。");
        if (!await db.Courses.AnyAsync(
                x => x.Id == request.CourseId && x.IsEnabled,
                cancellationToken))
            return ValidationProblem("所选课程不存在或已停用。");

        var teachers = db.Teachers.Where(x =>
            x.Id == request.TeacherId &&
            x.Status == TeacherStatus.Active);
        var collegeId = currentUserDataScope.Current.RestrictedCollegeId;
        if (collegeId.HasValue)
            teachers = teachers.Where(x => x.CollegeId == collegeId.Value);
        if (!await teachers.AnyAsync(cancellationToken))
            return ValidationProblem("所选教师不存在、不在职或不属于当前学院。");

        var application = await db.TeacherCourseApplications
            .FirstOrDefaultAsync(x =>
                x.AcademicTermId == request.AcademicTermId &&
                x.TeacherId == request.TeacherId &&
                x.CourseId == request.CourseId,
                cancellationToken);
        if (application?.Status == TeacherCourseApplicationStatus.Approved)
            return ConflictProblem("该教师在所选学期已具备此课程的授课资格。");

        if (application is null)
        {
            application = new TeacherCourseApplication
            {
                AcademicTermId = request.AcademicTermId,
                TeacherId = request.TeacherId,
                CourseId = request.CourseId,
                SubmittedAt = DateTime.UtcNow
            };
            db.TeacherCourseApplications.Add(application);
        }

        application.Status = TeacherCourseApplicationStatus.Approved;
        application.ReviewComment = Normalize(request.Comment) ?? "学院直接分配";
        application.ReviewedAt = DateTime.UtcNow;
        application.ReviewedByUserId = currentUserDataScope.Current.UserId;
        await db.SaveChangesAsync(cancellationToken);
        return Ok(new { application.Id });
    }

    [HttpPost("{id:guid}/review")]
    [Authorize(Roles = ReviewRoles)]
    public async Task<ActionResult> Review(
        Guid id,
        TeacherCourseReviewRequest request,
        CancellationToken cancellationToken)
    {
        if (request.Status is not (
            TeacherCourseApplicationStatus.Approved or
            TeacherCourseApplicationStatus.Rejected))
            return ValidationProblem("审核结论只能为通过或驳回。");
        var application = await ScopedApplications()
            .FirstOrDefaultAsync(x => x.Id == id, cancellationToken);
        if (application is null) return NotFound();
        if (application.Status != TeacherCourseApplicationStatus.Pending)
            return ConflictProblem("只有待审核申报可以审核。");
        application.Status = request.Status;
        application.ReviewComment = Normalize(request.ReviewComment);
        application.ReviewedAt = DateTime.UtcNow;
        application.ReviewedByUserId = currentUserDataScope.Current.UserId;
        await db.SaveChangesAsync(cancellationToken);
        return NoContent();
    }

    [HttpGet("eligible")]
    [Authorize(Roles = ReviewRoles)]
    public async Task<ActionResult> GetEligible(
        Guid academicTermId,
        Guid courseId,
        CancellationToken cancellationToken)
    {
        var source = ScopedApplications().AsNoTracking()
            .Where(x =>
                x.AcademicTermId == academicTermId &&
                x.CourseId == courseId &&
                x.Status == TeacherCourseApplicationStatus.Approved &&
                x.Teacher!.Status == TeacherStatus.Active);
        return Ok(await source
            .OrderBy(x => x.Teacher!.TeacherNumber)
            .Select(x => new
            {
                x.TeacherId,
                x.Teacher!.TeacherNumber,
                x.Teacher.Name,
                x.Teacher.Title,
                CollegeName = x.Teacher.College!.Name
            })
            .ToListAsync(cancellationToken));
    }

    private IQueryable<TeacherCourseApplication> ScopedApplications()
    {
        var source = db.TeacherCourseApplications.AsQueryable();
        var collegeId = currentUserDataScope.Current.RestrictedCollegeId;
        return collegeId.HasValue
            ? source.Where(x => x.Teacher!.CollegeId == collegeId.Value)
            : source;
    }

    private Task<Teacher?> CurrentTeacherAsync(CancellationToken cancellationToken)
    {
        var userId = currentUserDataScope.Current.UserId;
        return db.Teachers.FirstOrDefaultAsync(
            x => x.UserId == userId && x.Status == TeacherStatus.Active,
            cancellationToken);
    }

    private ActionResult ConflictProblem(string detail) =>
        Conflict(new ProblemDetails
        {
            Title = "无法完成操作",
            Detail = detail,
            Status = StatusCodes.Status409Conflict
        });

    private static string? Normalize(string? value) =>
        string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}

public sealed record TeacherCourseApplicationRequest(
    Guid AcademicTermId,
    Guid CourseId,
    [MaxLength(500)] string? Statement);

public sealed record TeacherCourseReviewRequest(
    TeacherCourseApplicationStatus Status,
    [MaxLength(500)] string? ReviewComment);

public sealed record TeacherCourseAssignmentRequest(
    Guid AcademicTermId,
    Guid TeacherId,
    Guid CourseId,
    [MaxLength(500)] string? Comment);
