require_relative "../helpers/config"

module Fastlane
  module TestMode
    BUILD_AND_TEST = "build-and-test"
    TEST_ONLY      = "test-only"
    BUILD_ONLY     = "build-only"
    ALL            = [BUILD_AND_TEST, TEST_ONLY, BUILD_ONLY]
  end

  module Actions
    class RunShardedTestsAction < Action
      def self.run(params)
        unless params[:skip_resolve_dependencies] && params[:mode] != TestMode::TEST_ONLY
          other_action.resolve_dependencies(build_flavor: params[:build_flavor])
        end

        mode = params[:mode]
        logs_dir = params[:logs_dir]
        output_dir = params[:output_dir]
        build_flavor = params[:build_flavor]
        build_config = BuildConfig.for(build_flavor)
        unless build_config[:testing]
          UI.user_error!("Testing is not enabled for build flavor: #{build_flavor}")
        end

        xctestrun_path = Dir[File.join(params[:derived_data_path], "Build/Products/*.xctestrun")].first
        test_targets = (params[:targets] || "").split(",").map(&:strip).reject(&:empty?)
        if test_targets.empty? && !xctestrun_path.nil?
          test_targets = other_action.tests_from_xctestrun(xctestrun: xctestrun_path).map { |target, tests| tests }.flatten.uniq.to_a
        end
        shard_index = params[:shard_index]
        shard_count = params[:shard_count]
        if !shard_index.nil? && !shard_count.nil?
          shard_size = (test_targets.length.to_f / shard_count).ceil
          start_index = (shard_index - 1) * shard_size
          end_index = [start_index + shard_size - 1, test_targets.length - 1].min
          UI.message("Choosen range for shard #{shard_index} of #{shard_count}: test targets #{start_index+1} to #{end_index+1} of #{test_targets.length}")
          test_targets = test_targets[start_index..end_index]
        end

        # Base options shared across modes
        options = {
          project: build_config[:project],
          scheme: build_config[:scheme],
          cloned_source_packages_path: params[:source_packages_path],
          derived_data_path: params[:derived_data_path],
          device: build_config[:testing][:device],
          deployment_target_version: build_config[:testing][:deployment_target_version],
          only_testing: test_targets.empty? ? nil : test_targets,
          buildlog_path: params[:logs_dir],
          output_directory: params[:output_dir],
          xcargs: build_config[:testing][:xcargs],
          disable_package_automatic_updates: true,
          skip_package_dependencies_resolution: true,
        }

        # Mode-specific behavior
        case mode
        when TestMode::TEST_ONLY
          # Reuse prior build artifacts: require an .xctestrun produced by a previous "build-for-testing"
          unless xctestrun_path
            UI.user_error!("'mode:#{TestMode::TEST_ONLY}' requires an existing .xctestrun file. Run with 'mode:#{TestMode::BUILD_ONLY}' first.")
          end
          options[:test_without_building] = true
          options[:xctestrun] = xctestrun_path
          options[:result_bundle_path] = File.join(output_dir, "result.xcresult")
        when TestMode::BUILD_ONLY
          # Only compile tests; don't execute them
          options[:build_for_testing] = true
        when TestMode::BUILD_AND_TEST
          # Default multi_scan behavior (build + run tests). No extra flags needed.
        else
          options[:result_bundle_path] = File.join(output_dir, "result.xcresult")
          UI.user_error!("Unsupported mode: #{mode}")
        end

        # TODO: re-enable multi-scan after https://github.com/fastlane/fastlane/issues/20012 is fixed
        # other_action.multi_scan(**options)
        other_action.scan(**options)
      end

      private

      def self.description
        "Runs tests with sharding support"
      end

      def self.available_options
        [
          FastlaneCore::ConfigItem.new(
            key: :mode,
            description: "Test execution mode (one of #{TestMode::ALL.join(', ')})",
            default_value: TestMode::BUILD_AND_TEST,
            verify_block: proc do |value|
              UI.user_error!("Invalid mode (must be one of #{TestMode::ALL.join(', ')}): #{value}") unless TestMode::ALL.include?(value)
            end
          ),
          FastlaneCore::ConfigItem.new(
            key: :build_flavor,
            description: "The build flavor (#{BuildFlavor::ALL.join(', ')})",
            verify_block: proc do |value|
              UI.user_error!("Invalid flavor (must be one of #{BuildFlavor::ALL.join(', ')})") unless BuildFlavor::ALL.include?(value)
            end,
            default_value: BuildFlavor::PROD
          ),
          FastlaneCore::ConfigItem.new(
            key: :targets,
            description: "Comma-separated list of test targets/suites to run",
            optional: true
          ),
          FastlaneCore::ConfigItem.new(
            key: :shard_index,
            env_name: "SHARD_INDEX",
            description: "Shard index for test sharding (1-based index)",
            type: Integer,
            optional: true
          ),
          FastlaneCore::ConfigItem.new(
            key: :shard_count,
            env_name: "SHARD_COUNT",
            description: "Total number of shards for test sharding",
            type: Integer,
            optional: true
          ),
          FastlaneCore::ConfigItem.new(
            key: :skip_resolve_dependencies,
            description: "Skip resolving dependencies",
            optional: true,
            default_value: false,
            type: Boolean,
          ),
          *BuildConfig.default_options
        ]
      end

      def self.authors
        ["Suno"]
      end

      def self.is_supported?(platform)
        platform == :ios
      end
    end
  end
end
