#!/usr/bin/env ruby
# frozen_string_literal: true

# CocoaPods generates an XCFramework selector from the vendored package's
# Info.plist. App Store delivery swaps in source-built VLCKit after the normal
# Flutter build, so the generated selector must be regenerated and checked
# against the staged package before Xcode archives it.

require 'json'
require 'open3'
require 'shellwords'
require 'set'

def abort_verification(message)
  warn "[appstore] CocoaPods XCFramework integration verification failed: #{message}"
  exit 1
end

def capture(*command)
  stdout, stderr, status = Open3.capture3(*command)
  return stdout if status.success?

  abort_verification("command failed: #{command.join(' ')}\n#{stderr.strip}")
end

unless ARGV.length == 2
  abort_verification(
    'usage: verify_cocoapods_xcframework_integration.rb XCFRAMEWORK COCOAPODS_SCRIPT'
  )
end

xcframework = File.expand_path(ARGV.fetch(0))
support_script = File.expand_path(
  ARGV.fetch(1) { abort_verification('CocoaPods support script is required') }
)

abort_verification("XCFramework not found: #{xcframework}") unless File.directory?(xcframework)
abort_verification("support script not found: #{support_script}") unless File.file?(support_script)

name = File.basename(xcframework, '.xcframework')
info_plist = File.join(xcframework, 'Info.plist')
libraries = JSON.parse(capture('/usr/bin/plutil', '-convert', 'json', '-o', '-', info_plist))
                .fetch('AvailableLibraries')
identifiers = libraries.map { |library| library.fetch('LibraryIdentifier') }
abort_verification('XCFramework has no library identifiers') if identifiers.empty?
unless identifiers.uniq.length == identifiers.length
  abort_verification("XCFramework has duplicate library identifiers: #{identifiers.inspect}")
end

libraries.each do |library|
  identifier = library.fetch('LibraryIdentifier')
  library_path = library.fetch('LibraryPath')
  path = File.join(xcframework, identifier, library_path)
  abort_verification("declared library is missing: #{path}") unless File.directory?(path)
end

script = File.read(support_script)
install_lines = script.lines.grep(/^install_xcframework\s+/)
unless install_lines.length == 1
  abort_verification("expected one install_xcframework invocation, found #{install_lines.length}")
end

arguments = Shellwords.shellsplit(install_lines.first)
expected_prefix = [
  'install_xcframework',
  "${PODS_ROOT}/#{name}/#{name}.xcframework",
  name,
  'framework'
]
unless arguments.first(expected_prefix.length) == expected_prefix
  abort_verification("unexpected install invocation: #{arguments.inspect}")
end

selected_identifiers = arguments.drop(expected_prefix.length)
unless selected_identifiers.to_set == identifiers.to_set &&
       selected_identifiers.length == identifiers.length
  abort_verification(
    "selector identifiers #{selected_identifiers.inspect} do not exactly match " \
    "Info.plist identifiers #{identifiers.inspect}"
  )
end

# Current CocoaPods scripts encode every identifier in both selector case
# functions and the install invocation. Requiring exactly those identifiers in
# every XCFramework case label catches a partially or stale regenerated file.
case_identifiers = script.scan(/"#{Regexp.escape(name)}\.xcframework\/([^"\/]+)"/).flatten
unless case_identifiers.to_set == identifiers.to_set
  abort_verification(
    "case identifiers #{case_identifiers.uniq.inspect} do not exactly match " \
    "Info.plist identifiers #{identifiers.inspect}"
  )
end
identifiers.each do |identifier|
  occurrences = case_identifiers.count(identifier)
  next if occurrences >= 2

  abort_verification("selector has only #{occurrences} case mapping(s) for #{identifier}")
end

puts "[appstore] verified CocoaPods #{name} selector matches staged XCFramework identifiers"
