use std::process;

use ron::error::Position;

/// Report an `Error` from the `serde_json` crate
pub fn report_serde_json_err(src: &str, err: &serde_json::Error) -> ! {
    report_serde_err(src, err.line(), err.column(), err.to_string())
}

/// Report a `SpannedError` from the `ron` crate
pub fn report_serde_ron_err(src: &str, err: &ron::error::SpannedError) -> ! {
    let Position { line, col } = err.position;
    report_serde_err(src, line, col, err.to_string())
}

/// Basic function for reporting serde type of errors
fn report_serde_err(src: &str, line: usize, col: usize, msg: String) -> ! {
    use ariadne::{Label, Report, Source};
    let offset = try_reconstruct_loc(src, line, col);

    Report::build(ariadne::ReportKind::Error, "test", offset)
        .with_label(Label::new(("test", offset..offset)).with_message("Something went wrong here!"))
        .with_message(msg)
        .with_note("We'd like to give better errors, but serde errors are horrible to work with...")
        .finish()
        .eprint(("test", Source::from(src)))
        .expect("writing error to stderr failed");
    process::exit(1)
}

/// Reconstruct a byte offset from the line + column numbers typical from serde crates
fn try_reconstruct_loc(src: &str, line_nr: usize, col_nr: usize) -> usize {
    let (line_nr, col_nr) = (line_nr - 1, col_nr - 1);

    src.lines()
        .enumerate()
        .fold(0, |acc, (i, line)| match i.cmp(&line_nr) {
            std::cmp::Ordering::Less => acc + line.len() + 1,
            std::cmp::Ordering::Equal => acc + col_nr,
            std::cmp::Ordering::Greater => acc,
        })
}
