// @ts-check
const path = require('path')
const fs = require('fs')
const getRootDir = require('../utils/get-root-dirs')
const {
  getUrlFromPagesDirectories,
  normalizeURL,
  execOnce,
} = require('../utils/url')

const pagesDirWarning = execOnce((pagesDirs) => {
  console.warn(
    `Pages directory cannot be found at ${pagesDirs.join(' or ')}. ` +
      `If using a custom path, please configure with the no-html-link-for-pages rule in your eslint config file`
  )
})

// Cache for fs.existsSync lookup.
// Prevent multiple blocking IO requests that have already been calculated.
const fsExistsSyncCache = {}

module.exports = {
  meta: {
    docs: {
      description: 'Prohibit full page refresh for Next.js pages',
      category: 'HTML',
      recommended: true,
      url: 'https://nextjs.org/docs/messages/no-html-link-for-pages',
    },
    fixable: null, // or "code" or "whitespace"
    schema: [
      {
        oneOf: [
          {
            type: 'string',
          },
          {
            type: 'array',
            uniqueItems: true,
            items: {
              type: 'string',
            },
          },
        ],
      },
    ],
  },

  /**
   * Creates an ESLint rule listener.
   *
   * @param {import('eslint').Rule.RuleContext} context - ESLint rule context
   * @returns {import('eslint').Rule.RuleListener} An ESLint rule listener
   */
  create: function (context) {
    /** @type {(string|string[])[]} */
    const ruleOptions = context.options
    const [customPagesDirectory] = ruleOptions

    const rootDirs = getRootDir(context)

    const pagesDirs = (
      customPagesDirectory
        ? [customPagesDirectory]
        : rootDirs.map((dir) => [
            path.join(dir, 'pages'),
            path.join(dir, 'src', 'pages'),
          ])
    ).flat()

    const foundPagesDirs = pagesDirs.filter((dir) => {
      if (fsExistsSyncCache[dir] === undefined) {
        fsExistsSyncCache[dir] = fs.existsSync(dir)
      }
      return fsExistsSyncCache[dir]
    })
    if (foundPagesDirs.length === 0) {
      pagesDirWarning(pagesDirs)
      return {}
    }

    const urls = getUrlFromPagesDirectories('/', foundPagesDirs)
    return {
      JSXOpeningElement(node) {
        if (node.name.name !== 'a') {
          return
        }

        if (node.attributes.length === 0) {
          return
        }

        const target = node.attributes.find(
          (attr) => attr.type === 'JSXAttribute' && attr.name.name === 'target'
        )

        if (target && target.value.value === '_blank') {
          return
        }

        const href = node.attributes.find(
          (attr) => attr.type === 'JSXAttribute' && attr.name.name === 'href'
        )

        if (!href || (href.value && href.value.type !== 'Literal')) {
          return
        }

        const hasDownloadAttr = node.attributes.find(
          (attr) =>
            attr.type === 'JSXAttribute' && attr.name.name === 'download'
        )

        if (hasDownloadAttr) {
          return
        }

        const hrefPath = normalizeURL(href.value.value)
        // Outgoing links are ignored
        if (/^(https?:\/\/|\/\/)/.test(hrefPath)) {
          return
        }

        urls.forEach((url) => {
          if (url.test(normalizeURL(hrefPath))) {
            context.report({
              node,
              message: `Do not use the HTML <a> tag to navigate to ${hrefPath}. Use Link from 'next/link' instead. See: https://nextjs.org/docs/messages/no-html-link-for-pages`,
            })
          }
        })
      },
    }
  },
}
