# frozen_string_literal: true

RSpec.describe SidebarSectionsController do
  fab!(:user) { Fabricate(:user) }
  fab!(:admin) { Fabricate(:admin) }
  fab!(:moderator) { Fabricate(:moderator) }

  describe "#index" do
    fab!(:sidebar_section) { Fabricate(:sidebar_section, title: "private section", user: user) }
    fab!(:sidebar_url_1) { Fabricate(:sidebar_url, name: "tags", value: "/tags") }
    fab!(:sidebar_url_2) { Fabricate(:sidebar_url, name: "categories", value: "/categories") }
    fab!(:section_link_1) do
      Fabricate(:sidebar_section_link, sidebar_section: sidebar_section, linkable: sidebar_url_1)
    end
    fab!(:sidebar_section_2) { Fabricate(:sidebar_section, title: "public section", public: true) }
    fab!(:section_link_2) do
      Fabricate(:sidebar_section_link, sidebar_section: sidebar_section, linkable: sidebar_url_2)
    end

    it "returns public and private sections" do
      sign_in(user)
      get "/sidebar_sections.json"
      expect(response.status).to eq(200)
      expect(response.parsed_body["sidebar_sections"].map { |section| section["title"] }).to eq(
        ["Community", "public section", "private section"],
      )
    end
  end

  describe "#create" do
    it "is not available for anonymous" do
      post "/sidebar_sections.json",
           params: {
             title: "custom section",
             links: [
               { icon: "link", name: "categories", value: "/categories" },
               { icon: "link", name: "tags", value: "/tags" },
             ],
           }
      expect(response.status).to eq(403)
    end

    it "creates custom section for user" do
      sign_in(user)
      expect(SidebarSection.count).to eq(1)

      post "/sidebar_sections.json",
           params: {
             title: "custom section",
             links: [
               {
                 icon: "link",
                 name: "categories",
                 value: "http://#{Discourse.current_hostname}/categories",
               },
               { icon: "address-book", name: "tags", value: "/tags" },
               { icon: "external-link-alt", name: "Discourse", value: "https://discourse.org" },
               { icon: "external-link-alt", name: "My preferences", value: "/my/preferences" },
             ],
           }

      expect(response.status).to eq(200)

      expect(SidebarSection.count).to eq(2)
      sidebar_section = SidebarSection.last

      expect(sidebar_section.title).to eq("custom section")
      expect(sidebar_section.user).to eq(user)
      expect(sidebar_section.public).to be false
      expect(UserHistory.count).to eq(0)
      expect(sidebar_section.sidebar_urls.count).to eq(4)
      expect(sidebar_section.sidebar_urls.first.icon).to eq("link")
      expect(sidebar_section.sidebar_urls.first.name).to eq("categories")
      expect(sidebar_section.sidebar_urls.first.value).to eq("/categories")
      expect(sidebar_section.sidebar_urls.first.external).to be false
      expect(sidebar_section.sidebar_urls.second.icon).to eq("address-book")
      expect(sidebar_section.sidebar_urls.second.name).to eq("tags")
      expect(sidebar_section.sidebar_urls.second.value).to eq("/tags")
      expect(sidebar_section.sidebar_urls.second.external).to be false
      expect(sidebar_section.sidebar_urls.third.icon).to eq("external-link-alt")
      expect(sidebar_section.sidebar_urls.third.name).to eq("Discourse")
      expect(sidebar_section.sidebar_urls.third.value).to eq("https://discourse.org")
      expect(sidebar_section.sidebar_urls.third.external).to be true
      expect(sidebar_section.sidebar_urls.fourth.icon).to eq("external-link-alt")
      expect(sidebar_section.sidebar_urls.fourth.name).to eq("My preferences")
      expect(sidebar_section.sidebar_urls.fourth.value).to eq("/my/preferences")
      expect(sidebar_section.sidebar_urls.fourth.external).to be false
    end

    it "validates max number of links" do
      SiteSetting.max_sidebar_section_links = 5

      sign_in(user)

      links =
        6.times.map do
          { icon: "external-link-alt", name: "My preferences", value: "/my/preferences" }
        end

      post "/sidebar_sections.json", params: { title: "custom section", links: links }

      expect(response.status).to eq(422)
      expect(response.parsed_body["errors"]).to eq(
        ["Maximum 5 records are allowed. Got 6 records instead."],
      )
    end

    it "does not allow regular user to create public section" do
      sign_in(user)

      post "/sidebar_sections.json",
           params: {
             title: "custom section",
             public: true,
             links: [
               { icon: "link", name: "categories", value: "/categories" },
               { icon: "address-book", name: "tags", value: "/tags" },
             ],
           }

      expect(response.status).to eq(403)
    end

    it "does not allow moderator to create public section" do
      sign_in(moderator)

      post "/sidebar_sections.json",
           params: {
             title: "custom section",
             public: true,
             links: [
               { icon: "link", name: "categories", value: "/categories" },
               { icon: "address-book", name: "tags", value: "/tags" },
             ],
           }

      expect(response.status).to eq(403)
    end

    it "allows admin to create public section" do
      sign_in(admin)

      post "/sidebar_sections.json",
           params: {
             title: "custom section",
             public: true,
             links: [
               { icon: "link", name: "categories", value: "/categories" },
               { icon: "address-book", name: "tags", value: "/tags" },
             ],
           }

      expect(response.status).to eq(200)

      sidebar_section = SidebarSection.last
      expect(sidebar_section.title).to eq("custom section")
      expect(sidebar_section.public).to be true
      expect(sidebar_section.user_id).to be Discourse.system_user.id

      user_history = UserHistory.last
      expect(user_history.action).to eq(UserHistory.actions[:create_public_sidebar_section])
      expect(user_history.subject).to eq("custom section")
      expect(user_history.details).to eq("links: categories - /categories, tags - /tags")
    end
  end

  describe "#update" do
    fab!(:sidebar_section) { Fabricate(:sidebar_section, user: user) }
    fab!(:sidebar_url_1) { Fabricate(:sidebar_url, name: "tags", value: "/tags") }
    fab!(:sidebar_url_2) { Fabricate(:sidebar_url, name: "categories", value: "/categories") }

    fab!(:section_link_1) do
      Fabricate(:sidebar_section_link, sidebar_section: sidebar_section, linkable: sidebar_url_1)
    end

    fab!(:section_link_2) do
      Fabricate(:sidebar_section_link, sidebar_section: sidebar_section, linkable: sidebar_url_2)
    end

    let(:community_section) do
      SidebarSection.find_by(section_type: SidebarSection.section_types[:community])
    end

    it "allows user to update their own section and links" do
      sign_in(user)

      put "/sidebar_sections/#{sidebar_section.id}.json",
          params: {
            title: "custom section edited",
            links: [
              { icon: "link", id: sidebar_url_1.id, name: "latest", value: "/latest" },
              { icon: "link", id: sidebar_url_2.id, name: "tags", value: "/tags", _destroy: "1" },
            ],
          }

      expect(response.status).to eq(200)

      expect(sidebar_section.reload.title).to eq("custom section edited")
      expect(UserHistory.count).to eq(0)
      expect(sidebar_url_1.reload.name).to eq("latest")
      expect(sidebar_url_1.value).to eq("/latest")
      expect { section_link_2.reload }.to raise_error(ActiveRecord::RecordNotFound)
      expect { sidebar_url_2.reload }.to raise_error(ActiveRecord::RecordNotFound)
    end

    it "allows admin to update public section and links" do
      sign_in(admin)
      sidebar_section.update!(public: true)

      put "/sidebar_sections/#{sidebar_section.id}.json",
          params: {
            title: "custom section edited",
            links: [
              {
                icon: "link",
                name: "meta",
                value: "https://meta.discourse.org",
                segment: "primary",
              },
              { icon: "link", id: sidebar_url_1.id, name: "latest", value: "/latest" },
              { icon: "link", id: sidebar_url_2.id, name: "tags", value: "/tags", _destroy: "1" },
              {
                icon: "link",
                name: "homepage",
                value: "https://discourse.org",
                segment: "secondary",
              },
            ],
          }

      expect(response.status).to eq(200)

      expect(sidebar_section.reload.title).to eq("custom section edited")
      expect(sidebar_url_1.reload.name).to eq("latest")
      expect(sidebar_url_1.value).to eq("/latest")
      expect { section_link_2.reload }.to raise_error(ActiveRecord::RecordNotFound)
      expect { sidebar_url_2.reload }.to raise_error(ActiveRecord::RecordNotFound)

      urls = sidebar_section.sidebar_urls
      expect(urls[0].name).to eq("meta")
      expect(urls[0].value).to eq("https://meta.discourse.org")
      expect(urls[0].segment).to eq("primary")
      expect(urls[1].name).to eq("latest")
      expect(urls[1].value).to eq("/latest")
      expect(urls[2].name).to eq("homepage")
      expect(urls[2].value).to eq("https://discourse.org")
      expect(urls[2].segment).to eq("secondary")

      user_history = UserHistory.last
      expect(user_history.action).to eq(UserHistory.actions[:update_public_sidebar_section])
      expect(user_history.subject).to eq("custom section edited")
      expect(user_history.details).to eq(
        "links: latest - /latest, meta - https://meta.discourse.org, homepage - https://discourse.org",
      )
    end

    it "validates limit of links" do
      SiteSetting.max_sidebar_section_links = 5

      sign_in(user)

      links =
        6.times.map do
          { icon: "external-link-alt", name: "My preferences", value: "/my/preferences" }
        end

      put "/sidebar_sections/#{sidebar_section.id}.json",
          params: {
            title: "custom section",
            links: links,
          }

      expect(response.status).to eq(422)
      expect(response.parsed_body["errors"]).to eq(
        ["Maximum 5 records are allowed. Got 6 records instead."],
      )
    end

    it "doesn't allow to edit other's sections" do
      sidebar_section_2 = Fabricate(:sidebar_section)
      sidebar_url_3 = Fabricate(:sidebar_url, name: "other_tags", value: "/tags")
      Fabricate(:sidebar_section_link, sidebar_section: sidebar_section_2, linkable: sidebar_url_3)
      sign_in(user)

      put "/sidebar_sections/#{sidebar_section_2.id}.json",
          params: {
            title: "custom section edited",
            links: [{ icon: "link", id: sidebar_url_3.id, name: "takeover", value: "/categories" }],
          }

      expect(response.status).to eq(403)
    end

    it "doesn't allow to edit public sections" do
      sign_in(user)
      sidebar_section.update!(public: true)

      put "/sidebar_sections/#{sidebar_section.id}.json",
          params: {
            title: "custom section edited",
            links: [
              { icon: "link", id: sidebar_url_1.id, name: "latest", value: "/latest" },
              { icon: "link", id: sidebar_url_2.id, name: "tags", value: "/tags", _destroy: "1" },
            ],
          }

      expect(response.status).to eq(403)
    end

    it "doesn't allow to edit other's links" do
      sidebar_url_3 = Fabricate(:sidebar_url, name: "other_tags", value: "/tags")

      Fabricate(
        :sidebar_section_link,
        sidebar_section: Fabricate(:sidebar_section),
        linkable: sidebar_url_3,
      )

      sign_in(user)

      put "/sidebar_sections/#{sidebar_section.id}.json",
          params: {
            title: "custom section edited",
            links: [{ icon: "link", id: sidebar_url_3.id, name: "takeover", value: "/categories" }],
          }

      expect(response.status).to eq(404)
      expect(sidebar_url_3.reload.name).to eq("other_tags")
    end

    it "doesn't allow users to edit community section" do
      sign_in(user)

      put "/sidebar_sections/#{community_section.id}.json",
          params: {
            title: "custom section edited",
            links: [],
          }

      expect(response.status).to eq(403)
    end

    it "allows admin to edit community section" do
      sign_in(admin)

      topics_link = community_section.sidebar_urls.find_by(name: "Topics")
      my_posts_link = community_section.sidebar_urls.find_by(name: "My Posts")

      community_section
        .sidebar_section_links
        .where.not(linkable_id: [topics_link.id, my_posts_link.id])
        .destroy_all

      put "/sidebar_sections/#{community_section.id}.json",
          params: {
            title: "community section edited",
            links: [
              { icon: "link", id: my_posts_link.id, name: "my posts edited", value: "/my_posts" },
              { icon: "link", id: topics_link.id, name: "topics edited", value: "/new" },
            ],
          }

      expect(response.status).to eq(200)

      expect(community_section.reload.title).to eq("community section edited")
      expect(community_section.sidebar_urls[0].name).to eq("my posts edited")
      expect(community_section.sidebar_urls[0].value).to eq("/my_posts")
      expect(community_section.sidebar_urls[1].name).to eq("topics edited")
      expect(community_section.sidebar_urls[1].value).to eq("/new")
    end
  end

  describe "#reorder" do
    fab!(:user2) { Fabricate(:user) }
    fab!(:sidebar_section) { Fabricate(:sidebar_section, user: user) }
    fab!(:sidebar_url_1) { Fabricate(:sidebar_url, name: "tags", value: "/tags") }
    fab!(:sidebar_url_2) { Fabricate(:sidebar_url, name: "categories", value: "/categories") }
    fab!(:sidebar_url_3) { Fabricate(:sidebar_url, name: "topic", value: "/t/1") }

    fab!(:section_link_1) do
      Fabricate(:sidebar_section_link, sidebar_section: sidebar_section, linkable: sidebar_url_1)
    end

    fab!(:section_link_2) do
      Fabricate(:sidebar_section_link, sidebar_section: sidebar_section, linkable: sidebar_url_2)
    end

    fab!(:section_link_3) do
      Fabricate(:sidebar_section_link, sidebar_section: sidebar_section, linkable: sidebar_url_3)
    end

    it "sorts links" do
      expect(sidebar_section.sidebar_urls.pluck(:id)).to eq(
        [sidebar_url_1.id, sidebar_url_2.id, sidebar_url_3.id],
      )

      sign_in(user)

      post "/sidebar_sections/reorder.json",
           params: {
             sidebar_section_id: sidebar_section.id,
             links_order: [sidebar_url_2.id, sidebar_url_3.id, sidebar_url_1.id],
           }

      expect(response.status).to eq(200)

      expect(sidebar_section.reload.sidebar_urls.pluck(:id)).to eq(
        [sidebar_url_2.id, sidebar_url_3.id, sidebar_url_1.id],
      )
    end

    it "returns 403 when a user tries to reorder a section that doesn't belong to them" do
      sign_in(user2)

      post "/sidebar_sections/reorder.json",
           params: {
             sidebar_section_id: sidebar_section.id,
             links_order: [sidebar_url_2.id, sidebar_url_3.id, sidebar_url_1.id],
           }

      expect(response.status).to eq(403)

      expect(sidebar_section.reload.sidebar_urls.pluck(:id)).to eq(
        [sidebar_url_1.id, sidebar_url_2.id, sidebar_url_3.id],
      )
    end

    it "returns 403 for an non user" do
      post "/sidebar_sections/reorder.json",
           params: {
             sidebar_section_id: sidebar_section.id,
             links_order: [sidebar_url_2.id, sidebar_url_3.id, sidebar_url_1.id],
           }

      expect(response.status).to eql(403)
    end
  end

  describe "#destroy" do
    fab!(:sidebar_section) { Fabricate(:sidebar_section, user: user) }

    let(:community_section) do
      SidebarSection.find_by(section_type: SidebarSection.section_types[:community])
    end

    it "allows user to delete their own section" do
      sign_in(user)
      delete "/sidebar_sections/#{sidebar_section.id}.json"

      expect(response.status).to eq(200)

      expect { sidebar_section.reload }.to raise_error(ActiveRecord::RecordNotFound)
      expect(UserHistory.count).to eq(0)
    end

    it "allows admin to delete public section" do
      sign_in(admin)
      sidebar_section.update!(public: true)
      delete "/sidebar_sections/#{sidebar_section.id}.json"

      expect(response.status).to eq(200)

      expect { sidebar_section.reload }.to raise_error(ActiveRecord::RecordNotFound)

      user_history = UserHistory.last
      expect(user_history.action).to eq(UserHistory.actions[:destroy_public_sidebar_section])
      expect(user_history.subject).to eq("Sidebar section")
    end

    it "doesn't allow to delete other's sidebar section" do
      sidebar_section_2 = Fabricate(:sidebar_section)
      sign_in(user)
      delete "/sidebar_sections/#{sidebar_section_2.id}.json"

      expect(response.status).to eq(403)
    end

    it "doesn't allow to delete public sidebar section" do
      sign_in(user)
      sidebar_section.update!(public: true)
      delete "/sidebar_sections/#{sidebar_section.id}.json"

      expect(response.status).to eq(403)
    end

    it "doesn't allow moderator to delete public sidebar section" do
      sign_in(moderator)
      sidebar_section.update!(public: true)
      delete "/sidebar_sections/#{sidebar_section.id}.json"

      expect(response.status).to eq(403)
    end
  end

  describe "#reset" do
    let(:community_section) do
      SidebarSection.find_by(section_type: SidebarSection.section_types[:community])
    end

    it "doesn't allow user to reset community section" do
      sign_in(user)
      SidebarSection.any_instance.expects(:reset_community!).never
      put "/sidebar_sections/reset/#{community_section.id}.json"
      expect(response.status).to eq(403)
    end

    it "doesn't allow staff to reset community section" do
      sign_in(moderator)
      SidebarSection.any_instance.expects(:reset_community!).never
      put "/sidebar_sections/reset/#{community_section.id}.json"
      expect(response.status).to eq(403)
    end

    it "allows admins to reset community section to default" do
      sign_in(admin)
      SidebarSection.any_instance.expects(:reset_community!).once
      put "/sidebar_sections/reset/#{community_section.id}.json"
      expect(response.status).to eq(200)
      expect(response.parsed_body["sidebar_section"]["id"]).to eq(community_section.id)
      expect(response.parsed_body["sidebar_section"]["title"]).to eq(community_section.title)
    end

    it "doesn't allow admin to delete community sidebar section" do
      sign_in(admin)
      delete "/sidebar_sections/#{community_section.id}.json"

      expect(response.status).to eq(403)
    end
  end
end