# frozen_string_literal: true

require_dependency "renalware/ukrdc"
require "gpg_encrypt_folder"

module Renalware
  module UKRDC
    class SendPatients
      attr_reader :patient_ids, :changed_since, :logger, :request_uuid

      def initialize(changed_since: nil, patient_ids: nil, logger: nil)
        @changed_since = Time.zone.parse(changed_since) if changed_since.present?
        @patient_ids = Array(patient_ids)
        @logger = logger || Rails.logger
        @request_uuid = SecureRandom.uuid # helps group logs together
      end

      def call
        logger.info "Request #{request_uuid}"

        ms = Benchmark.ms do
          send_patients
        end
        print_summary(ms)
      end

      private

      # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
      def send_patients
        logger.info("Generating XML files for #{patient_ids&.any? ? patient_ids : 'all'} patients")
        query = Renalware::UKRDC::PatientsQuery.new.call(changed_since: changed_since)
        query = query.where(id: Array(patient_ids)) if patient_ids.present?

        if changed_since.present?
          logger.info("#{query.count} patients have changed since #{changed_since}")
        else
          logger.info("#{query.count} patients have changed since the last send")
        end

        folder_name = within_new_folder do |dir|
          xml_path = dir.join("xml")
          query.all.find_each do |patient|
            SendPatient.new(
              patient: patient,
              dir: xml_path,
              changes_since: changed_since,
              request_uuid: request_uuid,
              logger: logger
            ).call
          end
          encrypt_xml_files_in(dir)
        end
        logger.info("Files saved to #{folder_name}")
      end
      # rubocop:enable Metrics/AbcSize, Metrics/MethodLength

      def gpg_encrypt(filepath, output_filepath)
        path_to_key = File.open(Rails.root.join("key.gpg"))
        # only needed if the key has not been imported previously
        GPGME::Key.import(path_to_key)
        crypto = GPGME::Crypto.new(always_trust: true)
        File.open(filepath) do |in_file|
          File.open(output_filepath, "wb") do |out_file|
            crypto.encrypt in_file, output: out_file, recipients: "Patient View"
          end
        end
      end

      def timestamp
        Time.zone.now.strftime("%Y%m%d%H%M%S%L")
      end

      def within_new_folder
        dir = Pathname(File.join("/var", "ukrdc", timestamp))
        FileUtils.mkdir_p File.join(dir, "xml")
        FileUtils.mkdir_p File.join(dir, "encrypted")
        yield dir if block_given?
        dir
      end

      def filepath_for(patient, dir, sub_folder)
        raise(ArgumentError, "Patient has no ukrdc_external_id") if patient.ukrdc_external_id.blank?

        filename = "#{patient.ukrdc_external_id}.xml"
        File.join(dir, sub_folder.to_s, filename)
      end

      def print_summary(ms)
        logger.info "*** Summary ***"
        logger.info "Took #{ms.to_i / 1000} seconds"
        results = TransmissionLog.where(request_uuid: request_uuid).group(:status).count(:status)
        results.to_h.map{ |key, value| logger.info("#{key}: #{value}") }
      end

      def encrypt_xml_files_in(dir)
        logger.info "Encrypting..."
        GpgEncryptFolder.new(folder: dir, options: gpg_options).call
      end

      def config
        Renalware.config
      end

      def gpg_options
        GpgOptions.new(
          recipient: config.ukrdc_gpg_recipient,
          keyring: config.ukrdc_gpg_keyring,
          homedir: config.ukrdc_gpg_homedir,
          filename_transform: lambda do |filename|
            filename.to_s.gsub("/xml/", "/encrypted/") + ".enc"
          end
        )
      end
    end
  end
end