# frozen_string_literal: true
require "swagger_helper"

RSpec.describe "users" do
  let(:"Api-Key") { Fabricate(:api_key).key }
  let(:"Api-Username") { "system" }
  let(:admin) { Fabricate(:admin) }

  before do
    SiteSetting.tagging_enabled = true
    Jobs.run_immediately!
    sign_in(admin)
  end

  path "/users.json" do
    post "Creates a user" do
      tags "Users"
      operationId "createUser"
      consumes "application/json"
      # This endpoint requires an api key or the active param is ignored
      parameter name: "Api-Key", in: :header, type: :string, required: true
      parameter name: "Api-Username", in: :header, type: :string, required: true
      expected_request_schema = load_spec_schema("user_create_request")
      parameter name: :params, in: :body, schema: expected_request_schema

      produces "application/json"
      response "200", "user created" do
        expected_response_schema = load_spec_schema("user_create_response")
        schema expected_response_schema

        let(:params) do
          {
            "name" => "user",
            "username" => "user1",
            "email" => "user1@example.com",
            "password" => "13498428e9597cab689b468ebc0a5d33",
            "active" => true,
          }
        end

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/u/{username}.json" do
    get "Get a single user by username" do
      tags "Users"
      operationId "getUser"
      consumes "application/json"
      parameter name: "Api-Key", in: :header, type: :string, required: true
      parameter name: "Api-Username", in: :header, type: :string, required: true
      parameter name: :username, in: :path, type: :string, required: true
      expected_request_schema = nil

      produces "application/json"
      response "200", "user response" do
        expected_response_schema = load_spec_schema("user_get_response")
        schema expected_response_schema

        let(:username) { Fabricate(:user).username }

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end

      response "200", "user with primary group response" do
        expected_response_schema = load_spec_schema("user_get_response")
        schema expected_response_schema

        let(:username) { Fabricate(:user, primary_group_id: Fabricate(:group).id).username }

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end

    put "Update a user" do
      tags "Users"
      operationId "updateUser"
      consumes "application/json"

      parameter name: "Api-Key", in: :header, type: :string, required: true
      parameter name: "Api-Username", in: :header, type: :string, required: true
      expected_request_schema = load_spec_schema("user_update_request")
      parameter name: :username, in: :path, type: :string, required: true
      parameter name: :params, in: :body, schema: expected_request_schema

      produces "application/json"
      response "200", "user updated" do
        expected_response_schema = load_spec_schema("user_update_response")
        schema expected_response_schema

        let(:username) { Fabricate(:user).username }
        let(:params) { { "name" => "user" } }

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/u/by-external/{external_id}.json" do
    get "Get a user by external_id" do
      tags "Users"
      operationId "getUserExternalId"
      consumes "application/json"
      parameter name: "Api-Key", in: :header, type: :string, required: true
      parameter name: "Api-Username", in: :header, type: :string, required: true
      parameter name: :external_id, in: :path, type: :string, required: true
      expected_request_schema = nil

      produces "application/json"
      response "200", "user response" do
        expected_response_schema = load_spec_schema("user_get_response")
        schema expected_response_schema

        let(:user) { Fabricate(:user) }
        let(:external_id) { "1" }

        before do
          SiteSetting.discourse_connect_url = "http://someurl.com"
          SiteSetting.enable_discourse_connect = true
          user.create_single_sign_on_record(external_id: "1", last_payload: "")
        end

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/u/by-external/{provider}/{external_id}.json" do
    get "Get a user by identity provider external ID" do
      tags "Users"
      operationId "getUserIdentiyProviderExternalId"
      consumes "application/json"
      parameter name: "Api-Key", in: :header, type: :string, required: true
      parameter name: "Api-Username", in: :header, type: :string, required: true
      parameter name: :provider,
                in: :path,
                type: :string,
                required: true,
                description:
                  "Authentication provider name. Can be found in the provider callback URL: `/auth/{provider}/callback`"
      parameter name: :external_id, in: :path, type: :string, required: true
      expected_request_schema = nil

      produces "application/json"
      response "200", "user response" do
        expected_response_schema = load_spec_schema("user_get_response")
        schema expected_response_schema

        let(:user) { Fabricate(:user) }
        let(:provider) { "google_oauth2" }
        let(:external_id) { "myuid" }

        before do
          SiteSetting.enable_google_oauth2_logins = true
          UserAssociatedAccount.create!(
            user: user,
            provider_uid: "myuid",
            provider_name: "google_oauth2",
          )
        end

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/u/{username}/preferences/avatar/pick.json" do
    put "Update avatar" do
      tags "Users"
      operationId "updateAvatar"
      consumes "application/json"
      expected_request_schema = load_spec_schema("user_update_avatar_request")

      parameter name: :username, in: :path, type: :string, required: true
      parameter name: :params, in: :body, schema: expected_request_schema

      produces "application/json"
      response "200", "avatar updated" do
        expected_response_schema = load_spec_schema("success_ok_response")

        let(:user) { Fabricate(:user, refresh_auto_groups: true) }
        let(:username) { user.username }
        let(:upload) { Fabricate(:upload, user: user) }
        let(:params) { { "upload_id" => upload.id, "type" => "uploaded" } }

        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/u/{username}/preferences/email.json" do
    put "Update email" do
      tags "Users"
      operationId "updateEmail"
      consumes "application/json"
      expected_request_schema = load_spec_schema("user_update_email_request")

      parameter name: :username, in: :path, type: :string, required: true
      parameter name: :params, in: :body, schema: expected_request_schema

      produces "application/json"
      response "200", "email updated" do
        let(:user) { Fabricate(:user) }
        let(:username) { user.username }
        let(:params) { { "email" => "test@example.com" } }

        expected_response_schema = nil

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/u/{username}/preferences/username.json" do
    put "Update username" do
      tags "Users"
      operationId "updateUsername"
      consumes "application/json"
      expected_request_schema = load_spec_schema("user_update_username_request")

      parameter name: :username, in: :path, type: :string, required: true
      parameter name: :params, in: :body, schema: expected_request_schema

      produces "application/json"
      response "200", "username updated" do
        let(:user) { Fabricate(:user) }
        let(:username) { user.username }
        let(:params) { { "new_username" => "#{user.username}1" } }

        expected_response_schema = nil

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/directory_items.json" do
    get "Get a public list of users" do
      tags "Users"
      operationId "listUsersPublic"
      consumes "application/json"
      expected_request_schema = nil

      parameter name: :period,
                in: :query,
                schema: {
                  type: :string,
                  enum: %w[daily weekly monthly quarterly yearly all],
                },
                required: true
      parameter name: :order,
                in: :query,
                schema: {
                  type: :string,
                  enum: %w[
                    likes_received
                    likes_given
                    topic_count
                    post_count
                    topics_entered
                    posts_read
                    days_visited
                  ],
                },
                required: true
      parameter name: :asc, in: :query, schema: { type: :string, enum: ["true"] }
      parameter name: :page, in: :query, type: :integer

      produces "application/json"
      response "200", "directory items response" do
        let(:period) { "weekly" }
        let(:order) { "likes_received" }
        let(:asc) { "true" }
        let(:page) { 0 }

        expected_response_schema = load_spec_schema("users_public_list_response")
        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/admin/users/{id}.json" do
    # TODO @blake / @sam - this is not passing cause "silence_reason" is a conditional attribute
    # (also can_be_deleted is) - we need to figure out how to not include it in the schema - it is not included
    # in the admin response by design
    #
    #   get "Get a user by id" do
    #     tags "Users", "Admin"
    #     operationId "adminGetUser"
    #     consumes "application/json"
    #     expected_request_schema = nil

    #     parameter name: :id, in: :path, type: :integer, required: true

    #     produces "application/json"
    #     response "200", "response" do
    #       let(:id) { Fabricate(:user).id }

    #       expected_response_schema = load_spec_schema("admin_user_response")
    #       schema(expected_response_schema)

    #       it_behaves_like "a JSON endpoint", 200 do
    #         let(:expected_response_schema) { expected_response_schema }
    #         let(:expected_request_schema) { expected_request_schema }
    #       end
    #     end
    #   end

    delete "Delete a user" do
      tags "Users", "Admin"
      operationId "deleteUser"
      consumes "application/json"
      expected_request_schema = load_spec_schema("user_delete_request")

      parameter name: :id, in: :path, type: :integer, required: true
      parameter name: :params, in: :body, schema: expected_request_schema

      produces "application/json"
      response "200", "response" do
        let(:id) { Fabricate(:user).id }
        let(:params) do
          {
            "delete_posts" => true,
            "block_email" => false,
            "block_urls" => false,
            "block_ip" => false,
          }
        end

        expected_response_schema = load_spec_schema("user_delete_response")
        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/admin/users/{id}/activate.json" do
    put "Activate a user" do
      tags "Users", "Admin"
      operationId "activateUser"
      consumes "application/json"
      expected_request_schema = nil
      parameter name: :id, in: :path, type: :integer, required: true

      produces "application/json"
      response "200", "response" do
        let(:id) { Fabricate(:user, active: false).id }

        expected_response_schema = load_spec_schema("success_ok_response")
        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/admin/users/{id}/deactivate.json" do
    put "Deactivate a user" do
      tags "Users", "Admin"
      operationId "deactivateUser"
      consumes "application/json"
      expected_request_schema = nil
      parameter name: :id, in: :path, type: :integer, required: true

      produces "application/json"
      response "200", "response" do
        let(:id) { Fabricate(:user).id }

        expected_response_schema = load_spec_schema("success_ok_response")
        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/admin/users/{id}/suspend.json" do
    put "Suspend a user" do
      tags "Users", "Admin"
      operationId "suspendUser"
      consumes "application/json"
      expected_request_schema = load_spec_schema("user_suspend_request")

      parameter name: :id, in: :path, type: :integer, required: true
      parameter name: :params, in: :body, schema: expected_request_schema

      produces "application/json"
      response "200", "response" do
        let(:id) { Fabricate(:user).id }
        let(:params) { { "suspend_until" => "2121-02-22", "reason" => "inactivity" } }

        expected_response_schema = load_spec_schema("user_suspend_response")
        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/admin/users/{id}/silence.json" do
    put "Silence a user" do
      tags "Users", "Admin"
      operationId "silenceUser"
      consumes "application/json"
      expected_request_schema = load_spec_schema("user_silence_request")

      parameter name: :id, in: :path, type: :integer, required: true
      parameter name: :params, in: :body, schema: expected_request_schema

      produces "application/json"
      response "200", "response" do
        let(:id) { Fabricate(:user).id }
        let(:params) { { "reason" => "up to me", "silenced_till" => "2301-08-15" } }

        expected_response_schema = load_spec_schema("user_silence_response")
        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/admin/users/{id}/anonymize.json" do
    put "Anonymize a user" do
      tags "Users", "Admin"
      operationId "anonymizeUser"
      consumes "application/json"
      expected_request_schema = nil

      parameter name: :id, in: :path, type: :integer, required: true

      produces "application/json"
      response "200", "response" do
        let(:id) { Fabricate(:user).id }

        expected_response_schema = load_spec_schema("user_anonymize_response")
        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/admin/users/{id}/log_out.json" do
    post "Log a user out" do
      tags "Users", "Admin"
      operationId "logOutUser"
      consumes "application/json"
      expected_request_schema = nil

      parameter name: :id, in: :path, type: :integer, required: true

      produces "application/json"
      response "200", "response" do
        let(:id) { Fabricate(:user).id }

        expected_response_schema = load_spec_schema("success_ok_response")
        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/user_avatar/{username}/refresh_gravatar.json" do
    before do
      stub_request(
        :get,
        %r{https://www.gravatar.com/avatar/\w+.png\?d=404&reset_cache=\S+&s=#{Discourse.avatar_sizes.max}},
      ).with(
        headers: {
          "Accept" => "*/*",
          "Accept-Encoding" => "gzip",
          "Host" => "www.gravatar.com",
        },
      ).to_return(status: 200, body: "", headers: {})
    end

    post "Refresh gravatar" do
      tags "Users", "Admin"
      operationId "refreshGravatar"
      consumes "application/json"
      expected_request_schema = nil

      parameter name: :username, in: :path, type: :string, required: true

      produces "application/json"
      response "200", "response" do
        let(:user) { Fabricate(:user) }
        let(:username) { user.username }

        expected_response_schema = load_spec_schema("user_refresh_gravatar_response")
        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/admin/users/list/{flag}.json" do
    get "Get a list of users" do
      tags "Users", "Admin"
      operationId "adminListUsers"
      consumes "application/json"
      expected_request_schema = nil

      parameter name: :flag,
                in: :path,
                schema: {
                  type: :string,
                  enum: %w[active new staff suspended blocked suspect],
                },
                required: true
      parameter name: :order,
                in: :query,
                schema: {
                  type: :string,
                  enum: %w[
                    created
                    last_emailed
                    seen
                    username
                    email
                    trust_level
                    days_visited
                    posts_read
                    topics_viewed
                    posts
                    read_time
                  ],
                }
      parameter name: :asc, in: :query, schema: { type: :string, enum: ["true"] }
      parameter name: :page, in: :query, type: :integer
      parameter name: :show_emails,
                in: :query,
                type: :boolean,
                description:
                  "Include user email addresses in response. These requests will be logged in the staff action logs."
      parameter name: :stats,
                in: :query,
                type: :boolean,
                description: "Include user stats information"
      parameter name: :email,
                in: :query,
                type: :string,
                description: "Filter to the user with this email address"
      parameter name: :ip,
                in: :query,
                type: :string,
                description: "Filter to users with this IP address"

      produces "application/json"
      response "200", "response" do
        let(:flag) { "active" }
        let(:order) { "created" }
        let(:asc) { "true" }
        let(:page) { 0 }
        let(:show_emails) { false }
        let(:stats) { nil }
        let(:email) { nil }
        let(:ip) { nil }

        expected_response_schema = load_spec_schema("admin_user_list_response")
        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/user_actions.json" do
    get "Get a list of user actions" do
      tags "Users"
      operationId "listUserActions"
      consumes "application/json"
      expected_request_schema = nil

      parameter name: :offset, in: :query, type: :integer, required: true
      parameter name: :username, in: :query, type: :string, required: true
      parameter name: :filter, in: :query, type: :string, required: true

      produces "application/json"
      response "200", "response" do
        let(:offset) { 0 }
        let(:username) { Fabricate(:user).username }
        let(:filter) { "4,5" }

        expected_response_schema = load_spec_schema("user_actions_response")
        schema(expected_response_schema)

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/session/forgot_password.json" do
    SiteSetting.hide_email_address_taken = false

    post "Send password reset email" do
      tags "Users"
      operationId "sendPasswordResetEmail"
      consumes "application/json"
      expected_request_schema = load_spec_schema("user_password_reset_request")
      parameter name: :params, in: :body, schema: expected_request_schema

      produces "application/json"
      response "200", "success response" do
        expected_response_schema = load_spec_schema("user_password_reset_response")
        schema expected_response_schema

        let(:user) { Fabricate(:user) }
        let(:params) { { "login" => user.username } }

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/users/password-reset/{token}.json" do
    put "Change password" do
      tags "Users"
      operationId "changePassword"
      consumes "application/json"
      expected_request_schema = load_spec_schema("user_password_change_request")
      parameter name: :token, in: :path, type: :string, required: true
      parameter name: :params, in: :body, schema: expected_request_schema

      produces "application/json"
      response "200", "success response" do
        expected_response_schema = nil

        let(:user) { Fabricate(:user) }
        let(:token) do
          Fabricate(:email_token, user: user, scope: EmailToken.scopes[:password_reset]).token
        end
        let(:params) { { "username" => user.username, "password" => "NH8QYbxYS5Zv5qEFzA4jULvM" } }

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end

  path "/u/{username}/emails.json" do
    get "Get email addresses belonging to a user" do
      tags "Users"
      operationId "getUserEmails"
      consumes "application/json"
      expected_request_schema = nil
      parameter name: :username, in: :path, type: :string, required: true

      produces "application/json"
      response "200", "success response" do
        expected_response_schema = load_spec_schema("user_emails_response")
        schema expected_response_schema

        let(:username) { Fabricate(:user).username }

        it_behaves_like "a JSON endpoint", 200 do
          let(:expected_response_schema) { expected_response_schema }
          let(:expected_request_schema) { expected_request_schema }
        end
      end
    end
  end
end