module api_modules.classroom_compliance.util;

import api_modules.classroom_compliance.model;
import api_modules.auth;
import db;

import ddbc : Connection;
import handy_httpd;
import std.datetime;

/**
 * Gets a Classroom-Compliance class from an HTTP request's path parameters,
 * as well as a given user that the class should belong to.
 * Params:
 *   ctx = The request context.
 *   conn = The database connection to use.
 *   user = The user who the class belongs to.
 * Returns: The class that was found, or a 404 status exception is thrown.
 */
ClassroomComplianceClass getClassOrThrow(in HttpRequestContext ctx, Connection conn, in User user) {
    ulong classId = ctx.request.getPathParamAs!ulong("classId");
    return findOne(
        conn,
        "SELECT * FROM classroom_compliance_class WHERE user_id = ? AND id = ?",
        &ClassroomComplianceClass.parse,
        user.id, classId
    ).orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND));
}

/**
 * Gets a Classroom-Compliance student from an HTTP request's path parameters,
 * as well as a given user that the student should belong to.
 * Params:
 *   ctx = The request context.
 *   conn = The database connection to use.
 *   user = The user who the student belongs to.
 * Returns: The student that was found, or a 404 status exception is thrown.
 */
ClassroomComplianceStudent getStudentOrThrow(in HttpRequestContext ctx, Connection conn, in User user) {
    ulong classId = ctx.request.getPathParamAs!ulong("classId");
    ulong studentId = ctx.request.getPathParamAs!ulong("studentId");
    string query = "
    SELECT s.*
    FROM classroom_compliance_student s
    LEFT JOIN classroom_compliance_class c ON s.class_id = c.id
    WHERE s.id = ? AND s.class_id = ? AND c.user_id = ?
    ";
    return findOne(
        conn,
        query,
        &ClassroomComplianceStudent.parse,
        studentId,
        classId,
        user.id
    ).orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND));
}

struct DateRange {
    Date from;
    Date to;
}

/**
 * Helper function to parse a date range from a request's "from" and "to"
 * parameters, because this is used commonly for entry-related functions.
 * Params:
 *   ctx = The request context.
 *   maxDays = The maximum date range length.
 * Returns: The date range that was parsed.
 */
DateRange parseDateRangeParams(in HttpRequestContext ctx, uint maxDays = 10) {
    SysTime now = Clock.currTime();
    Date toDate = Date(now.year, now.month, now.day);
    Date fromDate = toDate - days(4);
    if (ctx.request.queryParams.contains("to")) {
        try {
            toDate = Date.fromISOExtString(ctx.request.queryParams.getFirst("to").orElse(""));
        } catch (DateTimeException e) {
            throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Invalid \"to\" date.");
        }
    }
    if (ctx.request.queryParams.contains("from")) {
        try {
            fromDate = Date.fromISOExtString(ctx.request.queryParams.getFirst("from").orElse(""));
        } catch (DateTimeException e) {
            throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Invalid \"from\" date.");
        }
    }
    if (fromDate > toDate) {
        throw new HttpStatusException(
            HttpStatus.BAD_REQUEST,
            "Invalid date range. From-date must be less than or equal to the to-date."
        );
    }
    if (toDate - fromDate > days(maxDays)) {
        throw new HttpStatusException(
            HttpStatus.BAD_REQUEST,
            "Date range is too big."
        );
    }
    return DateRange(fromDate, toDate);
}
