# typed: strict

require 'sorbet-runtime'
require 'json'
require 'yaml'
require 'benchmark'
require 'teams'
require 'code_ownership'
require 'pathname'
require 'modularization_statistics/private/source_code_file'
require 'modularization_statistics/private/datadog_reporter'
require 'parse_packwerk'
require 'package_protections'
require 'modularization_statistics/tag'
require 'modularization_statistics/tags'
require 'modularization_statistics/gauge_metric'

module ModularizationStatistics
  extend T::Sig

  ROOT_PACKAGE_NAME = T.let('root'.freeze, String)

  DEFAULT_COMPONENTIZED_SOURCE_CODE_LOCATIONS = T.let(
    [
      Pathname.new('components'),
      Pathname.new('gems'),
    ].freeze, T::Array[Pathname]
  )

  DEFAULT_PACKAGED_SOURCE_CODE_LOCATIONS = T.let(
    [
      Pathname.new('packs'),
      Pathname.new('packages'),
    ].freeze, T::Array[Pathname]
  )

  sig do
    params(
      datadog_client: Dogapi::Client,
      app_name: String,
      source_code_pathnames: T::Array[Pathname],
      componentized_source_code_locations: T::Array[Pathname],
      packaged_source_code_locations: T::Array[Pathname],
      report_time: Time,
      verbose: T::Boolean
    ).void
  end
  def self.report_to_datadog!(
    datadog_client:,
    app_name:,
    source_code_pathnames:,
    componentized_source_code_locations: DEFAULT_COMPONENTIZED_SOURCE_CODE_LOCATIONS,
    packaged_source_code_locations: DEFAULT_PACKAGED_SOURCE_CODE_LOCATIONS,
    report_time: Time.now, # rubocop:disable Rails/TimeZone
    verbose: false
  )

    all_metrics = self.get_metrics(
      source_code_pathnames: source_code_pathnames,
      componentized_source_code_locations: componentized_source_code_locations,
      packaged_source_code_locations: packaged_source_code_locations,
      app_name: app_name
    )

    # This helps us debug what metrics are being sent
    if verbose
      all_metrics.each do |metric|
        puts "Sending metric: #{metric}"
      end
    end

    Private::DatadogReporter.report!(
      datadog_client: datadog_client,
      report_time: report_time,
      metrics: all_metrics
    )
  end

  sig do
    params(
      source_code_pathnames: T::Array[Pathname],
      componentized_source_code_locations: T::Array[Pathname],
      packaged_source_code_locations: T::Array[Pathname],
      app_name: String
    ).returns(T::Array[GaugeMetric])
  end
  def self.get_metrics(
    source_code_pathnames:,
    componentized_source_code_locations:,
    packaged_source_code_locations:,
    app_name:
  )
    Private::DatadogReporter.get_metrics(
      source_code_files: source_code_files(
        source_code_pathnames: source_code_pathnames,
        componentized_source_code_locations: componentized_source_code_locations,
        packaged_source_code_locations: packaged_source_code_locations
      ),
      app_name: app_name
    )
  end

  sig do
    params(
      source_code_pathnames: T::Array[Pathname],
      componentized_source_code_locations: T::Array[Pathname],
      packaged_source_code_locations: T::Array[Pathname]
    ).returns(T::Array[Private::SourceCodeFile])
  end
  def self.source_code_files(
    source_code_pathnames:,
    componentized_source_code_locations:,
    packaged_source_code_locations:
  )

    # Sorbet has the wrong signatures for `Pathname#find`, whoops!
    componentized_file_set = Set.new(componentized_source_code_locations.select(&:exist?).flat_map { |pathname| T.unsafe(pathname).find.to_a })
    packaged_file_set = Set.new(packaged_source_code_locations.select(&:exist?).flat_map { |pathname| T.unsafe(pathname).find.to_a })

    source_code_pathnames.map do |pathname|
      componentized_file = componentized_file_set.include?(pathname)
      packaged_file = packaged_file_set.include?(pathname)

      Private::SourceCodeFile.new(
        pathname: pathname,
        team_owner: CodeOwnership.for_file(pathname.to_s),
        is_componentized_file: componentized_file,
        is_packaged_file: packaged_file
      )
    end
  end

  private_class_method :source_code_files
end