#!/usr/bin/env ruby
# frozen_string_literal: true
#
# Copyright (C) 2019 Harald Sitter <sitter@kde.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of
# the License or any later version accepted by the membership of
# KDE e.V. (or its successor approved by the membership of KDE
# e.V.), which shall act as a proxy defined in Section 14 of
# version 3 of the license.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

require 'json'
require 'net/http'
require 'socket'

SOCKET_PATH = '/run/snapd.socket'

class APIResponse
  attr_reader :type
  attr_reader :status_code
  attr_reader :status
  attr_reader :result

  def initialize(json)
    @type = json.fetch('type')
    @status_code = json.fetch('status-code')
    @status = json.fetch('status')
    @result = json.fetch('result')
  end
end

class Connection
  def initialize
    @socket = Net::BufferedIO.new(UNIXSocket.new(SOCKET_PATH))
  end

  # @return returns the (not-parsed) json reply obj
  def get(path)
    request = Net::HTTP::Get.new(path)
    request['host'] = 'localhost'
    request.exec(@socket, '1.1', path)

    response = nil
    loop do
      response = Net::HTTPResponse.read_new(@socket)
      break unless response.is_a?(Net::HTTPContinue)
    end
    response.reading_body(@socket, request.response_body_permitted?) {}

    yield response if block_given?

    response
  end
end

unless File.exist?(SOCKET_PATH)
  warn 'snapd not running ➜ no clean-up of kde-frameworks-5 snap'
  exit 0
end

# Get a list of all slot interfaces of kf5 and record snaps connected to it.
# If none are connected that means the snap is unused and can be thrown out
# under the assumption that the reason it is installed is because it was seeded
# on the ISO.
kf5_used_by = []
Connection.new.get('/v2/interfaces') do |response|
  response = JSON.parse(response.body)
  response = APIResponse.new(response)

  if response.result.empty?
    puts 'snapd entirely empty; nothing to clean up.'
    exit 0
  end

  slots = response.result.fetch('slots')
  kf5_slots = slots.find_all { |x| x.fetch('snap') == 'kde-frameworks-5' }

  if kf5_slots.empty?
    puts 'kde-frameworks-5 snap not installed; nothing to clean up.'
    exit 0
  end

  kf5_slots.each do |slot|
    connections = slot&.fetch('connections', [])
    kf5_used_by += connections.collect { |x| x.fetch('snap') }
  end
end

if kf5_used_by.empty?
  puts 'kde-frameworks-5 snap used by nothing ➜ scheduling removal'
  system('snap remove --no-wait kde-frameworks-5')
else
  puts "kde-frameworks-5 snap used by #{kf5_used_by} ➜ leaving it installed"
end
