# frozen_string_literal: true

require 'open3'

module UITest
  # Manages app build, install, and launch lifecycle
  class App
    class AppError < StandardError; end

    class << self
      # Build the app for simulator
      # @param device [String] Device name for destination
      # @param skip [Boolean] Skip build if true
      def build(device:, skip: false)
        if skip
          puts 'Skipping build (--skip-build)'
          validate_app_exists!
          return
        end

        puts "Building Yattee for #{device}..."

        args = [
          'xcodebuild',
          '-project', Config.xcodeproj,
          '-scheme', Config.scheme,
          '-configuration', Config.configuration,
          '-destination', "platform=iOS Simulator,name=#{device}",
          '-derivedDataPath', Config.derived_data_path,
          'build'
        ]

        # Run build and capture output
        output, status = Open3.capture2e(*args)

        unless status.success?
          # Extract relevant error lines
          error_lines = output.lines.select { |l| l.include?('error:') }.join
          raise AppError, "Build failed:\n#{error_lines.empty? ? output.last(2000) : error_lines}"
        end

        puts 'Build succeeded'
        validate_app_exists!
      end

      # Install app to simulator
      # @param udid [String] UDID of the simulator
      # By default, uninstalls first to reset app data (use --keep-app-data to skip)
      def install(udid:)
        validate_app_exists!

        # Uninstall first to reset app data (unless --keep-app-data)
        unless Config.keep_app_data?
          puts 'Resetting app data (uninstalling previous install)...'
          uninstall(udid: udid)

          # Reset keychain to prevent "Save Password?" dialogs during tests
          puts 'Resetting simulator keychain...'
          reset_keychain(udid: udid)
        end

        puts 'Installing app...'
        output, status = Open3.capture2e('xcrun', 'simctl', 'install', udid, Config.app_path)

        raise AppError, "Install failed: #{output}" unless status.success?

        puts 'App installed'
      end

      # Launch app on simulator
      # @param udid [String] UDID of the simulator
      def launch(udid:)
        puts 'Launching app...'

        # Terminate if already running
        terminate(udid: udid, silent: true)

        # Skip onboarding in tests by passing launch argument
        # iOS apps accept UserDefaults overrides via launch arguments
        output, status = Open3.capture2e(
          'xcrun', 'simctl', 'launch', udid, Config.bundle_id,
          '-onboardingCompleted', 'YES'
        )

        raise AppError, "Launch failed: #{output}" unless status.success?

        puts 'App launched'
      end

      # Terminate app on simulator
      # @param udid [String] UDID of the simulator
      # @param silent [Boolean] Don't raise error if app not running
      def terminate(udid:, silent: false)
        output, status = Open3.capture2e('xcrun', 'simctl', 'terminate', udid, Config.bundle_id)

        # simctl terminate returns non-zero if app isn't running
        return if silent

        raise AppError, "Terminate failed: #{output}" unless status.success?
      end

      # Uninstall app from simulator
      # @param udid [String] UDID of the simulator
      def uninstall(udid:)
        Open3.capture2e('xcrun', 'simctl', 'uninstall', udid, Config.bundle_id)
        # Ignore errors - app might not be installed
      end

      # Reset simulator keychain to prevent "Save Password?" dialogs
      # @param udid [String] UDID of the simulator
      def reset_keychain(udid:)
        Open3.capture2e('xcrun', 'simctl', 'keychain', udid, 'reset')
        # Ignore errors - reset is best effort
      end

      private

      def validate_app_exists!
        return if File.exist?(Config.app_path)

        raise AppError, "App not found at #{Config.app_path}. Run build first or remove --skip-build"
      end
    end
  end
end
