use std::process;

use crate::workdir::Workdir;
use crate::Csv;

fn no_headers(cmd: &mut process::Command) {
    cmd.arg("--no-headers");
}

fn pad(cmd: &mut process::Command) {
    cmd.arg("--pad");
}

fn run_cat<X, Y, Z, F>(test_name: &str, which: &str, rows1: X, rows2: Y, modify_cmd: F) -> Z
where
    X: Csv,
    Y: Csv,
    Z: Csv,
    F: FnOnce(&mut process::Command),
{
    let wrk = Workdir::new(test_name);
    wrk.create("in1.csv", rows1);
    wrk.create("in2.csv", rows2);

    let mut cmd = wrk.command("cat");
    modify_cmd(cmd.arg(which).arg("in1.csv").arg("in2.csv"));
    wrk.read_stdout(&mut cmd)
}

#[test]
fn cat_rows_space() {
    let rows = vec![svec!["\u{0085}"]];
    let expected = rows.clone();
    let (rows1, rows2) = if rows.is_empty() {
        (vec![], vec![])
    } else {
        let (rows1, rows2) = rows.split_at(rows.len() / 2);
        (rows1.to_vec(), rows2.to_vec())
    };
    let got: Vec<Vec<String>> = run_cat("cat_rows_space", "rows", rows1, rows2, no_headers);
    assert_eq!(got, expected);
}

#[test]
fn cat_rows_headers() {
    let rows1 = vec![svec!["h1", "h2"], svec!["a", "b"]];
    let rows2 = vec![svec!["h1", "h2"], svec!["y", "z"]];

    let mut expected = rows1.clone();
    expected.extend(rows2.clone().into_iter().skip(1));

    let got: Vec<Vec<String>> = run_cat("cat_rows_headers", "rows", rows1, rows2, |_| ());
    assert_eq!(got, expected);
}

#[test]
fn cat_rows_paths() {
    let wrk = Workdir::new("cat_rows_paths");
    wrk.create("a.csv", vec![svec!["name"], svec!["John"]]);
    wrk.create("b.csv", vec![svec!["name"], svec!["Suzy"]]);
    wrk.create("p.csv", vec![svec!["path"], svec!["a.csv"], svec!["b.csv"]]);

    let mut cmd = wrk.command("cat");
    cmd.arg("rows")
        .args(["--path-column", "path"])
        .args(["--paths", "p.csv"]);

    let got: Vec<Vec<String>> = wrk.read_stdout(&mut cmd);
    let expected = vec![svec!["name"], svec!["John"], svec!["Suzy"]];
    assert_eq!(got, expected);
}

#[test]
fn cat_rows_source_column() {
    let wrk = Workdir::new("cat_rows_source_column");
    wrk.create("a.csv", vec![svec!["name"], svec!["John"]]);
    wrk.create("b.csv", vec![svec!["name"], svec!["Suzy"]]);

    let mut cmd = wrk.command("cat");
    cmd.arg("rows")
        .args(["--source-column", "source"])
        .arg("a.csv")
        .arg("b.csv");

    let got: Vec<Vec<String>> = wrk.read_stdout(&mut cmd);
    let expected = vec![
        svec!["source", "name"],
        svec!["a.csv", "John"],
        svec!["b.csv", "Suzy"],
    ];
    assert_eq!(got, expected);
}

#[test]
fn cat_rows_paths_source_column() {
    let wrk = Workdir::new("cat_rows_paths_source_column");
    wrk.create("a.csv", vec![svec!["name"], svec!["John"]]);
    wrk.create("b.csv", vec![svec!["name"], svec!["Suzy"]]);
    wrk.create("p.csv", vec![svec!["path"], svec!["a.csv"], svec!["b.csv"]]);

    let mut cmd = wrk.command("cat");
    cmd.arg("rows")
        .args(["--source-column", "source"])
        .args(["--path-column", "path"])
        .args(["--paths", "p.csv"]);

    let got: Vec<Vec<String>> = wrk.read_stdout(&mut cmd);
    let expected = vec![
        svec!["source", "name"],
        svec!["a.csv", "John"],
        svec!["b.csv", "Suzy"],
    ];
    assert_eq!(got, expected);
}

#[test]
fn cat_cols_headers() {
    let rows1 = vec![svec!["h1", "h2"], svec!["a", "b"]];
    let rows2 = vec![svec!["h3", "h4"], svec!["y", "z"]];

    let expected = vec![svec!["h1", "h2", "h3", "h4"], svec!["a", "b", "y", "z"]];
    let got: Vec<Vec<String>> = run_cat("cat_cols_headers", "columns", rows1, rows2, |_| ());
    assert_eq!(got, expected);
}

#[test]
fn cat_cols_no_pad() {
    let rows1 = vec![svec!["a", "b"]];
    let rows2 = vec![svec!["y", "z"], svec!["y", "z"]];

    let expected = vec![svec!["a", "b", "y", "z"]];
    let got: Vec<Vec<String>> = run_cat("cat_cols_headers", "columns", rows1, rows2, no_headers);
    assert_eq!(got, expected);
}

#[test]
fn cat_cols_pad() {
    let rows1 = vec![svec!["a", "b"]];
    let rows2 = vec![svec!["y", "z"], svec!["y", "z"]];

    let expected = vec![svec!["a", "b", "y", "z"], svec!["", "", "y", "z"]];
    let got: Vec<Vec<String>> = run_cat("cat_cols_headers", "columns", rows1, rows2, pad);
    assert_eq!(got, expected);
}