defmodule TarakanWeb.FeedControllerTest do
  use TarakanWeb.ConnCase

  alias Tarakan.FindingMemory

  test "the findings feed is a well-formed Atom document with absolute entry links", %{
    conn: conn
  } do
    submitter = github_account_fixture()
    repository = listed_github_repository_fixture(submitter)

    scan =
      repository
      |> scan_fixture(submitter, %{"findings_json" => findings_json_fixture(1)})
      |> publish_scan("public")

    [finding] = scan.findings
    base = TarakanWeb.Endpoint.url()

    conn = get(conn, ~p"/feeds/findings.xml")

    assert response_content_type(conn, :xml) =~ "application/atom+xml"
    assert get_resp_header(conn, "cache-control") == ["public, max-age=300"]

    body = response(conn, 200)
    assert_well_formed(body)

    assert body =~ ~s(<feed xmlns="http://www.w3.org/2005/Atom">)
    assert body =~ ~s(<link rel="self" href="#{base}/feeds/findings.xml"/>)
    assert body =~ "<id>#{base}/findings/#{finding.public_id}</id>"
    assert body =~ ~s(<link href="#{base}/findings/#{finding.public_id}"/>)
    assert body =~ finding.title
  end

  test "the findings feed excludes open and restricted findings", %{conn: conn} do
    submitter = github_account_fixture()
    repository = listed_github_repository_fixture(submitter)

    open_scan =
      scan_fixture(repository, submitter, %{"findings_json" => findings_json_fixture(1)})

    restricted =
      scan_fixture(repository, account_fixture(), %{"findings_json" => findings_json_fixture(1)})

    {:ok, _restricted} =
      Tarakan.Scans.update_visibility(
        Tarakan.Accounts.Scope.for_account(moderator_account_fixture()),
        restricted,
        "restricted",
        %{
          "moderation_reason" => "takedown_review",
          "moderation_notes" => "Restricted in this test to keep it out of the feed."
        }
      )

    body = conn |> get(~p"/feeds/findings.xml") |> response(200)

    for scan <- [open_scan, restricted], finding <- scan.findings do
      refute body =~ to_string(finding.public_id)
    end
  end

  test "the infestations feed is a well-formed Atom document with absolute entry links", %{
    conn: conn
  } do
    submitter = github_account_fixture()
    other = github_account_fixture()
    repo_a = listed_github_repository_fixture(submitter)
    {:ok, repo_b} = Tarakan.Repositories.register_github_repository("acme/widget", other)
    repo_b = listed_repository_fixture(repo_b)

    title = "Feed infestation sample #{System.unique_integer([:positive])}"

    findings = fn path ->
      Jason.encode!(%{
        "tarakan_scan_format" => 1,
        "findings" => [
          %{
            "file" => path,
            "severity" => "high",
            "title" => title,
            "description" => "Shared issue class for the patterns feed test."
          }
        ]
      })
    end

    scan_fixture(repo_a, submitter, %{"findings_json" => findings.("lib/a.ex")})
    scan_fixture(repo_b, other, %{"findings_json" => findings.("src/b.rs")})

    pattern_key = FindingMemory.pattern_key(title)
    base = TarakanWeb.Endpoint.url()

    conn = get(conn, ~p"/feeds/infestations.xml")

    assert response_content_type(conn, :xml) =~ "application/atom+xml"

    body = response(conn, 200)
    assert_well_formed(body)

    assert body =~ ~s(<link rel="self" href="#{base}/feeds/infestations.xml"/>)
    assert body =~ "<id>#{base}/infestations/#{pattern_key}</id>"
    assert body =~ title
  end

  defp assert_well_formed(xml) do
    {document, rest} = :xmerl_scan.string(String.to_charlist(xml))
    assert rest == []
    assert {:xmlElement, :feed, :feed, _, _, _, _, _, _, _, _, _} = document
  end

  defp publish_scan(scan, visibility) do
    scan = confirmation_fixture(scan, reviewer_account_fixture())
    scan = confirmation_fixture(scan, reviewer_account_fixture())

    scope = Tarakan.Accounts.Scope.for_account(moderator_account_fixture())

    if visibility == "public" do
      scan.repository
      |> Tarakan.Repositories.Repository.participation_changeset(%{
        participation_mode: "maintainer_verified"
      })
      |> Tarakan.Repo.update!()
    end

    {:ok, scan} =
      Tarakan.Scans.accept_scan(scope, scan, %{
        "moderation_reason" => "evidence_reviewed",
        "moderation_notes" =>
          "Two independent reviewers supplied reproducible evidence for the pinned commit."
      })

    {:ok, scan} =
      Tarakan.Scans.update_visibility(scope, scan, visibility, %{
        "moderation_reason" => "disclosure_reviewed",
        "moderation_notes" =>
          "Disclosure was separately reviewed for scope, secrets, and personal data.",
        "sensitive_data_reviewed" => "true"
      })

    scan
  end
end
