defmodule Mobilizon.GraphQL.Resolvers.ReportTest do
  use Mobilizon.Web.ConnCase
  use Mobilizon.Tests.Helpers

  import Mobilizon.Factory

  alias Mobilizon.Actors.Actor
  alias Mobilizon.Config
  alias Mobilizon.Events.Event
  alias Mobilizon.GraphQL.AbsintheHelpers
  alias Mobilizon.Reports.{Note, Report}
  alias Mobilizon.Users.User

  import Swoosh.TestAssertions

  describe "Resolver: Report a content" do
    @create_report_mutation """
    mutation CreateReport($reportedId: ID!, $eventId: ID, $content: String) {
      createReport(
        reportedId: $reportedId,
        eventId: $eventId,
        content: $content
      ) {
          content,
          reporter {
            id
          },
          event {
            id
          },
          status
        }
      }
    """

    setup do: clear_config([:anonymous, :reports])

    setup %{conn: conn} do
      Mobilizon.Config.clear_config_cache()
      anonymous_actor_id = Config.anonymous_actor_id()
      {:ok, conn: conn, anonymous_actor_id: anonymous_actor_id}
    end

    test "create_report/3 creates a report", %{conn: conn} do
      %User{email: admin_email} = insert(:user, email: "loulou@example.com", role: :administrator)
      %User{} = user_reporter = insert(:user)
      %Actor{} = reporter = insert(:actor, user: user_reporter)
      %Actor{} = reported = insert(:actor)
      %Event{} = event = insert(:event, organizer_actor: reported)

      res =
        conn
        |> auth_conn(user_reporter)
        |> AbsintheHelpers.graphql_query(
          query: @create_report_mutation,
          variables: %{
            reportedId: reported.id,
            eventId: event.id,
            content: "This is an issue"
          }
        )

      assert res["errors"] == nil
      assert res["data"]["createReport"]["content"] == "This is an issue"
      assert res["data"]["createReport"]["status"] == "OPEN"
      assert res["data"]["createReport"]["event"]["id"] == to_string(event.id)

      assert res["data"]["createReport"]["reporter"]["id"] ==
               to_string(reporter.id)

      assert_email_sent(to: admin_email)
    end

    test "create_report/3 without being connected doesn't create any report", %{conn: conn} do
      %Actor{} = reported = insert(:actor)

      res =
        conn
        |> AbsintheHelpers.graphql_query(
          query: @create_report_mutation,
          variables: %{
            reportedId: reported.id,
            content: "This is an issue"
          }
        )

      assert res["errors"] |> hd |> Map.get("message") ==
               "You need to be logged-in to create reports"
    end

    test "create_report/3 anonymously creates a report if config has allowed", %{
      conn: conn,
      anonymous_actor_id: anonymous_actor_id
    } do
      %Actor{} = reported = insert(:actor)
      Config.put([:anonymous, :reports, :allowed], true)

      res =
        conn
        |> AbsintheHelpers.graphql_query(
          query: @create_report_mutation,
          variables: %{
            reportedId: reported.id,
            content: "This is an issue"
          }
        )

      assert is_nil(res["errors"])
      assert res["data"]["createReport"]["content"] == "This is an issue"
      assert res["data"]["createReport"]["status"] == "OPEN"

      assert res["data"]["createReport"]["reporter"]["id"] ==
               to_string(anonymous_actor_id)
    end
  end

  describe "Resolver: update a report" do
    @update_report_mutation """
    mutation UpdateReport($reportId: ID!, $status: ReportStatus!) {
      updateReportStatus(reportId: $reportId, status: $status) {
        content,
        reporter {
          id
        },
        event {
          id
        },
        status
      }
    }
    """

    test "update_report/3 updates the report's status", %{conn: conn} do
      %User{} = user_moderator = insert(:user, role: :moderator)
      %Actor{} = _actor_moderator = insert(:actor, user: user_moderator)
      %Report{} = report = insert(:report)

      res =
        conn
        |> auth_conn(user_moderator)
        |> AbsintheHelpers.graphql_query(
          query: @update_report_mutation,
          variables: %{reportId: report.id, status: "RESOLVED"}
        )

      assert is_nil(res["errors"])

      assert res["data"]["updateReportStatus"]["content"] ==
               "This is problematic"

      assert res["data"]["updateReportStatus"]["status"] == "RESOLVED"

      assert res["data"]["updateReportStatus"]["reporter"]["id"] ==
               to_string(report.reporter.id)
    end

    test "create_report/3 without being connected doesn't create any report", %{conn: conn} do
      %Report{} = report = insert(:report)

      res =
        conn
        |> AbsintheHelpers.graphql_query(
          query: @update_report_mutation,
          variables: %{reportId: report.id, status: "RESOLVED"}
        )

      assert res["errors"] |> hd |> Map.get("message") ==
               "You need to be logged in"
    end

    test "update_report/3 without being a moderator doesn't update any report", %{conn: conn} do
      %User{} = user = insert(:user)
      %Report{} = report = insert(:report)

      res =
        conn
        |> auth_conn(user)
        |> AbsintheHelpers.graphql_query(
          query: @update_report_mutation,
          variables: %{reportId: report.id, status: "RESOLVED"}
        )

      assert res["errors"] |> hd |> Map.get("message") ==
               "You don't have permission to do this"
    end
  end

  describe "Resolver: Get list of reports" do
    @reports_query """
    query ListReports($page: Int, $limit: Int) {
      reports(page: $page, limit: $limit) {
        elements {
          id
          reported {
            id
            preferredUsername
          }
          content,
          updatedAt
        }
        total
      }
    }
    """

    test "get an empty list of reports", %{conn: conn} do
      %User{} = user_moderator = insert(:user, role: :moderator)

      res = AbsintheHelpers.graphql_query(conn, query: @reports_query)

      assert hd(res["errors"])["message"] ==
               "You need to be logged in"

      res =
        conn
        |> auth_conn(user_moderator)
        |> AbsintheHelpers.graphql_query(query: @reports_query)

      assert res["errors"] == nil
      assert res["data"]["reports"]["elements"] == []
    end

    test "get a list of reports", %{conn: conn} do
      %User{} = user_moderator = insert(:user, role: :moderator)

      # Report don't hold millisecond information so we need to wait a bit
      # between each insert to keep order
      %Report{id: report_1_id} = insert(:report, content: "My content 1")
      Process.sleep(1000)
      %Report{id: report_2_id} = insert(:report, content: "My content 2")
      Process.sleep(1000)
      %Report{id: report_3_id} = insert(:report, content: "My content 3")
      %Report{} = insert(:report, status: :closed)

      res =
        conn
        |> auth_conn(user_moderator)
        |> AbsintheHelpers.graphql_query(query: @reports_query)

      assert res["errors"] == nil
      assert res["data"]["reports"]["total"] == 3

      assert res["data"]["reports"]["elements"]
             |> Enum.map(fn report -> Map.get(report, "id") end) ==
               Enum.map([report_3_id, report_2_id, report_1_id], &to_string/1)

      res =
        conn
        |> auth_conn(user_moderator)
        |> AbsintheHelpers.graphql_query(query: @reports_query, variables: %{page: 2, limit: 2})

      assert res["errors"] == nil
      assert res["data"]["reports"]["total"] == 3
      assert res["data"]["reports"]["elements"] |> length == 1

      res =
        conn
        |> auth_conn(user_moderator)
        |> AbsintheHelpers.graphql_query(query: @reports_query, variables: %{page: 3, limit: 2})

      assert res["errors"] == nil
      assert res["data"]["reports"]["elements"] |> length == 0
    end
  end

  describe "Resolver: View a report" do
    test "view a report", %{conn: conn} do
      %User{} = user_moderator = insert(:user, role: :moderator)
      %Actor{} = insert(:actor, user: user_moderator)
      %Actor{} = reporter = insert(:actor)
      %Report{} = report = insert(:report, reporter: reporter)

      query = """
      {
        report (id: "#{report.id}") {
          id,
          reported {
            preferredUsername
          },
          reporter {
            preferredUsername
          },
          event {
            title
          },
          comments {
            text
          },
          content
        }
      }
      """

      res =
        conn
        |> get("/api", AbsintheHelpers.query_skeleton(query, "report"))

      assert json_response(res, 200)["errors"] |> hd |> Map.get("message") ==
               "You need to be logged in"

      res =
        conn
        |> auth_conn(user_moderator)
        |> get("/api", AbsintheHelpers.query_skeleton(query, "report"))

      assert json_response(res, 200)["errors"] == nil

      assert json_response(res, 200)["data"]["report"]["reported"]["preferredUsername"] ==
               report.reported.preferred_username

      assert json_response(res, 200)["data"]["report"]["reporter"]["preferredUsername"] ==
               reporter.preferred_username

      assert json_response(res, 200)["data"]["report"]["content"] == report.content
      assert json_response(res, 200)["data"]["report"]["event"]["title"] == report.event.title

      assert json_response(res, 200)["data"]["report"]["comments"] |> hd |> Map.get("text") ==
               report.comments |> hd |> Map.get(:text)
    end
  end

  describe "Resolver: Add a note on a report" do
    @report_note_content "I agree with this this report"

    test "create_report_note/3 creates a report note", %{conn: conn} do
      %User{} = user_moderator = insert(:user, role: :moderator)
      %Actor{} = moderator = insert(:actor, user: user_moderator)
      %Report{id: report_id} = insert(:report)

      mutation = """
          mutation {
            createReportNote(
              report_id: #{report_id},
              content: "#{@report_note_content}"
            ) {
                content,
                moderator {
                  preferred_username
                },
                report {
                  id
                }
              }
            }
      """

      res =
        conn
        |> auth_conn(user_moderator)
        |> post("/api", AbsintheHelpers.mutation_skeleton(mutation))

      assert json_response(res, 200)["errors"] == nil

      assert json_response(res, 200)["data"]["createReportNote"]["content"] ==
               @report_note_content

      assert json_response(res, 200)["data"]["createReportNote"]["moderator"][
               "preferred_username"
             ] == moderator.preferred_username

      assert json_response(res, 200)["data"]["createReportNote"]["report"]["id"] ==
               to_string(report_id)
    end

    test "delete_report_note deletes a report note", %{conn: conn} do
      %User{} = user_moderator = insert(:user, role: :moderator)
      %Actor{} = moderator = insert(:actor, user: user_moderator)
      %Note{id: report_note_id} = insert(:report_note, moderator: moderator)

      mutation = """
          mutation {
            deleteReportNote(
              note_id: #{report_note_id},
            ) {
                id
              }
            }
      """

      res =
        conn
        |> auth_conn(user_moderator)
        |> post("/api", AbsintheHelpers.mutation_skeleton(mutation))

      assert json_response(res, 200)["errors"] == nil

      assert json_response(res, 200)["data"]["deleteReportNote"]["id"] ==
               to_string(report_note_id)
    end
  end
end
