import { Ctx, File } from "./ctx.ts";
import { exhausted } from "./util.ts";

export type Span = {
    begin: Pos;
    end: Pos;
};

export type Pos = {
    idx: number;
    line: number;
    col: number;
};

export const Span = {
    fromto: ({ begin }: Span, { end }: Span): Span => ({ begin, end }),
} as const;

export type Report = {
    severity: "fatal" | "error" | "warning" | "info";
    origin?: string;
    msg: string;
    file?: File;
    span?: Span;
    pos?: Pos;
};

function severityColor(severity: "fatal" | "error" | "warning" | "info") {
    switch (severity) {
        case "fatal":
            return "\x1b[1m\x1b[31m";
        case "error":
            return "\x1b[1m\x1b[31m";
        case "warning":
            return "\x1b[1m\x1b[33m";
        case "info":
            return "\x1b[1m\x1b[34m";
    }
    exhausted(severity);
}

export function prettyPrintReport(ctx: Ctx, rep: Report) {
    const { severity, msg } = rep;
    const origin = rep.origin ? `\x1b[1m${rep.origin}:\x1b[0m ` : "";
    console.error(
        `${origin}${
            severityColor(severity)
        }${severity}:\x1b[0m \x1b[37m${msg}\x1b[0m`,
    );
    if (rep.file && (rep.span || rep.pos)) {
        const errorLineOffset = 2;
        const { absPath: path } = ctx.fileInfo(rep.file);
        const { line, col } = rep.span?.begin ?? rep.pos!;
        console.error(`    --> ./${path}:${line}:${col}`);
        if (rep.span) {
            const spanLines = ctx.fileSpanText(rep.file, rep.span).split("\n");
            spanLines.pop();
            if (spanLines.length == 1) {
                console.error(
                    `${rep.span.begin.line.toString().padStart(4, " ")}| ${
                        spanLines[0]
                    }`,
                );
                console.error(
                    `    | ${severityColor(severity)}${
                        " ".repeat(rep.span.begin.col)
                    }${
                        "~".repeat(rep.span.end.col - rep.span.begin.col)
                    }\x1b[0m`,
                );
                return;
            }
            for (let i = 0; i < spanLines.length; i++) {
                console.error(
                    `${
                        (rep.span.begin.line + i).toString().padStart(4, " ")
                    }| ${spanLines[i]}`,
                );
                if (i == 0) {
                    console.error(
                        `    | ${" ".repeat(rep.span.begin.col - 1)}${
                            severityColor(severity)
                        }${
                            "~".repeat(
                                spanLines[i].length - (rep.span.begin.col - 1),
                            )
                        }\x1b[0m`,
                    );
                } else if (i == spanLines.length - 1) {
                    console.error(
                        `    | ${severityColor(severity)}${
                            "~".repeat(rep.span.end.col)
                        }\x1b[0m`,
                    );
                } else {
                    console.error(
                        `    | ${severityColor(severity)}${
                            "~".repeat(spanLines[i].length)
                        }\x1b[0m`,
                    );
                }
            }
        } else if (rep.pos) {
            console.error(
                `${rep.pos.line.toString().padStart(4, " ")}| ${
                    ctx.filePosLineText(rep.file, rep.pos)
                }`,
            );
            console.error(
                `    | ${severityColor(severity)}${
                    " ".repeat(rep.pos.col)
                }^\x1b[0m`,
            );
        }
    }
}

export function printStackTrace() {
    class StackTracer extends Error {
        constructor() {
            super("StackTracer");
        }
    }
    try {
        throw new StackTracer();
    } catch (error) {
        if (!(error instanceof StackTracer)) {
            throw error;
        }
        console.log(
            error.stack?.replace(
                "Error: StackTracer",
                "Stack trace:",
            ) ??
                error,
        );
    }
}