require "json"
require "uri"

manifest_path = ENV.fetch("INTERACTIVE_GAMES_MANIFEST")
apply_changes = ENV["APPLY"] == "1"
manifest = JSON.parse(File.read(manifest_path))
games = manifest.fetch("games")
expected_count = manifest.fetch("expected_game_count")
expected_active_count = manifest.fetch("expected_active_count")

raise "Unsupported manifest schema" unless manifest.fetch("schema_version") == 1
raise "Expected #{expected_count} games, found #{games.length}" unless games.length == expected_count

codes = games.map { |game| game.fetch("game_code") }
urls = games.map { |game| game.fetch("game_url") }
active_games = games.select { |game| game.fetch("status") == "active" }

raise "Game codes must be unique" unless codes.uniq.length == games.length
raise "Game URLs must be unique" unless urls.uniq.length == games.length
raise "Expected #{expected_active_count} active games, found #{active_games.length}" unless active_games.length == expected_active_count
raise "Book associations are not allowed in this import" unless games.all? { |game| game["book_id"].nil? }
raise "All game URLs must use the production interactives origin" unless urls.all? { |url| url.start_with?("https://bytelift.ph/interactives/") }
raise "Every active game must use the session API" unless active_games.all? { |game| game.dig("settings", "api_integrated") == true }

urls.each do |url|
  parsed_url = URI.parse(url)
  raise "Invalid game URL: #{url}" unless parsed_url.is_a?(URI::HTTPS) && parsed_url.host == "bytelift.ph"
end

attributes_for = lambda do |game|
  {
    title: game.fetch("title"),
    description: game["description"],
    game_url: game.fetch("game_url"),
    status: game.fetch("status"),
    settings: game.fetch("settings"),
    book_id: nil
  }
end

summarize = lambda do
  summary = { created: 0, updated: 0, unchanged: 0 }

  games.each do |game|
    record = InteractiveGame.find_or_initialize_by(game_code: game.fetch("game_code"))
    is_new = record.new_record?
    record.assign_attributes(attributes_for.call(game))

    unless record.valid?
      raise "Invalid #{game.fetch("game_code")}: #{record.errors.full_messages.join(", ")}"
    end

    if is_new
      summary[:created] += 1
    elsif record.has_changes_to_save?
      summary[:updated] += 1
    else
      summary[:unchanged] += 1
    end
  end

  summary
end

planned_summary = summarize.call

unless apply_changes
  puts({ mode: "dry_run", **planned_summary }.to_json)
  exit 0
end

InteractiveGame.transaction do
  games.each do |game|
    record = InteractiveGame.find_or_initialize_by(game_code: game.fetch("game_code"))
    record.assign_attributes(attributes_for.call(game))
    record.save!
  end

  synced_records = InteractiveGame.where(game_code: codes)
  raise "Not all manifest games were persisted" unless synced_records.count == expected_count
  raise "Persisted games must not have book associations" unless synced_records.where.not(book_id: nil).none?

  persisted_active_codes = synced_records.active.order(:game_code).pluck(:game_code)
  expected_active_codes = active_games.map { |game| game.fetch("game_code") }.sort
  raise "Persisted active games do not match the manifest" unless persisted_active_codes == expected_active_codes
end

puts({ mode: "apply", **planned_summary }.to_json)
