# frozen_string_literal: true
require 'rails_helper'

RSpec.describe InlineUploads do
  before do
    set_cdn_url "https://awesome.com"
  end

  describe '.process' do
    describe 'local uploads' do
      fab!(:upload) { Fabricate(:upload) }
      fab!(:upload2) { Fabricate(:upload) }
      fab!(:upload3) { Fabricate(:upload) }

      it "should not correct existing inline uploads" do
        md = <<~MD
        ![test](#{upload.short_url})haha
        [test]#{upload.short_url}
        MD

        expect(InlineUploads.process(md)).to eq(md)

        md = <<~MD
        ![test](#{upload.short_url})
        [test|attachment](#{upload.short_url})
        MD

        expect(InlineUploads.process(md)).to eq(md)
      end

      it "should not escape existing content" do
        md = "1 > 2"

        expect(InlineUploads.process(md)).to eq(md)
      end

      it "should not escape invalid HTML tags" do
        md = "<x>.<y>"

        expect(InlineUploads.process(md)).to eq(md)
      end

      it "should work with invalid img tags" do
        md = <<~MD
        <img src="#{upload.url}">

        This is an invalid `<img ...>` tag
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        ![](#{upload.short_url})

        This is an invalid `<img ...>` tag
        MD
      end

      it "should not correct code blocks" do
        md = "`<a class=\"attachment\" href=\"#{upload2.url}\">In Code Block</a>`"

        expect(InlineUploads.process(md)).to eq(md)

        md = "    <a class=\"attachment\" href=\"#{upload2.url}\">In Code Block</a>"

        expect(InlineUploads.process(md)).to eq(md)
      end

      it "should not correct invalid links in quotes" do
        post = Fabricate(:post)
        user = Fabricate(:user)

        md = <<~MD
        [quote="#{user.username}, post:#{post.post_number}, topic:#{post.topic.id}"]
        <img src="#{upload.url}"
        someothertext#{upload2.url}someothertext

        <img src="#{upload.url}"

        sometext#{upload2.url}sometext

        #{upload3.url}

        #{Discourse.base_url}#{upload3.url}
        [/quote]

        <img src="#{upload2.url}">
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        [quote="#{user.username}, post:#{post.post_number}, topic:#{post.topic.id}"]
        <img src="#{upload.url}"
        someothertext#{upload2.url}someothertext

        <img src="#{upload.url}"

        sometext#{upload2.url}sometext

        #{upload3.url}

        ![](#{upload3.short_url})
        [/quote]

        ![](#{upload2.short_url})
        MD
      end

      it "should correct links in quotes" do
        post = Fabricate(:post)
        user = Fabricate(:user)

        md = <<~MD
        [quote="#{user.username}, post:#{post.post_number}, topic:#{post.topic.id}"]
        some quote

        #{Discourse.base_url}#{upload3.url}

        ![](#{upload.url})
        [/quote]
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        [quote="#{user.username}, post:#{post.post_number}, topic:#{post.topic.id}"]
        some quote

        ![](#{upload3.short_url})

        ![](#{upload.short_url})
        [/quote]
        MD
      end

      it "should correct markdown linked images" do
        md = <<~MD
        [![](#{upload.url})](https://somelink.com)

        [![some test](#{upload2.url})](https://somelink.com)
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        [![](#{upload.short_url})](https://somelink.com)

        [![some test](#{upload2.short_url})](https://somelink.com)
        MD
      end

      it "should correct markdown images with title" do
        md = <<~MD
        ![](#{upload.url} "some alt")
        ![testing](#{upload2.url}  'some alt'  )
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        ![](#{upload.short_url} "some alt")
        ![testing](#{upload2.short_url}  'some alt'  )
        MD
      end

      it "should correct bbcode img URLs to the short version" do
        md = <<~MD
        [img]http://some.external.img[/img]
        [img]#{upload.url}[/img]
        <img src="#{upload3.url}">

        [img]
        #{upload2.url}
        [/img]

        [img]#{upload.url}[/img][img]#{upload2.url}[/img]
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        [img]http://some.external.img[/img]
        ![](#{upload.short_url})
        ![](#{upload3.short_url})

        ![](#{upload2.short_url})

        ![](#{upload.short_url})![](#{upload2.short_url})
        MD
      end

      it "should correct markdown references" do
        md = <<~MD
        [link3][3]

        [3]: #{Discourse.base_url}#{upload2.url}

        This is a [link1][1] test [link2][2] something

        <img src="#{upload.url}">

        [1]: #{Discourse.base_url}#{upload.url}
        [2]: #{Discourse.base_url.sub("http://", "https://")}#{upload2.url}
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        [link3][3]

        [3]: #{Discourse.base_url}#{upload2.short_path}

        This is a [link1][1] test [link2][2] something

        ![](#{upload.short_url})

        [1]: #{Discourse.base_url}#{upload.short_path}
        [2]: #{Discourse.base_url}#{upload2.short_path}
        MD
      end

      it "should correct html and markdown uppercase references" do
        md = <<~MD
        [IMG]#{upload.url}[/IMG]
        <IMG src="#{upload2.url}" />
        <A class="attachment" href="#{upload3.url}">Text</A>
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        ![](#{upload.short_url})
        ![](#{upload2.short_url})
        [Text|attachment](#{upload3.short_url})
        MD
      end

      it "should correct image URLs with v parameters" do
        md = <<~MD
        <img src="#{upload.url}?v=1">

        <img src="#{Discourse.base_url}#{upload.url}?v=2">

        <img src="#{GlobalSetting.cdn_url}#{upload.url}?v=3">

        #{Discourse.base_url}#{upload.url}?v=45

        #{GlobalSetting.cdn_url}#{upload.url}?v=999
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        ![](#{upload.short_url})

        ![](#{upload.short_url})

        ![](#{upload.short_url})

        ![](#{upload.short_url})

        ![](#{upload.short_url})
        MD
      end

      context "subfolder" do
        before do
          set_subfolder "/community"
        end

        it "should correct subfolder images" do
          md = <<~MD
            <img src="/community#{upload.url}">

            #{Discourse.base_url}#{upload.url}
          MD

          expect(InlineUploads.process(md)).to eq(<<~MD)
            ![](#{upload.short_url})

            ![](#{upload.short_url})
          MD
        end
      end

      it "should correct raw image URLs to the short url and paths" do
        md = <<~MD
        #{Discourse.base_url}#{upload.url}

        #{Discourse.base_url}#{upload.url} #{Discourse.base_url}#{upload2.url}

        #{Discourse.base_url}#{upload3.url}

        #{GlobalSetting.cdn_url}#{upload3.url}
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        ![](#{upload.short_url})

        #{Discourse.base_url}#{upload.short_path} #{Discourse.base_url}#{upload2.short_path}

        ![](#{upload3.short_url})

        ![](#{upload3.short_url})
        MD
      end

      it "should correct non image URLs to the short url" do
        SiteSetting.authorized_extensions = "mp4"
        upload = Fabricate(:video_upload)
        upload2 = Fabricate(:video_upload)

        md = <<~MD
        #{Discourse.base_url}#{upload.url}

        #{Discourse.base_url}#{upload.url} #{Discourse.base_url}#{upload2.url}

        #{GlobalSetting.cdn_url}#{upload2.url}
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        #{Discourse.base_url}#{upload.short_path}

        #{Discourse.base_url}#{upload.short_path} #{Discourse.base_url}#{upload2.short_path}

        #{Discourse.base_url}#{upload2.short_path}
        MD
      end

      it "should correct img tags with uppercase upload extension" do
        md = <<~MD
        test<img src="#{upload.url.sub(".png", ".PNG")}">
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        test![](#{upload.short_url})
        MD
      end

      it "should correct image URLs that follows an image md" do
        md = <<~MD
        ![image|690x290](#{upload.short_url})#{Discourse.base_url}#{upload2.url}

        <#{Discourse.base_url}#{upload2.url}>
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        ![image|690x290](#{upload.short_url})#{Discourse.base_url}#{upload2.short_path}

        <#{Discourse.base_url}#{upload2.short_path}>
        MD
      end

      it "should correct image URLs to the short version" do
        md = <<~MD
        ![image|690x290](#{upload.short_url})

        ![IMAge|690x190,60%](#{upload.short_url})

        ![image](#{upload2.url})
        ![image|100x100](#{upload3.url})

        <img src="#{Discourse.base_url}#{upload.url}" alt="some image" title="some title" />
        <img src="#{Discourse.base_url}#{upload2.url}" alt="some image"><img src="#{Discourse.base_url}#{upload3.url}" alt="some image">

        #{Discourse.base_url}#{upload3.url} #{Discourse.base_url}#{upload3.url}

        <img src="#{upload.url}" width="5" height="4">
        <img src="#{upload.url}" width="5px" height="auto">

        `<img src="#{upload.url}" alt="image inside code quotes">`

        ```
        <img src="#{upload.url}" alt="image inside code fences">
        ```

            <img src="#{upload.url}" alt="image inside code block">
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        ![image|690x290](#{upload.short_url})

        ![IMAge|690x190,60%](#{upload.short_url})

        ![image](#{upload2.short_url})
        ![image|100x100](#{upload3.short_url})

        ![some image](#{upload.short_url} "some title")
        ![some image](#{upload2.short_url})![some image](#{upload3.short_url})

        #{Discourse.base_url}#{upload3.short_path} #{Discourse.base_url}#{upload3.short_path}

        ![|5x4](#{upload.short_url})
        ![](#{upload.short_url})

        `<img src="#{upload.url}" alt="image inside code quotes">`

        ```
        <img src="#{upload.url}" alt="image inside code fences">
        ```

            <img src="#{upload.url}" alt="image inside code block">
        MD
      end

      it "should not be affected by an emoji" do
        CustomEmoji.create!(name: 'test', upload: upload3)
        Emoji.clear_cache

        md = <<~MD
        :test:

        ![image|690x290](#{upload.url})
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        :test:

        ![image|690x290](#{upload.short_url})
        MD
      end

      it "should correctly update images sources within anchor tags with indentation" do
        md = <<~MD
        <h1></h1>
                        <a href="http://somelink.com">
                          <img src="#{upload2.url}" alt="test" width="500" height="500">
                        </a>

                        <a href="http://somelink.com">
                          <img src="#{upload2.url}" alt="test" width="500" height="500">
                        </a>
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        <h1></h1>
                        <a href="http://somelink.com">
                          ![test|500x500](#{upload2.short_url})
                        </a>

                        <a href="http://somelink.com">
                          <img src="#{upload2.url}" alt="test" width="500" height="500">
                        </a>
        MD

        md = "<h1></h1>\r\n<a href=\"http://somelink.com\">\r\n        <img src=\"#{upload.url}\" alt=\"test\" width=\"500\" height=\"500\">\r\n</a>"

        expect(InlineUploads.process(md)).to eq("<h1></h1>\r\n<a href=\"http://somelink.com\">\r\n        ![test|500x500](#{upload.short_url})\r\n</a>")
      end

      it "should correctly update image sources within anchor or paragraph tags" do
        md = <<~MD
        <a href="http://somelink.com">
          <img src="#{upload.url}" alt="test" width="500" height="500">
        </a>

        <p>
          <img src="#{upload2.url}" alt="test">
        </p>

        <a href="http://somelink.com"><img src="#{upload3.url}" alt="test" width="500" height="500"></a>

        <a href="http://somelink.com">  <img src="#{upload.url}" alt="test" width="500" height="500">  </a>

        <a href="http://somelink.com">


        <img src="#{upload.url}" alt="test" width="500" height="500">

        </a>

        <p>Test <img src="#{upload2.url}" alt="test" width="500" height="500"></p>

        <hr/>
        <img src="#{upload2.url}" alt="test" width="500" height="500">
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        <a href="http://somelink.com">
          ![test|500x500](#{upload.short_url})
        </a>

        <p>
          ![test](#{upload2.short_url})
        </p>

        <a href="http://somelink.com">![test|500x500](#{upload3.short_url})</a>

        <a href="http://somelink.com">  ![test|500x500](#{upload.short_url})  </a>

        <a href="http://somelink.com">


        ![test|500x500](#{upload.short_url})

        </a>

        <p>Test ![test|500x500](#{upload2.short_url})</p>

        <hr/>
        ![test|500x500](#{upload2.short_url})
        MD
      end

      it "should not be affected by fake HTML tags" do
        md = <<~MD
        ```
        This is some <img src=" and <a href="
        ```

        <img src="#{upload.url}" alt="test">

        <img src="#{upload2.url}" alt="test" height="150<img">

        > some quote

        <a class="attachment" href="#{upload2.url}">test2</a>
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        ```
        This is some <img src=" and <a href="
        ```

        ![test](#{upload.short_url})

        ![test](#{upload2.short_url})

        > some quote

        [test2|attachment](#{upload2.short_url})
        MD
      end

      it "should not be affected by an external or invalid links" do
        md = <<~MD
        <a id="test">invalid</a>

        [test]("https://this.is.some.external.link")

        <a href="https://some.external.com/link">test</a>

        <a class="attachment" href="#{upload2.url}">test2</a>
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        <a id="test">invalid</a>

        [test]("https://this.is.some.external.link")

        <a href="https://some.external.com/link">test</a>

        [test2|attachment](#{upload2.short_url})
        MD
      end

      it "should correct attachment URLS to the short version when raw contains inline image" do
        md = <<~MD
        ![image](#{upload.short_url}) ![image](#{upload.short_url})

        [some complicated.doc %50](#{upload3.url})

        <a class="attachment" href="#{upload2.url}">test2</a>
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        ![image](#{upload.short_url}) ![image](#{upload.short_url})

        [some complicated.doc %50](#{upload3.short_url})

        [test2|attachment](#{upload2.short_url})
        MD
      end

      it "should correct attachment URLs to the short version" do
        md = <<~MD
        <a class="attachment" href="#{upload.url}">
          this
          is
          some
          attachment

        </a>

        - <a class="attachment" href="#{upload.url}">test2</a>
          - <a class="attachment" href="#{upload2.url}">test2</a>
            - <a class="attachment" href="#{upload3.url}">test2</a>

        <a class="test attachment" href="#{upload.url}">test3</a>
        <a class="test attachment" href="#{upload2.url}">test3</a><a class="test attachment" href="#{upload3.url}">test3</a>

        <a class="test attachment" href="#{upload3.url}">This is some _test_ here</a>
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        [this is some attachment|attachment](#{upload.short_url})

        - [test2|attachment](#{upload.short_url})
          - [test2|attachment](#{upload2.short_url})
            - [test2|attachment](#{upload3.short_url})

        [test3|attachment](#{upload.short_url})
        [test3|attachment](#{upload2.short_url})[test3|attachment](#{upload3.short_url})

        [This is some _test_ here|attachment](#{upload3.short_url})
        MD
      end

      it 'should correct full upload url to the shorter version' do
        md = <<~MD
        Some random text

        ![test](#{upload.short_url})
        [test|attachment](#{upload.short_url})

        <a class="test attachment" href="#{upload.url}">
          test
        </a>

        `<a class="attachment" href="#{upload2.url}">In Code Block</a>`

            <a class="attachment" href="#{upload3.url}">In Code Block</a>

        <a href="#{upload.url}">newtest</a>
        <a href="#{Discourse.base_url_no_prefix}#{upload.url}">newtest</a>

        <a href="https://somerandomesite.com#{upload.url}">test</a>
        <a class="attachment" href="https://somerandom.com/url">test</a>
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        Some random text

        ![test](#{upload.short_url})
        [test|attachment](#{upload.short_url})

        [test|attachment](#{upload.short_url})

        `<a class="attachment" href="#{upload2.url}">In Code Block</a>`

            <a class="attachment" href="#{upload3.url}">In Code Block</a>

        [newtest](#{upload.short_url})
        [newtest](#{upload.short_url})

        <a href="https://somerandomesite.com#{upload.url}">test</a>
        <a class="attachment" href="https://somerandom.com/url">test</a>
        MD
      end

      it 'accepts a block that yields when link does not match an upload in the db' do
        url = "#{Discourse.base_url}#{upload.url}"

        md = <<~MD
        <img src="#{url}" alt="some image">
        <img src="#{upload2.url}" alt="some image">
        MD

        upload.destroy!

        InlineUploads.process(md, on_missing: lambda { |link|
          expect(link).to eq(url)
        })
      end
    end

    describe "s3 uploads" do
      let(:upload) { Fabricate(:upload_s3) }
      let(:upload2) { Fabricate(:upload_s3) }
      let(:upload3) { Fabricate(:upload) }

      before do
        upload3
        SiteSetting.enable_s3_uploads = true
        SiteSetting.s3_upload_bucket = "s3-upload-bucket"
        SiteSetting.s3_access_key_id = "some key"
        SiteSetting.s3_secret_access_key = "some secret key"
        SiteSetting.s3_cdn_url = "https://s3.cdn.com"
      end

      it "should correct image URLs to the short version" do
        md = <<~MD
        #{upload.url}
        <img src="#{upload.url}" alt="some image">
        test<img src="#{upload2.url}" alt="some image">test
        <img src="#{URI.join(SiteSetting.s3_cdn_url, URI.parse(upload2.url).path).to_s}" alt="some image">
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        ![](#{upload.short_url})
        ![some image](#{upload.short_url})
        test![some image](#{upload2.short_url})test
        ![some image](#{upload2.short_url})
        MD
      end

      it "should correct markdown references" do
        md = <<~MD
        This is a [some reference] somethign

        [some reference]: https:#{upload.url}
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        This is a [some reference] somethign

        [some reference]: #{Discourse.base_url}#{upload.short_path}
        MD
      end

      it "should correct image URLs in multisite", type: :multisite do
        md = <<~MD
        https:#{upload2.url} https:#{upload2.url}
        #{URI.join(SiteSetting.s3_cdn_url, URI.parse(upload2.url).path).to_s}

        <img src="#{upload.url}" alt="some image">
        <img src="#{URI.join(SiteSetting.s3_cdn_url, URI.parse(upload2.url).path).to_s}" alt="some image">
        <img src="#{upload3.url}">
        MD

        expect(InlineUploads.process(md)).to eq(<<~MD)
        #{Discourse.base_url}#{upload2.short_path} #{Discourse.base_url}#{upload2.short_path}
        #{Discourse.base_url}#{upload2.short_path}

        ![some image](#{upload.short_url})
        ![some image](#{upload2.short_url})
        ![](#{upload3.short_url})
        MD
      end
    end
  end

  describe ".match_md_inline_img" do
    it "matches URLs with various characters" do
      md = <<~MD
      ![test](https://some-site.com/a_test?q=1&b=hello%20there)
      MD

      url = nil
      InlineUploads.match_md_inline_img(md, external_src: true) { |_match, src| url = src }

      expect(url).to eq("https://some-site.com/a_test?q=1&b=hello%20there")
    end
  end
end