require 'openssl'
require 'base64'
require 'tempfile'

# OpenSSL 3.x rejects PKCS#8 keys via EC.new ("invalid curve name").
# Prepend a wrapper that falls back to PKey.read for both formats.
module OpenSSLECNewFix
  def new(arg = nil, *rest)
    super
  rescue OpenSSL::PKey::ECError
    raise if arg.nil? || !arg.is_a?(String)
    OpenSSL::PKey.read(arg)
  end
end
OpenSSL::PKey::EC.singleton_class.prepend(OpenSSLECNewFix)

def normalize_p8_key(raw)
  candidates = []

  cleaned = raw.gsub("\r", "")
  with_newlines = cleaned.gsub('\n', "\n")
  candidates << ["raw (newlines normalized)", with_newlines]

  if with_newlines.include?("BEGIN")
    b64_body = with_newlines.gsub(/-----(?:BEGIN|END)[^-]+-----/, '').gsub(/\s+/, '')
    rewrapped = "-----BEGIN PRIVATE KEY-----\n#{b64_body.scan(/.{1,64}/).join("\n")}\n-----END PRIVATE KEY-----\n"
    candidates << ["rewrapped PEM", rewrapped]
  end

  unless with_newlines.include?("BEGIN")
    body = cleaned.strip.gsub(/\s+/, '')
    pem = "-----BEGIN PRIVATE KEY-----\n#{body.scan(/.{1,64}/).join("\n")}\n-----END PRIVATE KEY-----\n"
    candidates << ["headerless body → PEM", pem]
  end

  begin
    decoded = Base64.decode64(cleaned.strip)
    if decoded.include?("BEGIN")
      decoded_clean = decoded.gsub("\r", "").gsub('\n', "\n")
      candidates << ["base64→PEM", decoded_clean]
      b64_body = decoded_clean.gsub(/-----(?:BEGIN|END)[^-]+-----/, '').gsub(/\s+/, '')
      rewrapped = "-----BEGIN PRIVATE KEY-----\n#{b64_body.scan(/.{1,64}/).join("\n")}\n-----END PRIVATE KEY-----\n"
      candidates << ["base64→PEM rewrapped", rewrapped]
    elsif decoded.length.between?(32, 256)
      candidates << ["base64→DER", decoded]
      der_pem = "-----BEGIN PRIVATE KEY-----\n#{Base64.strict_encode64(decoded).scan(/.{1,64}/).join("\n")}\n-----END PRIVATE KEY-----\n"
      candidates << ["base64→DER→PEM", der_pem]
    end
  rescue StandardError
    # not valid base64
  end

  begin
    double = Base64.decode64(Base64.decode64(cleaned.strip).strip)
    if double.include?("BEGIN")
      candidates << ["double-base64→PEM", double.gsub("\r", "")]
    elsif double.length.between?(32, 256)
      der_pem = "-----BEGIN PRIVATE KEY-----\n#{Base64.strict_encode64(double).scan(/.{1,64}/).join("\n")}\n-----END PRIVATE KEY-----\n"
      candidates << ["double-base64→DER→PEM", der_pem]
    end
  rescue StandardError
    # not double-encoded
  end

  candidates.each do |name, content|
    begin
      OpenSSL::PKey.read(content)
      UI.success("ASC key parsed with strategy: #{name}")
      return content
    rescue StandardError => e
      UI.message("Strategy '#{name}' failed: #{e.class} — #{e.message}")
    end
  end

  UI.error("=== ASC KEY DIAGNOSTIC (no key material shown) ===")
  UI.error("Raw byte length: #{raw.bytesize}")
  UI.error("Starts with BEGIN: #{raw.strip.start_with?('-----BEGIN')}")
  UI.error("Ends with -----: #{raw.strip.end_with?('-----')}")
  UI.error("Has real newlines: #{raw.include?("\n")}")
  UI.error("Has literal backslash-n: #{raw.include?('\\n')}")
  UI.error("Has carriage returns: #{raw.include?("\r")}")
  UI.error("Printable ASCII ratio: #{(raw.count(' -~').to_f / raw.bytesize * 100).round(1)}%")
  UI.error("Header (first 27 chars): #{raw[0..26]}")
  begin
    d = Base64.decode64(raw.strip)
    hex = d.bytes[0..15].map { |b| format('%02x', b) }.join(' ')
    UI.error("After base64 decode — length: #{d.bytesize}, first 16 bytes hex: #{hex}")
  rescue StandardError
    UI.error("base64 decode raised an exception")
  end
  UI.error("=== END DIAGNOSTIC ===")

  raise "Could not parse ASC_KEY_CONTENT in any known format. See diagnostics above."
end

default_platform(:ios)

platform :ios do
  desc "Build and upload to TestFlight"
  lane :beta do
    setup_ci(force: true)

    key_pem = normalize_p8_key(ENV["ASC_KEY_CONTENT"])

    key_path = File.join(Dir.tmpdir, "asc_api_key.p8")
    File.write(key_path, key_pem)

    app_store_connect_api_key(
      key_id: ENV["ASC_KEY_ID"],
      issuer_id: ENV["ASC_ISSUER_ID"],
      key_filepath: key_path
    )

    File.delete(key_path) if File.exist?(key_path)

    match(
      type: "appstore",
      readonly: true,
      keychain_name: "fastlane_tmp_keychain",
      keychain_password: ""
    )

    update_code_signing_settings(
      use_automatic_signing: false,
      team_id: "9KAQYC653W",
      code_sign_identity: "Apple Distribution",
      profile_name: "match AppStore com.seahavenind.proposals",
      path: "ios/ProposalSystem.xcodeproj"
    )

    node_path = sh("which node").strip
    xcode_env_path = File.join(__dir__, "..", "ios", ".xcode.env.local")
    File.write(xcode_env_path, "export NODE_BINARY=#{node_path}\n")
    UI.message("NODE_BINARY set to #{node_path} at #{xcode_env_path}")

    increment_build_number(
      build_number: ENV["GITHUB_RUN_NUMBER"],
      xcodeproj: "ios/ProposalSystem.xcodeproj"
    )

    build_app(
      workspace: "ios/ProposalSystem.xcworkspace",
      scheme: "ProposalSystem",
      configuration: "Release",
      export_method: "app-store",
      export_team_id: "9KAQYC653W",
      output_directory: "build",
      output_name: "ProposalSystem.ipa",
      xcodebuild_formatter: ""
    )

    upload_to_testflight(skip_waiting_for_build_processing: true)
  end
end
