# frozen_string_literal: true

require 'tmpdir'
require 'json'

module Jazzy
  # rubocop:disable Metrics/ClassLength
  class PodspecDocumenter
    attr_reader :podspec

    def initialize(podspec)
      @podspec = podspec
    end

    # Build documentation from the given options
    # @param [Config] options
    def sourcekitten_output(config)
      installation_root = Pathname(Dir.mktmpdir(['jazzy', podspec.name]))
      installation_root.rmtree if installation_root.exist?
      Pod::Config.instance.with_changes(installation_root: installation_root,
                                        verbose: false) do
        sandbox = Pod::Sandbox.new(Pod::Config.instance.sandbox_root)
        installer = Pod::Installer.new(sandbox, podfile(config))
        installer.install!
        stdout = Dir.chdir(sandbox.root) do
          targets = installer.pod_targets
            .select { |pt| pt.pod_name == podspec.root.name }
            .map(&:label)

          targets.map do |t|
            args = %W[doc --module-name #{podspec.module_name} -- -target #{t}]
            SourceKitten.run_sourcekitten(args)
          end
        end
        stdout.reduce([]) { |a, s| a + JSON.parse(s) }.to_json
      end
    end

    def self.create_podspec(podspec_path)
      case podspec_path
      when Pathname, String
        require 'cocoapods'
        Pod::Specification.from_file(podspec_path)
      end
    end

    # rubocop:disable Metrics/MethodLength
    def self.apply_config_defaults(podspec, config)
      return unless podspec

      unless config.author_name_configured
        config.author_name = author_name(podspec)
        config.author_name_configured = true
      end
      unless config.module_name_known?
        config.module_name = podspec.module_name
        config.module_name_configured = true
      end
      unless config.author_url_configured
        config.author_url = podspec.homepage || github_file_prefix(podspec)
        config.author_url_configured = true
      end
      unless config.version_configured
        config.version = podspec.version.to_s
        config.version_configured = true
      end
      unless config.source_host_files_url_configured
        config.source_host_files_url = github_file_prefix(podspec)
        config.source_host_files_url_configured = true
      end
      unless config.swift_version_configured
        trunk_swift_build = podspec.attributes_hash['pushed_with_swift_version']
        config.swift_version = trunk_swift_build if trunk_swift_build
        config.swift_version_configured = true
      end
    end
    # rubocop:enable Metrics/MethodLength

    private

    # @!group Config helper methods

    def self.author_name(podspec)
      if podspec.authors.respond_to? :to_hash
        podspec.authors.keys.to_sentence || ''
      elsif podspec.authors.respond_to? :to_ary
        podspec.authors.to_sentence
      end || podspec.authors || ''
    end

    private_class_method :author_name

    def self.github_file_prefix(podspec)
      return unless podspec.source[:url] =~ %r{github.com[:/]+(.+)/(.+)}

      org, repo = Regexp.last_match
      return unless rev = podspec.source[:tag] || podspec.source[:commit]

      "https://github.com/#{org}/#{repo.sub(/\.git$/, '')}/blob/#{rev}"
    end

    private_class_method :github_file_prefix

    # Latest valid value for SWIFT_VERSION.
    LATEST_SWIFT_VERSION = '6'

    # All valid values for SWIFT_VERSION that are longer
    # than a major version number.  Ordered ascending.
    LONG_SWIFT_VERSIONS = ['4.2'].freeze

    # Go from a full Swift version like 4.2.1 to
    # something valid for SWIFT_VERSION.
    def compiler_swift_version(user_version)
      unless user_version
        return podspec_swift_version || LATEST_SWIFT_VERSION
      end

      LONG_SWIFT_VERSIONS.select do |version|
        user_version.start_with?(version)
      end.last || "#{user_version[0]}.0"
    end

    def podspec_swift_version
      # `swift_versions` exists from CocoaPods 1.7
      if podspec.respond_to?('swift_versions')
        podspec.swift_versions.max
      else
        podspec.swift_version
      end
    end

    # @!group SourceKitten output helper methods

    def pod_path
      if podspec.defined_in_file
        podspec.defined_in_file.parent
      else
        config.source_directory
      end
    end

    # rubocop:disable Metrics/MethodLength
    def podfile(config)
      swift_version = compiler_swift_version(config.swift_version)
      podspec = @podspec
      path = pod_path
      @podfile ||= Pod::Podfile.new do
        config.pod_sources.each do |src|
          source src
        end

        install! 'cocoapods',
                 integrate_targets: false,
                 deterministic_uuids: false

        [podspec, *podspec.recursive_subspecs].each do |ss|
          next if ss.test_specification

          ss.available_platforms.each do |p|
            # Travis builds take too long when building docs for all available
            # platforms for the Moya integration spec, so we just document OSX.
            # TODO: remove once jazzy is fast enough.
            next if ENV['JAZZY_INTEGRATION_SPECS'] && p.name != :osx

            target("Jazzy-#{ss.name.gsub('/', '__')}-#{p.name}") do
              use_frameworks!
              platform p.name, p.deployment_target
              pod ss.name, path: path.realpath.to_s
              current_target_definition.swift_version = swift_version
            end
          end
        end
      end
    end
    # rubocop:enable Metrics/MethodLength
  end
  # rubocop:enable Metrics/ClassLength
end