# typed: false
# frozen_string_literal: true

require "rails_helper"

class ModelWithDescriptionHtml
  def initialize(base_domain, html)
    @base_domain = base_domain
    @html = html
  end
  include HasDescriptionHtmlForView

  def description_html_base_domain
    @base_domain
  end

  def description_html_for_view
    @html
  end
end

RSpec.describe Domain::DescriptionsHelper, type: :helper do
  def sanitize_description_html(html, base_domain = "www.furaffinity.net")
    helper.sanitize_description_html(
      ModelWithDescriptionHtml.new(base_domain, html),
    )
  end

  # Mock the policy for posts to avoid Devise authentication errors
  before do
    # Create a fake policy result that allows view_file?
    policy_double = double("PostPolicy", view_file?: true)
    # Allow any call to policy() with any Post class, including subclasses
    allow(helper).to receive(:policy).and_return(policy_double)
  end

  describe "#sanitize_description_html" do
    describe "basic HTML sanitization" do
      it "works" do
        sanitized =
          sanitize_description_html(
            '<b>Bold</b> <i>Italic</i> <span style="color: red; font-size: 12px;">Styled</span> <script>alert("bad")</script>',
          )
        [
          "<b>Bold</b>",
          "<i>Italic</i>",
          '<span style="color: red; font-size: 12px;">Styled</span>',
        ].each { |text| expect(sanitized).to include(text) }
        ["<script>"].each { |text| expect(sanitized).not_to include(text) }
      end

      it "handles manually embedded links" do
        user =
          create(
            :domain_user_fa_user,
            url_name: "starringer",
            name: "Starringer",
          )

        sanitized =
          sanitize_description_html(
            "my mate &lt;<a href=\"http://www.furaffinity.net/user/starringer/&gt;.\" title=\"http://www.furaffinity.net/user/starringer/&gt;.\" class=\"auto_link\">http://www.furaffinity.net/user/starringer/&gt;.</a> It was fun.",
          )
        expect(sanitized).to include(
          /UserHoverPreviewWrapper.+#{user.to_param}/,
        )
      end
    end

    describe "handling links that are bare text" do
      it "does not replace links that are already wrapped in an anchor tag" do
        html =
          '<a href="https://e621.net/posts/5406941" title="https://e621.net/posts/5406941" class="auto_link">https://e621.net/posts/5406941</a><br />'
        sanitized = sanitize_description_html(html)
        # should render the external link partial because the e621 post model is not found
        expect(sanitized).to include("_blank noopener noreferrer nofollow")
        expect(sanitized).to include("https://e621.net/posts/5406941")
        expect(sanitized).to include("<a")
      end

      it "does not make a link if the domain is not in the allowlist" do
        html =
          'before <a href="https://www.example.com/foo/" title="https://www.example.com/foo/" class="auto_link">https://www.example.com/foo/</a> after'
        sanitized = sanitize_description_html(html)
        expect(sanitized).to include("before ")
        expect(sanitized).to include(" after")
        expect(sanitized).to include("https://www.example.com/foo/")
        expect(sanitized).not_to include("<a")
      end

      it "does not make a link if anchor does not have href" do
        html = "<a>No href</a>"
        sanitized = sanitize_description_html(html)
        expect(sanitized).to eq("No href")
      end

      it "does not allow nested links" do
        html =
          '<a href="https://www.example.com/foo">Link <a href="https://www.example.com/bar/">Nested Link</a></a>'
        sanitized = sanitize_description_html(html)
        expect(sanitized).to eq("Link Nested Link")
      end

      it "handles models that are not recorded in the database as external links" do
        html = "before http://www.furaffinity.net/view/123456/ after"
        sanitized = sanitize_description_html(html)
        expect(sanitized).to include("before ")
        expect(sanitized).to include(" after")
        expect(sanitized).to include("http://www.furaffinity.net/view/123456/")
        # renders the inline_link_external partial because the FA post model is not found
        expect(sanitized).to include("_blank noopener noreferrer nofollow")
      end

      it "handles models that are recorded in the database as inline hoverable links" do
        post = create(:domain_post_e621_post, e621_id: 5_406_941)
        html = "before https://e621.net/posts/5406941 after"
        sanitized = sanitize_description_html(html)
        expect(sanitized).to include("before ")
        expect(sanitized).to include(" after")
        expect(sanitized).to include(
          /PostHoverPreviewWrapper.+#{post.to_param}/,
        )
      end

      it "handles text links that do not have a scheme" do
        html = "before e621.net/posts/5406941 after"
        sanitized = sanitize_description_html(html)
        expect(sanitized).to include("before ")
        expect(sanitized).to include(" after")
        expect(sanitized).to include("e621.net/posts/5406941")
        expect(sanitized).to include("_blank noopener noreferrer nofollow")
      end

      it "deep links text links that do not have a scheme" do
        post = create(:domain_post_e621_post, e621_id: 5_406_941)
        html = "before e621.net/posts/5406941 after"
        sanitized = sanitize_description_html(html)
        expect(sanitized).to include("before ")
        expect(sanitized).to include(" after")
        expect(sanitized).to include(
          /PostHoverPreviewWrapper.+#{post.to_param}/,
        )
      end
    end

    describe "FA post link handling" do
      %w[
        https://www.furaffinity.net/view/123456/
        https://furaffinity.net/view/123456/
        https://www.furaffinity.net/view/123456
        https://furaffinity.net/view/123456
        furaffinity.net/view/123456
        furaffinity.net/view/123456/
        www.furaffinity.net/view/123456/
        www.furaffinity.net/view/123456
        www.furaffinity.net/view/123456/
        www.furaffinity.net/view/123456
        Furaffinity.net/view/123456
      ].each do |url|
        it "processes #{url}" do
          post =
            create(:domain_post_fa_post, fa_id: "123456", title: "Post Title")
          html = %(<a href="#{url}">FA Link</a>)
          sanitized = sanitize_description_html(html)
          expect(sanitized).to include(
            /PostHoverPreviewWrapper.+#{post.to_param}/,
          )
        end
      end

      it "sanitizes nested content within valid FA links" do
        html =
          '<a href="https://www.furaffinity.net/view/123456/"><b>Bold</b> <script>bad</script> <span style="color: red; background: blue;">Text</span></a>'
        sanitized = sanitize_description_html(html)
        expect(sanitized).to include("<a")
        expect(sanitized).to include("Bold bad Text")
        expect(sanitized).to include("https://www.furaffinity.net/view/123456/")
        expect(sanitized).to include("_blank")
      end

      describe "post title lookup" do
        let!(:post1) do
          create(:domain_post_fa_post, fa_id: "123", title: "First Post")
        end
        let!(:post2) do
          create(:domain_post_fa_post, fa_id: "456", title: "Second Post")
        end

        it "replaces link text with post titles when posts exist" do
          html =
            '
            <a href="https://www.furaffinity.net/view/123/">Original Text 1</a>
            <a href="https://www.furaffinity.net/view/456/">Original Text 2</a>
          '
          sanitized = sanitize_description_html(html)

          expect(sanitized).to include(
            /PostHoverPreviewWrapper.+#{post1.to_param}/,
          )
          expect(sanitized).to include(
            /PostHoverPreviewWrapper.+#{post2.to_param}/,
          )
        end

        it "links to the external post when the post doesn't exist" do
          html =
            '<a href="https://www.furaffinity.net/view/789/">Original Text</a>'
          sanitized = sanitize_description_html(html)

          expect(sanitized).to include("Original Text")
          expect(sanitized).to include("<a")
          expect(sanitized).to include("https://www.furaffinity.net/view/789/")
          expect(sanitized).to include("_blank")
        end

        it "replaces nested elements when replacing titles" do
          html =
            '<a href="https://www.furaffinity.net/view/123/"><b>Bold</b> <i>Text</i></a>'
          sanitized = sanitize_description_html(html)

          expect(sanitized).to include(
            /PostHoverPreviewWrapper.+#{post1.to_param}/,
          )
        end
      end
    end

    describe "relative links" do
      it "works for posts" do
        post1 =
          create(:domain_post_fa_post, fa_id: "123456", title: "Post Title")
        html = '<a href="/view/123456/">Relative Link</a>'
        sanitized = sanitize_description_html(html)

        expect(sanitized).to include(
          /PostHoverPreviewWrapper.+#{post1.to_param}/,
        )
      end

      it "works for users" do
        user1 =
          create(
            :domain_user_fa_user,
            url_name: "artistone",
            name: "Artist One",
          )
        html = '<a href="/user/artistone/">Relative Link</a>'
        sanitized = sanitize_description_html(html)

        expect(sanitized).to include(
          /UserHoverPreviewWrapper.+#{user1.to_param}/,
        )
      end
    end

    describe "non-FA link handling" do
      {
        '<a href="https://www.google.com/">Google</a>' => "Google",
        '<a href="https://www.furaffinity.net/user/">Invalid User Link</a>' =>
          "Invalid User Link",
        '<a href="https://www.furaffinity.net/view/">Invalid Post Link</a>' =>
          "Invalid Post Link",
        '<a href="bfuraffinity.net/view/">Not actually an FA Link</a>' =>
          "Not actually an FA Link",
        "<a>No Href Link</a>" => "No Href Link",
        '<a href="https://www.google.com/"><b>Bold</b> and <i>italic</i> text</a>' =>
          "<b>Bold</b> and <i>italic</i> text",
      }.each do |input, expected|
        it "processes '#{input}' correctly" do
          sanitized = sanitize_description_html(input)
          expect(sanitized).to include(expected)
        end
      end
    end

    describe "CSS property handling" do
      it "only allows specified CSS properties" do
        input =
          '<span style="font-size: 12px; color: red; background: blue; position: absolute;">Test</span>'
        sanitized = sanitize_description_html(input)

        expect(sanitized).to include("font-size")
        expect(sanitized).to include("color")
        expect(sanitized).not_to include("background")
        expect(sanitized).not_to include("position")
      end
    end

    describe "nested element handling" do
      {
        "valid nested elements" => {
          input: '<b><i><span style="color: red;">Nested</span></i></b>',
          expected: '<b><i><span style="color: red;">Nested</span></i></b>',
        },
        "invalid nested elements" => {
          input: "<b><script>bad</script><i>good</i></b>",
          expected: "<b><i>good</i></b>",
        },
      }.each do |description, test_case|
        it "handles #{description}" do
          sanitized = sanitize_description_html(test_case[:input])
          expect(sanitized).to eq(test_case[:expected])
        end
      end
    end

    describe "multiple link handling" do
      it "correctly processes multiple links of different types" do
        post1 = create(:domain_post_fa_post, fa_id: "123", title: "Post Title")
        user1 =
          create(
            :domain_user_fa_user,
            url_name: "username1",
            name: "User Name 1",
          )

        html =
          '
          <a href="https://www.furaffinity.net/view/123/">FA Post</a>
          <a href="https://www.furaffinity.net/user/username1/">FA User</a>
          <a href="https://google.com">Google</a>
          <a>No href</a>
        '
        sanitized = sanitize_description_html(html)

        expect(sanitized).to include(
          /PostHoverPreviewWrapper.+#{post1.to_param}/,
        )
        expect(sanitized).to include(
          /UserHoverPreviewWrapper.+#{user1.to_param}/,
        )
        expect(sanitized).to include("FA Post")
        expect(sanitized).not_to include("FA User")
        expect(sanitized).to include("Google")
        expect(sanitized).to include("No href")
        expect(sanitized).not_to include("<a")
      end
    end

    describe "FA user link handling" do
      let!(:user1) do
        create(:domain_user_fa_user, url_name: "artistone", name: "Artist One")
      end
      let!(:user2) do
        create(:domain_user_fa_user, url_name: "artisttwo", name: "Artist Two")
      end
      let!(:user3) do
        create(
          :domain_user_fa_user,
          url_name: "artistthree",
          name: "Artist Three",
        )
      end

      it "replaces link text with user names when users exist" do
        html =
          '
          <a href="https://www.furaffinity.net/user/artistone/">Original Text 1</a>
          <a href="https://www.furaffinity.net/user/artisttwo/">Original Text 2</a>
        '
        sanitized = sanitize_description_html(html)

        expect(sanitized).to include(
          /UserHoverPreviewWrapper.+#{user1.to_param}/,
        )
        expect(sanitized).to include(
          /UserHoverPreviewWrapper.+#{user2.to_param}/,
        )
      end

      it "links to the external user when the user doesn't exist" do
        html =
          '<a href="https://www.furaffinity.net/user/nonexistent/">Original Text</a>'
        sanitized = sanitize_description_html(html)

        expect(sanitized).to include("Original Text")
        expect(sanitized).to include("<a")
        expect(sanitized).to include(
          "https://www.furaffinity.net/user/nonexistent/",
        )
        expect(sanitized).to include("_blank")
      end

      it "replaces nested elements when replacing names" do
        html =
          '<a href="https://www.furaffinity.net/user/artistone/"><b>Bold</b> <i>Text</i></a>'
        sanitized = sanitize_description_html(html)

        expect(sanitized).to include(
          /UserHoverPreviewWrapper.+#{user1.to_param}/,
        )
      end

      %w[
        https://www.furaffinity.net/user/artistone/
        https://furaffinity.net/user/artistone/
        https://www.furaffinity.net/user/artistone
        https://furaffinity.net/user/artistone
        furaffinity.net/user/artistone
        furaffinity.net/user/artistone/
        www.furaffinity.net/user/artistone/
        www.furaffinity.net/user/artistone
        Furaffinity.net/user/artistone
      ].each do |url|
        it "processes #{url}" do
          html = %(<a href="#{url}">FA User Link</a>)
          sanitized = sanitize_description_html(html)
          expect(sanitized).to include(
            /UserHoverPreviewWrapper.+#{user1.to_param}/,
          )
        end
      end
    end
  end
end
