import { Any, Cc, Z, P } from 'uc.micro'

export default function (opts) {
  const re = {}
  opts = opts || {}

  re.src_Any = Any.source
  re.src_Cc = Cc.source
  re.src_Z = Z.source
  re.src_P = P.source

  // \p{\Z\P\Cc\CF} (white spaces + control + format + punctuation)
  re.src_ZPCc = [re.src_Z, re.src_P, re.src_Cc].join('|')

  // \p{\Z\Cc} (white spaces + control)
  re.src_ZCc = [re.src_Z, re.src_Cc].join('|')

  // Experimental. List of chars, completely prohibited in links
  // because can separate it from other part of text
  const text_separators = '[><\uff5c]'

  // All possible word characters (everything without punctuation, spaces & controls)
  // Defined via punctuation & spaces to save space
  // Should be something like \p{\L\N\S\M} (\w but without `_`)
  re.src_pseudo_letter = `(?:(?!${text_separators}|${re.src_ZPCc})${re.src_Any})`
  // The same as abothe but without [0-9]
  // var src_pseudo_letter_non_d = '(?:(?![0-9]|' + src_ZPCc + ')' + src_Any + ')';

  re.src_ip4 =

    '(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'

  // Prohibit any of "@/[]()" in user/pass to avoid wrong domain fetch.
  // Length is capped to exclude possible rescans till the end and avoid O(n^2)
  // DoS. No standard limit, just take something reasonable.
  re.src_auth = `(?:(?:(?!${re.src_ZCc}|[@/\\[\\]()]).){1,50}@)?`

  re.src_port =

    '(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?'

  re.src_host_terminator =

    `(?=$|${text_separators}|${re.src_ZPCc})` +
    `(?!${opts['---'] ? '-(?!--)|' : '-|'}_|:\\d|\\.-|\\.(?!$|${re.src_ZPCc}))`

  re.src_path =

    '(?:' +
      '[/?#]' +
        '(?:' +
          `(?!${re.src_ZCc}|${text_separators}|[()[\\]{}.,"'?!\\-;]).|` +
          `\\[(?:(?!${re.src_ZCc}|\\]).)*\\]|` +
          `\\((?:(?!${re.src_ZCc}|[)]).)*\\)|` +
          `\\{(?:(?!${re.src_ZCc}|[}]).)*\\}|` +
          `\\"(?:(?!${re.src_ZCc}|["]).)+\\"|` +
          `\\'(?:(?!${re.src_ZCc}|[']).)+\\'|` +

          // allow `I'm_king` if no pair found
          `\\'(?=${re.src_pseudo_letter}|[-])|` +

          // google has many dots in "google search" links (#66, #81).
          // github has ... in commit range links,
          // Restrict to
          // - english
          // - percent-encoded
          // - parts of file path
          // - params separator
          // until more examples found.
          '\\.{2,}[a-zA-Z0-9%/&]|' +

          `\\.(?!${re.src_ZCc}|[.]|$)|` +
          (opts['---']
            ? '\\-(?!--(?:[^-]|$))(?:-*)|' // `---` => long dash, terminate
            : '\\-+|'
          ) +
          // allow `,,,` in paths
          `,(?!${re.src_ZCc}|$)|` +

          // allow `;` if not followed by space-like char
          `;(?!${re.src_ZCc}|$)|` +

          // allow `!!!` in paths, but not at the end
          `\\!+(?!${re.src_ZCc}|[!]|$)|` +

          `\\?(?!${re.src_ZCc}|[?]|$)` +
        ')+' +
      '|\\/' +
    ')?'

  // Allow anything in markdown spec, forbid quote (") at the first position
  // because emails enclosed in quotes are far more common
  // Max name length capped to 64 chars (RFC 5321). This also prevents O(n^2)
  // rescans to the end on inputs like `mailto:mailto:...`
  re.src_email_name =

    '[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]{0,63}'

  re.src_xn =

    'xn--[a-z0-9\\-]{1,59}'

  // More to read about domain names
  // http://serverfault.com/questions/638260/

  re.src_domain_root =

    // Allow letters & digits (http://test1)
    '(?:' +
      re.src_xn +
      '|' +
      `${re.src_pseudo_letter}{1,63}` +
    ')'

  re.src_domain =

    '(?:' +
      re.src_xn +
      '|' +
      `(?:${re.src_pseudo_letter})` +
      '|' +
      `(?:${re.src_pseudo_letter}(?:-|${re.src_pseudo_letter}){0,61}${re.src_pseudo_letter})` +
    ')'

  re.src_host =

    '(?:' +
    // Don't need IP check, because digits are already allowed in normal domain names
    //   src_ip4 +
    // '|' +
      `(?:(?:(?:${re.src_domain})\\.)*${re.src_domain})`/* _root */ +
    ')'

  re.tpl_host_fuzzy =

    '(?:' +
      re.src_ip4 +
    '|' +
      `(?:(?:(?:${re.src_domain})\\.)+(?:%TLDS%))` +
    ')'

  re.tpl_host_no_ip_fuzzy =

    `(?:(?:(?:${re.src_domain})\\.)+(?:%TLDS%))`

  re.src_host_strict =

    re.src_host + re.src_host_terminator

  re.tpl_host_fuzzy_strict =

    re.tpl_host_fuzzy + re.src_host_terminator

  re.src_host_port_strict =

    re.src_host + re.src_port + re.src_host_terminator

  re.tpl_host_port_fuzzy_strict =

    re.tpl_host_fuzzy + re.src_port + re.src_host_terminator

  re.tpl_host_port_no_ip_fuzzy_strict =

    re.tpl_host_no_ip_fuzzy + re.src_port + re.src_host_terminator

  //
  // Main rules
  //

  // Rude test fuzzy links by host, for quick deny
  re.tpl_host_fuzzy_test =

    `localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:${re.src_ZPCc}|>|$))`

  re.tpl_email_fuzzy =

      `(^|${text_separators}|"|\\(|${re.src_ZCc})` +
      `(${re.src_email_name}@${re.tpl_host_fuzzy_strict})`

  re.tpl_link_fuzzy =
      // Fuzzy link can't be prepended with .:/\- and non punctuation.
      // but can start with > (markdown blockquote)
      `(^|(?![.:/\\-_@])(?:[$+<=>^\`|\uff5c]|${re.src_ZPCc}))` +
      `((?![$+<=>^\`|\uff5c])${re.tpl_host_port_fuzzy_strict}${re.src_path})`

  re.tpl_link_no_ip_fuzzy =
      // Fuzzy link can't be prepended with .:/\- and non punctuation.
      // but can start with > (markdown blockquote)
      `(^|(?![.:/\\-_@])(?:[$+<=>^\`|\uff5c]|${re.src_ZPCc}))` +
      `((?![$+<=>^\`|\uff5c])${re.tpl_host_port_no_ip_fuzzy_strict}${re.src_path})`

  return re
}
