
require_relative "../helpers/git"
require_relative "../helpers/config"
require "yaml"
require "shellwords"

module Fastlane
  module Actions
    class BuildArtifactsAction < Action
      def self.run(params)
        unless params[:skip_resolve_dependencies]
          other_action.resolve_dependencies(build_flavor: params[:build_flavor])
        end
        build_config = BuildConfig.for(params[:build_flavor])
        build_configuration_name = "#{params[:build_type].capitalize}-#{params[:build_flavor] == BuildFlavor::STAFF ? "Staff" : "Production"}"
        build_version = other_action.build_version(build_flavor: params[:build_flavor], pr_number: params[:pr_number])
        build_name = "suno-ios-#{params[:build_flavor]}-#{params[:build_type]}-build-#{build_version[:version_code]}-v#{build_version[:version_name]}"

        unless params[:skip_sync_profiles]
          # ensure that xcodeproj file is not modified in the current git status
          if `git status --porcelain`.include?(build_config[:project])
            UI.user_error!("#{build_config[:project]} is modified in the current worktree. Please commit or stash your changes before retrying.")
          end
        end

        begin
          unless params[:skip_sync_profiles]
            UI.message("Syncing xcode signing settings for #{build_config[:project]}...")
            build_config[:provisioning_profiles].each do |identifier, profile_name|
              target_name = if identifier == build_config[:app_identifier]
                build_config[:target]
              else
                identifier.split(".").last
              end
              other_action.update_code_signing_settings(
                path: build_config[:project],
                use_automatic_signing: false,
                targets: [target_name],
                build_configurations: [build_configuration_name],
                profile_name: profile_name,
                code_sign_identity: build_config[:code_sign_identity],
                entitlements_file_path: build_config[:entitlements][identifier]
              )
            end
            other_action.sync_profiles(
              build_flavor: params[:build_flavor],
              build_type: params[:build_type],
              sync_devices: params[:sync_devices]
            )
          end

          other_action.build_ios_app(
            scheme: build_config[:scheme],
            project: build_config[:project],
            configuration: build_configuration_name,
            cloned_source_packages_path: params[:source_packages_path],
            derived_data_path: params[:derived_data_path],
            buildlog_path: params[:logs_dir],
            output_directory: params[:output_dir],
            output_name: "#{build_name}.ipa",
            disable_package_automatic_updates: true,
            skip_package_dependencies_resolution: true,
            xcargs: [
              "MARKETING_VERSION=#{Shellwords.shellescape(build_version[:version_name])}",
              "CURRENT_PROJECT_VERSION=#{Shellwords.shellescape(build_version[:version_code].to_s)}",
            ].join(" "),
          )
        ensure
          unless params[:skip_sync_profiles]
            UI.message("Reverting changes in #{build_config[:project]} file...")
            `git checkout -- #{build_config[:project]}`
          end
        end

        ipa_output_path = lane_context[SharedValues::IPA_OUTPUT_PATH]

        if params[:build_type] == BuildType::RELEASE
          unless params[:skip_release_checks]
            run_release_checks(
              build_flavor: params[:build_flavor],
              ipa_path: ipa_output_path
            )
          end
          unless params[:skip_uploading_symbols]
            UI.message("Uploading symbols to Crashlytics...")
            other_action.upload_symbols_to_crashlytics(
              gsp_path: build_config[:google_services_plist]
            )
          end
        end

        {
          **build_version,
          build_name: build_name,
          build_type: params[:build_type],
          build_flavor: params[:build_flavor],
          output_dir: params[:output_dir],
          output_path: lane_context[SharedValues::IPA_OUTPUT_PATH]
        }
      end

      def self.run_release_checks(build_flavor:, ipa_path:, expected_aps_environment: "production", expected_domain: "applinks:suno.com")
        UI.message("Running release checks on #{ipa_path}...")
        artifact_info = other_action.analyze_artifacts(artifact_path: ipa_path)
        unless artifact_info[:build_type] == BuildType::RELEASE
          UI.user_error!("Build type mismatched (expected: '#{BuildType::RELEASE}', actual: '#{artifact_info[:build_type]}')")
        end
        unless artifact_info[:build_flavor] == build_flavor
          UI.user_error!("Build flavor mismatched (expected: '#{build_flavor}', actual: '#{artifact_info[:build_flavor]}')")
        end
        unless artifact_info[:entitlements].is_a?(Hash)
          UI.user_error!("Entitlements must be a hash. Got: #{artifact_info[:entitlements]}")
        end
        unless artifact_info[:entitlements]["aps-environment"] == expected_aps_environment
          UI.user_error!("Entitlements must have aps-environment set to '#{expected_aps_environment}'. Got: '#{artifact_info[:entitlements]["aps-environment"]}'")
        end
        unless artifact_info[:entitlements]["com.apple.developer.associated-domains"].is_a?(Array)
          UI.user_error!("Entitlements must have associated-domains set to an array. Got: #{artifact_info[:entitlements]["com.apple.developer.associated-domains"]}")
        end
        unless artifact_info[:entitlements]["com.apple.developer.associated-domains"].include?(expected_domain)
          UI.user_error!("Entitlements must have associated-domains with '#{expected_domain}'. Got: #{artifact_info[:entitlements]["com.apple.developer.associated-domains"]}'")
        end
      end

      def self.description
        "Runs a xcode build of the app"
      end

      def self.available_options
        [
          FastlaneCore::ConfigItem.new(
            key: :build_type,
            description: "The build type (#{BuildType::ALL.join(', ')})",
            default_value: BuildType::RELEASE,
            verify_block: proc do |value|
              UI.user_error!("Invalid build type (must be one of #{BuildType::ALL.join(', ')})") unless BuildType::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: :sync_devices,
            description: "Sync devices before syncing the profiles",
            type: Boolean,
            default_value: false
          ),
          FastlaneCore::ConfigItem.new(
            key: :skip_resolve_dependencies,
            description: "Skip resolving dependencies",
            optional: true,
            default_value: false,
            type: Boolean,
          ),
          FastlaneCore::ConfigItem.new(
            key: :skip_sync_profiles,
            description: "Skip syncing provisioning profiles before signing",
            type: Boolean,
            default_value: false
          ),
          FastlaneCore::ConfigItem.new(
            key: :skip_uploading_symbols,
            description: "Skip uploading symbols to Crashlytics",
            optional: true,
            default_value: false,
            type: Boolean,
          ),
          FastlaneCore::ConfigItem.new(
            key: :skip_release_checks,
            description: "Skip release checks",
            optional: true,
            default_value: false,
            type: Boolean,
          ),
          FastlaneCore::ConfigItem.new(key: :pr_number,
            description: "Pull request number that triggered the distribution",
            optional: true,
            type: Integer,
            default_value: Github.pr_number,
            verify_block: proc do |value|
              UI.user_error!("Pull request number must be a positive integer") unless value.nil? || value > 0
            end),
          *BuildConfig.default_options
        ]
      end

      def self.authors
        ["Suno"]
      end

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