//! `pic routes` smoke + edge tests. Covers the create → ls → match →
//! rm round trip and the validation surface (unknown script id,
//! conflict on duplicate).

use predicates::prelude::*;

use crate::common;

#[ignore = "needs DATABASE_URL pointing at a running Postgres"]
#[test]
fn create_ls_match_remove_round_trip() {
    let Some(fx) = common::fixture_or_skip() else {
        return;
    };
    let env = common::admin_env(fx);
    let (script_id, guard) = common::deploy_fixture(&env, "routes-rt", "hello.rhai");
    let app_slug = guard.slug().to_string();

    // Create — exact path, any method, sync dispatch by default.
    common::pic_as(&env)
        .args([
            "routes", "create", "--script", &script_id, "--path", "/hook", "--method", "POST",
        ])
        .assert()
        .success()
        .stdout(predicate::str::contains("Created route"));

    // Ls — header columns and our row present.
    let out = common::pic_as(&env)
        .args(["routes", "ls", &script_id])
        .output()
        .expect("routes ls");
    assert!(out.status.success(), "routes ls failed: {out:?}");
    let stdout = String::from_utf8(out.stdout).unwrap();
    let mut lines = stdout.lines();
    let header = lines.next().expect("header row");
    assert_eq!(
        common::cells(header),
        vec![
            "id",
            "method",
            "host",
            "path_kind",
            "path",
            "dispatch",
            "created_at"
        ]
    );
    let row = lines
        .map(common::cells)
        .find(|c| c.get(4).copied() == Some("/hook"))
        .unwrap_or_else(|| panic!("/hook row not in routes ls output: {stdout}"));
    assert_eq!(row[1], "POST");
    assert_eq!(row[5], "sync");
    let route_id = row[0].to_string();

    // Match — POST /hook on the app's any-host space resolves.
    let out = common::pic_as(&env)
        .args([
            "routes",
            "match",
            "--app",
            &app_slug,
            "--method",
            "POST",
            "http://localhost/hook",
        ])
        .output()
        .expect("routes match");
    assert!(out.status.success(), "routes match failed: {out:?}");
    let stdout = String::from_utf8(out.stdout).unwrap();
    assert!(
        stdout.contains("matched") && stdout.contains("true"),
        "match should have hit: {stdout}"
    );
    assert!(
        stdout.contains(&route_id),
        "match output should name the route id: {stdout}"
    );

    // Rm — drops the row.
    common::pic_as(&env)
        .args(["routes", "rm", &route_id])
        .assert()
        .success()
        .stdout(predicate::str::contains(format!(
            "Deleted route {route_id}"
        )));

    let out = common::pic_as(&env)
        .args(["routes", "ls", &script_id])
        .output()
        .expect("routes ls after rm");
    assert!(out.status.success());
    let stdout = String::from_utf8(out.stdout).unwrap();
    assert!(
        !stdout.lines().skip(1).any(|l| l.contains(&route_id)),
        "deleted route still in ls: {stdout}"
    );
}

#[ignore = "needs DATABASE_URL pointing at a running Postgres"]
#[test]
fn create_async_dispatch_is_persisted() {
    let Some(fx) = common::fixture_or_skip() else {
        return;
    };
    let env = common::admin_env(fx);
    let (script_id, _guard) = common::deploy_fixture(&env, "routes-async", "hello.rhai");

    common::pic_as(&env)
        .args([
            "routes",
            "create",
            "--script",
            &script_id,
            "--path",
            "/bg",
            "--method",
            "POST",
            "--dispatch",
            "async",
        ])
        .assert()
        .success();

    let out = common::pic_as(&env)
        .args(["routes", "ls", &script_id])
        .output()
        .expect("routes ls");
    let stdout = String::from_utf8(out.stdout).unwrap();
    let row = stdout
        .lines()
        .skip(1)
        .map(common::cells)
        .find(|c| c.get(4).copied() == Some("/bg"))
        .unwrap_or_else(|| panic!("/bg row missing: {stdout}"));
    assert_eq!(row[5], "async", "dispatch column should be async: {row:?}");
}

#[ignore = "needs DATABASE_URL pointing at a running Postgres"]
#[test]
fn create_against_unknown_script_404s() {
    let Some(fx) = common::fixture_or_skip() else {
        return;
    };
    let env = common::admin_env(fx);
    // Bogus UUID-shaped script id so the path matcher accepts it
    // but the script lookup 404s.
    common::pic_as(&env)
        .args([
            "routes",
            "create",
            "--script",
            "00000000-0000-0000-0000-000000000000",
            "--path",
            "/x",
        ])
        .assert()
        .failure()
        .stderr(predicate::str::contains("HTTP 404"));
}
