using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Analytics;
using Jiaowu.Api.Infrastructure.Auth;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Jiaowu.Api.Controllers;

[ApiController]
[Authorize(Roles = AnalyticsUsers)]
[Route("api/clickhouse-analytics")]
public sealed class ClickHouseAnalyticsController(
    ClickHouseAnalyticsClient client,
    ClickHouseAnalyticsOptions options,
    ICurrentUserDataScope currentUserDataScope) : ControllerBase
{
    private const string AnalyticsUsers =
        SystemRoles.SuperAdmin + "," + SystemRoles.AcademicAdmin + "," +
        SystemRoles.CollegeAdmin + "," + SystemRoles.Leader;

    [HttpGet("status")]
    public ActionResult GetStatus() => Ok(new
    {
        options.Enabled,
        options.SyncIntervalSeconds,
        options.SourceLookbackDays,
        options.BatchSize,
        options.Database
    });

    [HttpGet("overview")]
    public async Task<ActionResult> GetOverview(
        DateOnly? from,
        DateOnly? to,
        CancellationToken cancellationToken)
    {
        if (!options.Enabled)
            return Conflict(new ProblemDetails
            {
                Title = "ClickHouse 分析未启用",
                Detail = "请先配置 ClickHouseAnalytics 并启动分析库。",
                Status = StatusCodes.Status409Conflict
            });

        var end = to ?? DateOnly.FromDateTime(DateTime.UtcNow);
        var start = from ?? end.AddDays(-29);
        if (start > end || end.DayNumber - start.DayNumber > 366)
            return ValidationProblem("分析时间范围应为 1 到 366 天，且开始日期不能晚于结束日期。");

        var scope = currentUserDataScope.Current;
        var collegeFilter = scope.RestrictedCollegeId is { } collegeId
            ? $" AND collegeId = toUUID('{collegeId:D}')"
            : string.Empty;
        var dateFilter = $"attendanceDate >= toDate('{start:yyyy-MM-dd}') AND attendanceDate <= toDate('{end:yyyy-MM-dd}')";

        var attendance = await client.QueryAsync($"""
            SELECT attendanceDate, count() AS total, countIf(status = 1) AS present,
                   countIf(status = 2) AS absent, countIf(status = 3) AS late
            FROM {options.Database}.attendanceRecords FINAL
            WHERE {dateFilter}{collegeFilter}
            GROUP BY attendanceDate ORDER BY attendanceDate
            """, cancellationToken);
        var grades = await client.QueryAsync($"""
            SELECT academicTermId, academicTermName,
                   totalStudentCount AS studentCount,
                   round(totalWeightedScore / nullIf(totalStudentCount, 0), 2) AS averageScore,
                   round(totalPassedCount / nullIf(totalStudentCount, 0), 4) AS passRate
            FROM
            (
                SELECT academicTermId, any(academicTermName) AS academicTermName,
                       sum(studentCount) AS totalStudentCount,
                       sum(averageScore * studentCount) AS totalWeightedScore,
                       sum(passedCount) AS totalPassedCount
                FROM {options.Database}.gradeStatistics FINAL
                WHERE 1 = 1{collegeFilter}
                GROUP BY academicTermId
            ) AS gradeTotals
            ORDER BY academicTermName
            """, cancellationToken);

        // Audit data has no college dimension, so it is never exposed to a
        // college-scoped administrator.
        var audit = scope.RestrictedCollegeId is null
            ? await client.QueryAsync($"""
                SELECT toDate(occurredAt) AS date, count() AS total,
                       countIf(statusCode >= 400) AS failed
                FROM {options.Database}.auditEvents FINAL
                WHERE occurredAt >= toDateTime('{start:yyyy-MM-dd}')
                  AND occurredAt < toDateTime('{end.AddDays(1):yyyy-MM-dd}')
                GROUP BY date ORDER BY date
                """, cancellationToken)
            : [];

        return Ok(new { Start = start, End = end, Attendance = attendance, Grades = grades, Audit = audit });
    }
}
