require_relative "test_helper"
require "tmpdir"
require "fileutils"
require "json"

class InspectorTest < Minitest::Test
  def test_detects_docs_issues_dir
    Dir.mktmpdir do |dir|
      FileUtils.mkdir_p(File.join(dir, "docs/issues"))
      assert_includes Backlog::Inspector.issue_files(dir), "docs/issues"
    end
  end

  def test_detects_issues_md_file
    Dir.mktmpdir do |dir|
      File.write(File.join(dir, "ISSUES.md"), "# issues\n")
      assert_includes Backlog::Inspector.issue_files(dir), "ISSUES.md"
    end
  end

  def test_returns_empty_when_nothing_present
    Dir.mktmpdir do |dir|
      assert_empty Backlog::Inspector.issue_files(dir)
    end
  end
end

class ClassifyIssueShapeTest < Minitest::Test
  def issue(number, title: "issue #{number}", labels: [], body: "")
    { number: number, title: title, labels: labels, body: body }
  end

  def test_nil_when_metadata_unavailable
    assert_nil Backlog::Inspector.classify_issue_shape(nil)
  end

  def test_nil_when_fewer_than_three_issues_even_with_chain_labels
    issues = [issue(1, labels: ["blocked"]), issue(2, labels: ["blocked"])]
    assert_nil Backlog::Inspector.classify_issue_shape(issues)
  end

  def test_flat_adhoc_when_no_issue_shows_chain_evidence
    issues = [issue(1), issue(2), issue(3, body: "fixed in #1 last week")]
    assert_equal "flat-adhoc", Backlog::Inspector.classify_issue_shape(issues)
  end

  def test_sequential_chain_from_labels
    issues = [
      issue(1, labels: ["Blocked-By"]),
      issue(2, labels: ["depends-on"]),
      issue(3, labels: ["enhancement"])
    ]
    assert_equal "sequential-chain", Backlog::Inspector.classify_issue_shape(issues)
  end

  def test_negated_label_token_does_not_count
    issues = [issue(1, labels: ["not-blocked"]), issue(2), issue(3)]
    assert_equal "flat-adhoc", Backlog::Inspector.classify_issue_shape(issues)
  end

  def test_cross_reference_near_dependency_language_counts
    issues = [
      issue(1, body: "This is blocked by #2 until the schema lands."),
      issue(2, body: "Must land after #3."),
      issue(3, body: "Standalone groundwork.")
    ]
    assert_equal "sequential-chain", Backlog::Inspector.classify_issue_shape(issues)
  end

  def test_plain_cross_reference_without_dependency_language_does_not_count
    issues = [
      issue(1, body: "See also #2 for background."),
      issue(2, body: "Related: #3."),
      issue(3)
    ]
    assert_equal "flat-adhoc", Backlog::Inspector.classify_issue_shape(issues)
  end

  def test_reference_to_number_outside_fetched_set_does_not_count
    issues = [
      issue(1, body: "blocked by #99 upstream"),
      issue(2),
      issue(3)
    ]
    assert_equal "flat-adhoc", Backlog::Inspector.classify_issue_shape(issues)
  end

  def test_dependency_language_too_far_from_reference_does_not_count
    padding = "x" * 60
    issues = [
      issue(1, body: "blocked #{padding} see #2"),
      issue(2),
      issue(3)
    ]
    assert_equal "flat-adhoc", Backlog::Inspector.classify_issue_shape(issues)
  end

  def test_mixed_when_some_but_not_most_show_evidence
    issues = [issue(1, labels: ["blocked"]), issue(2), issue(3)]
    assert_equal "mixed", Backlog::Inspector.classify_issue_shape(issues)
  end

  def test_self_reference_does_not_count
    issues = [
      issue(1, body: "depends on #1 (this issue)"),
      issue(2),
      issue(3)
    ]
    assert_equal "flat-adhoc", Backlog::Inspector.classify_issue_shape(issues)
  end
end

class InspectCliContractTest < Minitest::Test
  BIN = File.expand_path("../bin/os-backlog", __dir__)

  # `inspect` must always expose the issue_shape key, even when every
  # network-ish check degrades (tmpdir: no git remote, no config). Value is
  # null here because no metadata is fetchable — that's the fail-soft path.
  def test_inspect_json_always_contains_issue_shape_key
    Dir.mktmpdir do |dir|
      out = Dir.chdir(dir) { `ruby #{BIN} inspect 2>/dev/null` }
      findings = JSON.parse(out)
      assert findings.key?("issue_shape"), "inspect JSON missing issue_shape key"
      assert_nil findings["issue_shape"]
    end
  end
end
