use common::array::ensure_all_arrays_multiline;
use common::table::Tables;
use indoc::indoc;

use super::{assert_valid_toml, collect_entries, format_syntax, parse};
use crate::build_system::fix;

fn format_build_systems_core(start: &str, keep_full_version: bool) -> String {
    let root_ast = parse(start);
    let count = root_ast.children_with_tokens().count();
    let tables = Tables::from_ast(&root_ast);
    fix(&tables, keep_full_version);
    let entries = collect_entries(&tables);
    root_ast.splice_children(0..count, entries);
    ensure_all_arrays_multiline(&root_ast, 120);
    format_syntax(root_ast, 120)
}

fn format_build_systems_helper(start: &str, keep_full_version: bool) -> String {
    let result = format_build_systems_core(start, keep_full_version);
    assert_valid_toml(&result);
    result
}

#[test]
fn test_format_build_systems_no_build_system() {
    let start = indoc! {r""};
    let res = format_build_systems_helper(start, false);
    insta::assert_snapshot!(res, @"");
}

#[test]
fn test_format_build_systems_build_system_requires_no_keep() {
    let start = indoc! {r#"
    [build-system]
    requires=["a>=1.0.0", "b.c>=1.5.0"]
    "#};
    let res = format_build_systems_helper(start, false);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    requires = [ "a>=1", "b-c>=1.5" ]
    "#);
}

#[test]
fn test_format_build_systems_build_system_requires_keep() {
    let start = indoc! {r#"
    [build-system]
    requires=["a>=1.0.0", "b.c>=1.5.0"]
    "#};
    let res = format_build_systems_helper(start, true);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    requires = [ "a>=1.0.0", "b-c>=1.5.0" ]
    "#);
}

#[test]
fn test_format_build_systems_join() {
    let start = indoc! {r#"
    [build-system]
    requires=["a"]
    [build-system]
    build-backend = "hatchling.build"
    [[build-system.a]]
    name = "Hammer"
    [[build-system.a]]  # empty table within the array
    [[build-system.a]]
    name = "Nail"
    "#};
    let res = format_build_systems_core(start, false);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    build-backend = "hatchling.build"
    [build-system]
    [[build-system.a]]
    name = "Hammer"[[build-system.a]]  # empty table within the array
    [[build-system.a]]
    name = "Nail"
    "#);
}

#[test]
fn test_format_build_systems_issue_2_python_version_marker() {
    let start = indoc! {r#"
    [build-system]
    requires = [
      "cython==3.0.11",
      "numpy==1.22.2; python_version<'3.9'",
      "numpy>=2; python_version>='3.9'",
      "setuptools",
    ]
    "#};
    let res = format_build_systems_helper(start, true);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    requires = [
      "cython==3.0.11",
      "numpy==1.22.2; python_version<'3.9'",
      "numpy>=2; python_version>='3.9'",
      "setuptools",
    ]
    "#);
}

#[test]
fn test_format_build_systems_backend_path_sorting() {
    let start = indoc! {r#"
    [build-system]
    build-backend = "backend"
    backend-path = ["src", "lib", "another"]
    "#};
    let res = format_build_systems_helper(start, false);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    build-backend = "backend"
    backend-path = [ "another", "lib", "src" ]
    "#);
}

#[test]
fn test_format_build_systems_setuptools_backend_removes_bare_wheel() {
    let start = indoc! {r#"
    [build-system]
    requires = ["setuptools >= 45", "wheel"]
    build-backend = "setuptools.build_meta"
    "#};
    let res = format_build_systems_helper(start, false);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    build-backend = "setuptools.build_meta"
    requires = [ "setuptools>=45" ]
    "#);
}

#[test]
fn test_format_build_systems_setuptools_legacy_backend_removes_bare_wheel() {
    let start = indoc! {r#"
    [build-system]
    requires = ["setuptools", "wheel"]
    build-backend = "setuptools.build_meta:__legacy__"
    "#};
    let res = format_build_systems_helper(start, false);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    build-backend = "setuptools.build_meta:__legacy__"
    requires = [ "setuptools" ]
    "#);
}

#[test]
fn test_format_build_systems_setuptools_backend_keeps_constrained_wheel() {
    let start = indoc! {r#"
    [build-system]
    requires = ["setuptools", "wheel>=0.40"]
    build-backend = "setuptools.build_meta"
    "#};
    let res = format_build_systems_helper(start, false);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    build-backend = "setuptools.build_meta"
    requires = [ "setuptools", "wheel>=0.40" ]
    "#);
}

#[test]
fn test_format_build_systems_setuptools_backend_keeps_wheel_with_marker() {
    let start = indoc! {r#"
    [build-system]
    requires = ["setuptools", "wheel; sys_platform=='win32'"]
    build-backend = "setuptools.build_meta"
    "#};
    let res = format_build_systems_helper(start, false);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    build-backend = "setuptools.build_meta"
    requires = [ "setuptools", "wheel; sys_platform=='win32'" ]
    "#);
}

#[test]
fn test_format_build_systems_other_backend_keeps_wheel() {
    let start = indoc! {r#"
    [build-system]
    requires = ["hatchling", "wheel"]
    build-backend = "hatchling.build"
    "#};
    let res = format_build_systems_helper(start, false);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    build-backend = "hatchling.build"
    requires = [ "hatchling", "wheel" ]
    "#);
}

#[test]
fn test_format_build_systems_no_backend_keeps_wheel() {
    let start = indoc! {r#"
    [build-system]
    requires = ["setuptools", "wheel"]
    "#};
    let res = format_build_systems_helper(start, false);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    requires = [ "setuptools", "wheel" ]
    "#);
}

#[test]
fn test_format_build_systems_backend_path_keeps_wheel() {
    let start = indoc! {r#"
    [build-system]
    requires = ["setuptools", "wheel"]
    build-backend = "setuptools.build_meta"
    backend-path = ["_custom"]
    "#};
    let res = format_build_systems_helper(start, false);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    build-backend = "setuptools.build_meta"
    requires = [ "setuptools", "wheel" ]
    backend-path = [ "_custom" ]
    "#);
}

#[test]
fn test_format_build_systems_no_setuptools_keeps_wheel() {
    let start = indoc! {r#"
    [build-system]
    requires = ["wheel"]
    build-backend = "setuptools.build_meta"
    "#};
    let res = format_build_systems_helper(start, false);
    insta::assert_snapshot!(res, @r#"
    [build-system]
    build-backend = "setuptools.build_meta"
    requires = [ "wheel" ]
    "#);
}
