import { PathLike } from "fs";
import { parseJsonWithComments } from "../cli/cliUtil";

export async function readGradingSchema(schemaFile: PathLike) {
    const gradingSchema: GradingSchema = await parseJsonWithComments(schemaFile);

    // post processing
    gradingSchema.tasks.forEach(task => {
        if (!task.grading) {
            task.grading = [];
        }
    });

    return gradingSchema;
}

export interface GradingSchema {
    /** Only for VSCode, 
     * @see MANUAL_CORRECTIONS_SCHEMA_URL
     */
    $schema: string
    course: string,
    term: string,
    exam: string;
    points: number;
    minCoverageStatements?: number;
    penaltiesCoverageMax?: number;
    extraCoverageMax?: number;
    /**
     * Optional, if tests of penalties fail points are subtracted (well, 
     * actually added but the penalty points are expected to be negative).
     *
     * If the penalties have a precondition, the penalty is ignored if the precondition fails.
     * 
     * Output is usually only generated if a penalty test fails.
     * Otherwise it is not visible in the report later on.
     */
    penalties?: Grading[];
    tasks: Task[];
}

export interface Task {
    name: string;
    suite?: string;
    skipHint?: string;
    points: number;
    preconditions?: Precondition[];
    grading: Grading[];
    manual: boolean;
    /* Deprecated, use images instead */
    image?: Image;
    images?: Image[];
    /**
     * Optional, if tests of penalties fail points are subtracted (well, 
     * actually added but the penalty points are expected to be negative).
     *
     * If the penalties have a precondition, the penalty is ignored if the precondition fails.
     * 
     * Output is usually only generated if a penalty test fails.
     * Otherwise it is not visible in the report later on.
     */
    penalties?: Grading[];
}

export interface Image {
    src: string;
    text: string;
    missing?: string
    width?: string
}

/**
 * Bewertung (or Penalty)
 */
export interface Grading {
    suite?: string;
    tests: string[];
    isBonus?: boolean;
    /**
     * Points, equally divided for each test.
     * If the Bewertung is a penalty, the points are expected to be negative.
     */
    points: number;
    /**
     * If the precondition fails, no further tests are analyzed.
     * If the Bewertung is positive, 0 points are given.
     * If the Bewertung is a penalty, 0 points are subtracted as well.
     */
    preconditions?: Precondition[];
    /**
     * Do not access this property directly, instead
     * use {@link gradingText}.
     */
    text?: string;
    /**
     * Deprecated, use images instead
     */
    image?: Image;
    images?: Image[];
}

/**
 * If not provided, text of first test is used.
 */
export function gradingText(grading: Grading) {
    if (grading.text) {
        return grading.text;
    }
    if (grading.tests.length > 0) {
        return grading.tests[0];
    }
    throw new Error("Grading does not define any tests (and no text).")
}

export interface Precondition {
    test: string;
    /**
     * Use preconditionText instead
     */
    failure?: string;
    suite?: string;
}

/**
 * If not provided, text of first test is used.
 */
export function preconditionText(precondition: Precondition) {
    if (precondition.failure) {
        return precondition.failure;
    }
    return precondition.test;

}