using Jiaowu.Api.Controllers;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.System;
using Jiaowu.Api.Infrastructure.Persistence;
using Jiaowu.Api.Infrastructure.Scheduling;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;

namespace Jiaowu.Api.Tests;

public sealed class SchedulesControllerTests
{
    [Fact]
    public async Task Manual_entry_adds_experiment_hours_to_regular_schedule()
    {
        await using var connection = new SqliteConnection("Data Source=:memory:");
        await connection.OpenAsync();
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseSqlite(connection)
            .Options;
        await using var db = new AppDbContext(options);
        await db.Database.EnsureCreatedAsync();

        var term = new AcademicTerm
        {
            Code = "2026-LAB",
            Name = "2026 实验学时测试",
            AcademicYear = "2026-2027",
            Season = TermSeason.Autumn,
            StartDate = new DateOnly(2026, 9, 7),
            EndDate = new DateOnly(2027, 1, 17)
        };
        var college = new College { Code = "LAB", Name = "实验学院" };
        var campus = new Campus { Code = "LAB-CAMPUS", Name = "实验校区" };
        var building = new Building
        {
            Code = "LAB-BUILDING",
            Name = "实验楼",
            Campus = campus
        };
        var laboratory = new Classroom
        {
            Code = "LAB-201",
            Name = "实验室 201",
            Building = building,
            Capacity = 40,
            RoomType = "实验室",
            TeachingVenueNature = TeachingVenueNature.Laboratory
        };
        var course = new Course
        {
            Code = "LAB-01",
            Name = "实验混合课程",
            College = college,
            Credits = 2,
            TotalHours = 32,
            LectureHours = 16,
            PracticeHours = 16
        };
        var task = new TeachingTask
        {
            TaskNumber = "TASK-LAB",
            Name = "实验混合教学班",
            AcademicTerm = term,
            Course = course,
            Capacity = 30,
            StartWeek = 1,
            EndWeek = 16,
            WeeklyHours = 2,
            Status = TeachingTaskStatus.Published
        };
        var plan = new SchedulePlan
        {
            AcademicTerm = term,
            Name = "排课草稿",
            Version = "V1"
        };
        plan.Entries.Add(new ScheduleEntry
        {
            TeachingTask = task,
            DayOfWeek = 1,
            StartPeriod = 1,
            PeriodCount = 1,
            StartWeek = 1,
            EndWeek = 16,
            WeekPattern = WeekPattern.All
        });
        db.AddRange(term, college, campus, building, laboratory, course, task, plan);
        db.ScheduleTimeSlots.AddRange(
            new ScheduleTimeSlot
            {
                AcademicTerm = term,
                PeriodNumber = 1,
                Name = "第 1 节",
                StartsAt = new TimeOnly(8, 0),
                EndsAt = new TimeOnly(8, 45)
            },
            new ScheduleTimeSlot
            {
                AcademicTerm = term,
                PeriodNumber = 2,
                Name = "第 2 节",
                StartsAt = new TimeOnly(8, 55),
                EndsAt = new TimeOnly(9, 40)
            });
        db.TeachingTaskScheduleConstraints.Add(
            new TeachingTaskScheduleConstraint
            {
                TeachingTask = task,
                RequiresClassroom = false
            });
        await db.SaveChangesAsync();

        var controller = new SchedulesController(db);
        var result = await controller.CreateEntry(
            plan.Id,
            new ScheduleEntryRequest(
                task.Id,
                laboratory.Id,
                2,
                1,
                1,
                1,
                16,
                WeekPattern.All,
                null,
                ScheduleEntryKind.Experiment),
            CancellationToken.None);

        Assert.IsType<CreatedResult>(result);
        var entries = await db.ScheduleEntries.OrderBy(x => x.Kind).ToListAsync();
        Assert.Equal(2, entries.Count);
        Assert.Equal(ScheduleEntryKind.Experiment, entries[1].Kind);
        Assert.Equal(laboratory.Id, entries[1].ClassroomId);
    }

