require 'open-uri'
require 'httparty'

class Shortener

  def process(item, clicked)
    begin
      url = URI.parse(item)
      url = URI.parse("http://" + item) unless url.scheme
    rescue URI::InvalidURIError
      if (clicked)
        $dz.fail("No valid URL on clipboard to shorten")
      else
        $dz.fail("Invalid URL")
      end
    end

    url_as_string = url.to_s

    if url_as_string =~ /https?:\/\/tinyurl\.com\//
      $dz.begin('Getting expanded URL...')
      expand_url(url_as_string)
    else
      if (clicked)
        config = "
        *.title = Confirm Shorten URL
        cb.type = cancelbutton
        txt.type = text
        txt.default = Shorten '#{url_as_string}' using TinyURL?
        "
        result = $dz.pashua(config)

        if result['cb'] == "1"
          $dz.fail("Cancelled")
        end
      end
      
      $dz.begin('Getting shortened URL...')
      shorten_url(url_as_string)
    end
  end

  def shorten_url(url_to_shorten)
    error_shown = false
    disable_verify = ENV['disable_ssl_verify'] == '1'
    
    begin
      if (ENV['api_token'] and ENV['api_token'].strip != '')
        proxy_opts = {}
        proxy_url = ENV['https_proxy'] || ENV['http_proxy']
        if proxy_url && !proxy_url.strip.empty?
          begin
            proxy_uri = URI.parse(proxy_url)
            proxy_opts = {
              http_proxyaddr: proxy_uri.host,
              http_proxyport: proxy_uri.port
            }
            proxy_opts[:http_proxyuser] = proxy_uri.user if proxy_uri.user
            proxy_opts[:http_proxypass] = proxy_uri.password if proxy_uri.password
          rescue URI::InvalidURIError
            # Ignore invalid proxy URL
          end
        end
        request_opts = {
          headers: { "Authorization" => "Bearer #{ENV['api_token']}", "accept" => "application/json", "Content-Type" => "application/json" },
          body: { url: url_to_shorten }.to_json,
          verify: !disable_verify
        }.merge(proxy_opts)

        result = HTTParty.post("https://api.tinyurl.com/create", request_opts)

        if (result['code'] == 0)
          # OK
          short_url = result['data']['tiny_url']
        elsif (result['code'] == 1)
          error_shown = true
          $dz.error('Failed to shorten URL', "TinyURL Authorization failed.\nCheck the API Token in the action options is set correctly.")
        else
          # Should be errors in error array
          if result['errors'] and result['errors'].length
            error_shown = true
            $dz.error('Failed to shorten URL', result['errors'][0])
          else
            # Unknow error, just show the code
            error_shown = true
            $dz.error('Failed to shorten URL', "TinyURL returned error code: #{result['code']}")
          end
        end
      else
        proxy_url = ENV['https_proxy'] || ENV['http_proxy']
        open_options = { "UserAgent" => "Ruby Script" }
        open_options[:proxy] = proxy_url if proxy_url && !proxy_url.strip.empty?
        open_options[:ssl_verify_mode] = OpenSSL::SSL::VERIFY_NONE if disable_verify
        short_url = open('https://tinyurl.com/api-create.php?url=' + url_to_shorten, open_options).read
      end
    rescue Exception => e
      $dz.error('Failed to shorten URL', e.message) unless error_shown
    end
    
    if short_url.to_s.strip.length == 0
      $dz.fail('Error shortening URL')
    else
      $dz.finish("URL is now on clipboard")
      $dz.url(short_url)
    end
  end

  def expand_url(url_to_expand)
    begin
      expanded_url = follow_redirect(url_to_expand)
    rescue Exception => e
      $dz.error('Failed to expand URL', e.message)
    end
    
    if expanded_url.to_s.strip.length == 0
      $dz.fail('Error expanding URL')
    else
      $dz.finish("Expanded URL is now on clipboard")
      $dz.url(expanded_url)
    end
  end
  
  def follow_redirect(uri_str, limit = 10)
    raise ArgumentError, 'too many HTTP redirects' if limit == 0
    response = Net::HTTP.get_response(URI(uri_str))

    case response
    when Net::HTTPSuccess then
      uri_str
    when Net::HTTPRedirection then
      location = response['location']
      follow_redirect(location, limit - 1)
    else
      uri_str
    end
  end
end