///|
test "qualified parent identifies its binding package" {
  let options : SubclassCommandOptions = {
    inputs: ["renderer.mbt"],
    config_path: None,
    gir_dirs: [],
    output_dir: None,
    symbol_prefix: None,
  }
  let source : @subclass_model.SubclassSource = {
    declarations: [
      {
        name: "MotivaPangoSvgRenderer",
        parent: "@pango.Renderer",
        state: "MotivaPangoSvgRendererState",
        gtype_name: Some("MotivaPangoSvgRenderer"),
      },
    ],
    overrides: [
      {
        state: "MotivaPangoSvgRendererState",
        trait_name: "@pango.VRendererDrawGlyphItem",
        method_name: "draw_glyph_item",
      },
    ],
  }
  assert_eq(options.resolve_parent_import_alias(source), "pango")
  assert_eq(options.infer_symbol_prefix(source), "motiva_pango_svg_renderer")
}

///|
test "ancestor override does not choose the parent binding" {
  let options : SubclassCommandOptions = {
    inputs: ["custom_button.mbt"],
    config_path: None,
    gir_dirs: [],
    output_dir: None,
    symbol_prefix: None,
  }
  let source : @subclass_model.SubclassSource = {
    declarations: [
      {
        name: "CustomButton",
        parent: "@gtk.Button",
        state: "CustomButtonState",
        gtype_name: None,
      },
    ],
    overrides: [
      {
        state: "CustomButtonState",
        trait_name: "@gobject.VObjectDispose",
        method_name: "dispose",
      },
    ],
  }
  assert_eq(options.resolve_parent_import_alias(source), "gtk")
}

///|
test "rejects an unqualified parent" {
  let options : SubclassCommandOptions = {
    inputs: ["custom_button.mbt"],
    config_path: None,
    gir_dirs: [],
    output_dir: None,
    symbol_prefix: None,
  }
  let source : @subclass_model.SubclassSource = {
    declarations: [
      {
        name: "CustomButton",
        parent: "Button",
        state: "CustomButtonState",
        gtype_name: None,
      },
    ],
    overrides: [
      {
        state: "CustomButtonState",
        trait_name: "@gobject.VObjectDispose",
        method_name: "dispose",
      },
    ],
  }
  try options.resolve_parent_import_alias(source) catch {
    SubclassCommandError::Failure(message) =>
      inspect(
        message,
        content="CustomButton: parent 'Button' must be qualified as @pkg.Type",
      )
  } noraise {
    _ => fail("expected an unqualified-parent error")
  }
}

///|
test "one invocation has one parent binding package" {
  let options : SubclassCommandOptions = {
    inputs: ["custom_button.mbt", "renderer.mbt"],
    config_path: None,
    gir_dirs: [],
    output_dir: None,
    symbol_prefix: None,
  }
  let source : @subclass_model.SubclassSource = {
    declarations: [
      {
        name: "CustomButton",
        parent: "@gtk.Button",
        state: "CustomButtonState",
        gtype_name: None,
      },
      {
        name: "CustomRenderer",
        parent: "@pango.Renderer",
        state: "CustomRendererState",
        gtype_name: None,
      },
    ],
    overrides: [],
  }
  try options.resolve_parent_import_alias(source) catch {
    SubclassCommandError::Failure(message) =>
      inspect(
        message,
        content="one invocation cannot load parent bindings from both @gtk and @pango",
      )
  } noraise {
    _ => fail("expected a mixed-parent-package error")
  }
}

///|
test "moon.pkg resolves default and custom package aliases" {
  let source =
    #|import {
    #|  "tonyfettes/gobject",
    #|  "tonyfettes/pango" @rendering,
    #|}
    #|
    #|import {
    #|  "tonyfettes/gtk" @test_gtk,
    #|} for "test"
    #|
  let manifest = PackageManifest::parse("/application/moon.pkg", source)
  let default_import = manifest.resolve_import("gobject")
  assert_eq(default_import.import_path, "tonyfettes/gobject")
  let custom_import = manifest.resolve_import("rendering")
  assert_eq(custom_import.import_path, "tonyfettes/pango")
  assert_eq(
    custom_import.materialized_config_path(
      @path.Path("/application/.mooncakes"),
    ).0,
    "/application/.mooncakes/tonyfettes/pango/gir.json",
  )
  assert_eq(
    custom_import.local_config_path(@path.Path("/bindings")).0,
    "/bindings/pango/gir.json",
  )
}

///|
test "moon.pkg resolves imports in an executable package" {
  let source =
    #|import {
    #|  "tonyfettes/gtk",
    #|}
    #|
    #|pkgtype(kind: "executable")
    #|
  let manifest = PackageManifest::parse("/application/moon.pkg", source)
  assert_eq(manifest.resolve_import("gtk").import_path, "tonyfettes/gtk")
}

///|
test "moon.pkg rejects an alias that is not an ordinary import" {
  let source =
    #|import {
    #|  "tonyfettes/pango",
    #|}
    #|
  let manifest = PackageManifest::parse("/application/moon.pkg", source)
  try manifest.resolve_import("rendering") catch {
    SubclassCommandError::Failure(message) =>
      inspect(
        message,
        content="/application/moon.pkg: @rendering is not an ordinary package import",
      )
  } noraise {
    _ => fail("expected a missing-import error")
  }
}

///|
test "repeated --gir-dir values preserve command-line order" {
  let options = SubclassCommandOptions::parse(argv=[
    "--gir-dir", "/first/gir-1.0", "--gir-dir", "/second/gir-1.0", "renderer.mbt",
  ])
  assert_eq(options.gir_dirs, ["/first/gir-1.0", "/second/gir-1.0"])
}

///|
async test "GIR namespaces resolve independently across directories" {
  let primary_dir = @path.Path("subclass/testdata/primary").resolve()
  let dependency_dir = @path.Path("subclass/testdata/dependency").resolve()
  let search_path = GirSearchPath([primary_dir.0, dependency_dir.0][:])
  assert_eq(
    search_path.resolve_gir("TestPrimary-1.0").0,
    primary_dir.join(@path.Path("TestPrimary-1.0.gir")).0,
  )
  assert_eq(
    search_path.resolve_gir("TestDependency-1.0").0,
    dependency_dir.join(@path.Path("TestDependency-1.0.gir")).0,
  )
}