require_relative "test_helper"
require "json"

class FinderTest < Minitest::Test
  include AdrTestHelpers

  def adr(id:, title:, status: "Accepted", paths: [], components: [])
    <<~MD
      ---
      id: "#{id}"
      date: 2026-07-03
      status: #{status}
      supersedes:
      superseded-by:
      affected-paths: [#{paths.join(', ')}]
      affected-components: [#{components.join(', ')}]
      ---

      # #{id} — #{title}

      ## Context

      c

      ## Decision

      d

      ## Consequences

      q

      ## Alternatives rejected

      a
    MD
  end

  def storage_project(&block)
    with_project(
      "0001-storage.md" => adr(id: "0001", title: "Storage", paths: %w[lib/storage db/],
                               components: %w[storage]),
      "0002-old-storage.md" => adr(id: "0002", title: "Old storage", status: "Superseded",
                                   paths: %w[lib/storage]),
      "0003-auth.md" => adr(id: "0003", title: "Auth", paths: %w[lib/auth],
                            components: %w[auth]),
      &block
    )
  end

  def finder(root, graph: nil)
    Adr::Finder.new(repository: Adr::Repository.new(root: root),
                    graph_path: graph || File.join(root, "graphify-out", "graph.json"))
  end

  def test_path_prefix_match_narrows
    storage_project do |root|
      results = finder(root).find(paths: ["lib/storage/adapter.rb"])
      assert_equal ["0001"], results.map { |r| r["id"] }
      assert_equal ["path"], results.first["matched_on"]
    end
  end

  def test_directory_query_matches_file_scoped_adr
    storage_project do |root|
      results = finder(root).find(paths: ["db"])
      assert_equal ["0001"], results.map { |r| r["id"] }
    end
  end

  def test_component_match
    storage_project do |root|
      results = finder(root).find(components: ["auth"])
      assert_equal ["0003"], results.map { |r| r["id"] }
      assert_equal ["component"], results.first["matched_on"]
    end
  end

  def test_accepted_only_by_default_with_override
    storage_project do |root|
      default = finder(root).find(paths: ["lib/storage"])
      assert_equal ["0001"], default.map { |r| r["id"] }
      all = finder(root).find(paths: ["lib/storage"], all_statuses: true)
      assert_equal %w[0001 0002], all.map { |r| r["id"] }.sort
    end
  end

  def test_no_match_returns_empty
    storage_project do |root|
      assert_empty finder(root).find(paths: ["src/ui"])
    end
  end

  GRAPH = {
    "nodes" => [
      { "id" => "n1", "source_file" => "src/api/handler.rb" },
      { "id" => "n2", "source_file" => "lib/storage/adapter.rb" },
      { "id" => "n3", "source_file" => "lib/auth/session.rb" }
    ],
    "links" => [
      { "source" => "n1", "target" => "n2" }
    ]
  }.freeze

  def test_graph_layer_expands_one_hop
    storage_project do |root|
      graph_dir = File.join(root, "graphify-out")
      FileUtils.mkdir_p(graph_dir)
      File.write(File.join(graph_dir, "graph.json"), JSON.generate(GRAPH))
      # src/api/handler.rb is in no ADR's affected-paths, but is one hop from
      # lib/storage/adapter.rb which 0001 covers.
      results = finder(root).find(paths: ["src/api/handler.rb"])
      assert_equal ["0001"], results.map { |r| r["id"] }
      assert_equal ["graph"], results.first["matched_on"]
    end
  end

  def test_degrades_gracefully_without_graph
    storage_project do |root|
      assert_empty finder(root).find(paths: ["src/api/handler.rb"])
      assert_equal ["0001"], finder(root).find(paths: ["lib/storage"]).map { |r| r["id"] }
    end
  end

  def test_degrades_gracefully_on_corrupt_graph
    storage_project do |root|
      graph_dir = File.join(root, "graphify-out")
      FileUtils.mkdir_p(graph_dir)
      File.write(File.join(graph_dir, "graph.json"), "{not json")
      assert_equal ["0001"], finder(root).find(paths: ["lib/storage"]).map { |r| r["id"] }
    end
  end
end
