import express, { Request } from 'express';
import { Collection } from 'dce-mango';

/**
 * Type for Canvas API (see `caccl-api` package).
 * @author Benedikt Arnarsson
 */
type CanvasAPI = any;

/**
 * Type of a course event
 * @author Gabe Abrams
 */
declare enum CourseEventType {
    Class = "class",
    Section = "section",
    Lab = "lab",
    OfficeHours = "office-hours",
    Meeting = "meeting",
    Event = "event",
    Other = "other"
}

/**
 * Enum for the days of the week
 * @author Gabe Abrams
 */
declare enum DayOfWeek {
    Monday = "M",
    Tuesday = "T",
    Wednesday = "W",
    Thursday = "R",
    Friday = "F",
    Saturday = "S",
    Sunday = "U"
}

/**
 * A specific time window where a user can get an attendance point
 * @author Gabe Abrams
 */
declare enum PointWindow {
    Day = "Day",
    Week = "Week",
    Once = "Once"
}

/**
 * Event property for whether to assign groups across occurrences.
 * @author Benedikt Arnarsson
 */
declare enum AssignToGroups {
    Always = "Always",
    Never = "Never",
    Ask = "Ask"
}

/**
 * A single course event
 * @author Gabe Abrams
 */
type CourseEvent = ({
    key: string;
    courseId: number;
    ihid: string;
    name: string;
    type: CourseEventType;
    lockZoomToggle?: boolean;
    lockAutoRecordSetting?: boolean;
    banDCEStudents?: boolean;
    banFASStudents?: boolean;
    pinned?: boolean;
    archived?: boolean;
} & ({
    currentZoomId: number;
    pastZoomIds?: number[] | null;
    currentZoomHost: string;
    pastZoomHosts?: string[] | null;
    openZoomLink: string;
    isWebinar?: boolean;
} | {
    currentZoomId?: null;
    pastZoomIds?: number[] | null;
    currentZoomHost?: null;
    pastZoomHosts?: string[] | null;
    openZoomLink?: null;
    isWebinar?: null;
}) & ({
    isHeldInPerson: true;
    inPersonConfig: {
        assignToGroups: AssignToGroups;
    };
} | {
    isHeldInPerson?: false;
    inPersonConfig?: undefined;
}) & ({
    pointWindow?: PointWindow;
    daysOfWeek?: DayOfWeek[];
    maxPoints?: number | null | undefined;
} | {
    pointWindow?: null;
    daysOfWeek?: null;
    maxPoints?: null;
}));

type GetCourseEvent = (opts: {
    courseId: number;
    ihid: string;
    requireNotArchived: true;
}) => Promise<CourseEvent>;

/**
 * Record an action occurrence
 * @author Gabe Abrams
 * @author Karen Dolan
 * @param {string} type - the action type
 * @param {string} description - the error message
 * @param {object} req - Express request object
 * @param {number} [userId=value in session] - the Canvas id of the user who is
 *   attending
 * @param {string} [userFirstName=value in session] - the first name of the user
 * @param {string} [userLastName=value in session] - the last name of the user
 * @param {number} [courseId=value in session] - the Canvas Course Id
 * @param {object} [metadata={}] - additional metadata object
 * @param {boolean} [isLearner=value in session] - true if the user is a learner
 * @param {boolean} [isAdmin=value in session] - true if the user is an admin
 */
type LogAction = (opts: {
    type: string;
    description: string;
    req: Request;
    userId?: number;
    userFirstName?: string;
    userLastName?: string;
    courseId?: number;
    metadata?: {
        [k: string]: any;
    };
    isLearner?: boolean;
    isAdmin?: boolean;
}) => Promise<void>;

/**
 * Record an error
 * @author Gabe Abrams
 * @param {string} message - the error message
 * @param {string|number} code - the error code
 * @param {object} req - Express request object
 * @param {number} [userId=value in session] - the Canvas id of the user who is
 *   attending
 * @param {string} [userFirstName=value in session] - the first name of the user
 * @param {string} [userLastName=value in session] - the last name of the user
 * @param {number} [courseId=value in session] - the Canvas Course Id
 * @param {object} [metadata={}] - additional metadata object
 * @param {boolean} [isLearner=value in session] - true if the user is a learner
 * @param {boolean} [isAdmin=value in session] - true if the user is an admin
 */
type LogError = (opts: {
    message: string;
    code: string | number;
    req: Request;
    userId?: number;
    userFirstName?: string;
    userLastName?: string;
    courseId?: number;
    metadata?: {
        [key in string]: any;
    };
    isLearner?: boolean;
    isAdmin?: boolean;
}) => Promise<void>;

/**
 * Enum for the attendance method
 * @author Gabe Abrams
 */
declare enum AttendanceMethod {
    InPerson = "in-person",
    Live = "live",
    Async = "async",
    Excused = "excused"
}

/**
 * Information needed to log attendance.
 * @author Benedikt Arnarsson
 * @author Gabe Abrams
 */
type AttendanceEntry = {
    userId: number;
    userFirstName: string;
    userLastName: string;
    courseId: number;
    ihid: string;
    method: AttendanceMethod;
    groupNum?: number;
    isLearner?: boolean;
    isAdmin?: boolean;
    isHost?: boolean;
    eventTimestamp?: number;
};

/**
 * Type describing a function required for recording student attendance.
 * @author Benedikt Arnarsson
 */
type RecordAttendanceFunction = (opts: AttendanceEntry) => Promise<void>;

/**
 * Function to add the dce-check-in-ui backend to your Express app.
 * @author Benedikt Arnarsson
 * @param app the Express app that will handle the check-in UI backend calls.
 * @param opts.getCanvasAPI function for retrieving the Canvas API.
 * @param opts.CollectionCons constructor for Collection type (dce-mango).
 * @param opts.recordAttendance function for recording student attendance.
 * @param opts.logAction function for logging backend actions.
 * @param opts.logError function for logging backend errors.
 */
declare const addCheckInRoutes: (opts: {
    app: express.Application;
    getCanvasAPI: () => CanvasAPI;
    CollectionCons: typeof Collection;
    recordAttendance: RecordAttendanceFunction;
    getCourseEvent: GetCourseEvent;
    logAction: LogAction;
    logError: LogError;
}) => void;

export { addCheckInRoutes };
