# frozen_string_literal: true

module Danger
  class Tailwind
    FRONTEND_INTERPOLATION_PATTERN = /gl-[0-9a-z-]+(\$\{|['"] ?\+)/
    BACKEND_INTERPOLATION_PATTERN = /gl-[0-9a-z-]+(#\{|['"] ?\+)/

    def initialize(helper, git)
      @helper = helper
      @git = git
    end

    def report_interpolated_utils
      frontend_files_with_interpolated_utils = files_with_interpolated_utils(
        frontend_tailwindy_files(@helper.all_changed_files), FRONTEND_INTERPOLATION_PATTERN)
      backend_files_with_interpolated_utils = files_with_interpolated_utils(
        backend_tailwindy_files(@helper.all_changed_files), BACKEND_INTERPOLATION_PATTERN)

      return "" if frontend_files_with_interpolated_utils.empty? && backend_files_with_interpolated_utils.empty?

      <<~MARKDOWN
      ### Interpolated utils

      The following files might contain interpolated utils:
      #{format_files_list(frontend_files_with_interpolated_utils + backend_files_with_interpolated_utils)}

      If you are leveraging CSS utilities, make sure they are written in full and not built via string
      interpolation as that would prevent Tailwind CSS from generating them.
      MARKDOWN
    end

    def report_legacy_utils_usage
      `yarn tailwindcss:build`

      legacy_utils = File
        .read("./config/helpers/tailwind/css_in_js.js")
        .scan(/'(\.[^\']*)'/).flatten.map do |legacy_util|
          legacy_util.gsub('.', 'gl-').gsub('\\\\!', '!')
        end

      files_with_legacy_utils = @helper.all_changed_files.flat_map do |file|
        next [] if file.end_with?('tailwind_equivalents.json')

        diff = @git.diff_for_file(file)

        # When a file is just moved around it appears in the changed files list
        # but the diff is empty so we are skipping it.
        next [] if diff.nil?

        used_legacy_utils = diff.patch.each_line.flat_map do |line|
          next [] unless line.start_with?('+')

          legacy_utils.select do |legacy_util|
            legacy_util_regex = if legacy_util.end_with?('!')
                                  /#{legacy_util.gsub('\\\\!', '!')}/
                                else
                                  /#{legacy_util}(?!!)/
                                end

            line.match?(legacy_util_regex)
          end
        end

        next [] if used_legacy_utils.empty?

        [[file, used_legacy_utils]]
      end

      return "" if files_with_legacy_utils.empty?

      <<~MARKDOWN
      ### Legacy CSS utility classes

      The following files contain legacy CSS utility classes:
      #{format_files_with_legacy_utils_list(files_with_legacy_utils)}

      We are in the process of migrating our CSS utility classes to [Tailwind CSS](https://tailwindcss.com/).
      The above CSS utility classes do not comply with Tailwind CSS naming conventions.
      Please use the Tailwind CSS equivalent if it is available.
      For more information see [Tailwind CSS developer documentation](https://docs.gitlab.com/ee/development/fe_guide/style/scss.html#tailwind-css).

      If the Tailwind CSS equivalent is not available, it is okay to use the legacy CSS utility class for now.
      The Tailwind CSS equivalent will be made available when the corresponding migration issue
      in [&13521](https://gitlab.com/groups/gitlab-org/-/epics/13521) is completed.

      If a legacy CSS utility class is listed above but you did not change it in this MR it is okay to leave for now.
      If it is a small or simple MR, feel free to leave the code better than you found it and migrate those
      legacy CSS utility classes to Tailwind CSS.
      MARKDOWN
    end

    private

    def frontend_tailwindy_files(files)
      files.select do |file|
        file.end_with?('.vue', '.js')
      end
    end

    def backend_tailwindy_files(files)
      files.select do |file|
        file.end_with?('.html.haml', '.rb')
      end
    end

    def files_with_interpolated_utils(files, pattern)
      files.select do |file|
        diff = @git.diff_for_file(file)

        # When a file is just moved around it appears in the changed files list
        # but the diff is empty so we are skipping it.
        next if diff.nil?

        diff.patch.each_line.any? do |line|
          line.start_with?('+') && line.match?(pattern)
        end
      end
    end

    def format_files_list(files)
      files.map do |file|
        "- `#{file}`"
      end.join("\n")
    end

    def format_files_with_legacy_utils_list(files)
      # rubocop:disable Gitlab/Json -- we are outside of the GitLab Rails context
      # and therefore do not have access to the dependencies required for
      # Gitlab::Json.parse to work.
      tailwind_equivalents = JSON.parse(
        File.read("./scripts/frontend/tailwind_equivalents.json")
      )
      # rubocop:enable Gitlab/Json

      files.map do |file, legacy_utils|
        legacy_utils_bullet_points = legacy_utils.map do |legacy_util|
          tailwind_equivalent = tailwind_equivalents[legacy_util] || "Not available"
          "  - `#{legacy_util}` - Tailwind CSS equivalent: `#{tailwind_equivalent}`"
        end.join("\n")

        "- `#{file}`\n" + legacy_utils_bullet_points
      end.join("\n")
    end
  end
end

danger_tailwind = Danger::Tailwind.new(helper, git)
report = danger_tailwind.report_interpolated_utils + danger_tailwind.report_legacy_utils_usage

unless report.empty?
  markdown <<~MSG
   ## Tailwind CSS

   #{report}
  MSG
end