    [Fact]
    public async Task Publish_returns_accepted_before_background_validation_runs()
    {
        await using var connection = new SqliteConnection("Data Source=:memory:");
        await connection.OpenAsync();
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseSqlite(connection)
            .Options;
        await using var db = new AppDbContext(options);
        await db.Database.EnsureCreatedAsync();

        var term = new AcademicTerm
        {
            Code = "2026-P",
            Name = "2026 发布测试",
            AcademicYear = "2026-2027",
            Season = TermSeason.Autumn,
            StartDate = new DateOnly(2026, 9, 7),
            EndDate = new DateOnly(2027, 1, 17)
        };
        var college = new College { Code = "P", Name = "发布学院" };
        var course = new Course
        {
            Code = "P101",
            Name = "发布课程",
            College = college,
            Credits = 1,
            TotalHours = 16,
            LectureHours = 16
        };
        var task = new TeachingTask
        {
            TaskNumber = "TASK-P",
            Name = "发布教学班",
            AcademicTerm = term,
            Course = course,
            Capacity = 30,
            WeeklyHours = 1,
            Status = TeachingTaskStatus.Published
        };
        var plan = new SchedulePlan
        {
            AcademicTerm = term,
            Name = "发布草稿",
            Version = "V1"
        };
        plan.Entries.Add(new ScheduleEntry
        {
            TeachingTask = task,
            DayOfWeek = 1,
            StartPeriod = 1,
            PeriodCount = 1,
            StartWeek = 1,
            EndWeek = 16,
            WeekPattern = WeekPattern.All
        });
        db.AddRange(term, college, course, task, plan);
        await db.SaveChangesAsync();

        var controller = new SchedulesController(db)
        {
            ControllerContext = new ControllerContext
            {
                HttpContext = new DefaultHttpContext()
            }
        };
        var result = await controller.PublishPlan(plan.Id, CancellationToken.None);

        var accepted = Assert.IsType<AcceptedAtActionResult>(result.Result);
        var response = Assert.IsType<SchedulePublishJobResponse>(accepted.Value);
        Assert.Equal(SchedulePublishJobStatus.Queued, response.Status);
        Assert.Equal(plan.Id, response.SchedulePlanId);
        Assert.Equal(
            SchedulePlanStatus.Draft,
            (await db.SchedulePlans.SingleAsync()).Status);
        Assert.Equal(1, await db.SchedulePublishJobs.CountAsync());
        var outbox = await db.BackgroundJobOutboxMessages.SingleAsync();
        Assert.Equal(BackgroundJobKind.SchedulePublish, outbox.JobKind);
        Assert.Equal(response.Id, outbox.JobId);
        Assert.Equal(BackgroundJobOutboxState.Pending, outbox.State);
    }

    [Fact]
    public async Task Latest_auto_schedule_job_remains_available_after_completion()
    {
        await using var connection = new SqliteConnection("Data Source=:memory:");
        await connection.OpenAsync();
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseSqlite(connection)
            .Options;
        await using var db = new AppDbContext(options);
        await db.Database.EnsureCreatedAsync();

        var term = new AcademicTerm
        {
            Code = "2026-F",
            Name = "2026 秋季",
            AcademicYear = "2026-2027",
            Season = TermSeason.Autumn,
            StartDate = new DateOnly(2026, 9, 7),
            EndDate = new DateOnly(2027, 1, 17)
        };
        var plan = new SchedulePlan
        {
            AcademicTerm = term,
            Name = "排课草稿",
            Version = "V1"
        };
        var completed = new AutomaticScheduleJob
        {
            SchedulePlan = plan,
            Status = AutomaticScheduleJobStatus.Succeeded,
            CreatedEntries = 12,
            CompletedTasks = 5,
            TotalTasks = 6,
            ProcessedTasks = 6,
            MessagesJson = "[\"TASK-06 仍有 2 学时无法安排\"]",
            CompletedAt = DateTime.UtcNow
        };
        db.AddRange(term, plan, completed);
        await db.SaveChangesAsync();

        var controller = new SchedulesController(db);
        var result = await controller.GetLatestAutomaticScheduleJob(
            plan.Id,
            CancellationToken.None);

        var ok = Assert.IsType<OkObjectResult>(result.Result);
        var response = Assert.IsType<AutomaticScheduleJobResponse>(ok.Value);
        Assert.Equal(completed.Id, response.Id);
        Assert.Equal(AutomaticScheduleJobStatus.Succeeded, response.Status);
        Assert.Equal(
            "TASK-06 仍有 2 学时无法安排",
            Assert.Single(response.Messages));
    }
}
