# frozen_string_literal: true

module Admin
  class Backup
    class << self
      def zip
        # create_records_xlsx
        Dir.glob(Rails.root.join("storage", "*.sqlite3")).each do |entry|
          `sqlite3 #{entry} .dump > #{entry.sub(".sqlite3", ".dump.sql")}`
          `sqlite3 #{entry} .schema > #{entry.sub(".sqlite3", ".schema.sql")}`
        end
        ZipFileGenerator.new(Rails.root.join("storage")).write
      end

      def db_xlsx
        xlsx = Axlsx::Package.new
        workbook = xlsx.workbook

        ActiveRecord::Base.connection.tables.each do |table|
          workbook.add_worksheet(name: table) do |sheet|
            klass = table.classify.safe_constantize
            if klass
              attributes = klass.attribute_names
              attributes += klass.rich_text_association_names.map(&:to_s) if klass.respond_to?(:rich_text_association_names)

              attributes = ["id"] + attributes.reject{ _1 == "id"}
                                            .sort { _1.sub("rich_text_", "") <=> _2.sub("rich_text_", "") }

              sheet << attributes.map { _1.sub("rich_text_", "") }
              klass.find_each do |record|
                sheet << attributes.map { _1.starts_with?("rich_text") ? record.send(_1.to_s.sub("rich_text_", "")).body&.to_html : record.send(_1) }
              end
            else
              # TODO: Add "raw table data"
            end
          end
        end

        path = file_path("data.xls")
        xlsx.serialize(path)
        path
      end

      private

      def file_path(name)
        Rails.root.join("storage", name)
      end
    end
  end

  # This is a simple example which uses rubyzip to
  # recursively generate a zip file from the contents of
  # a specified directory. The directory itself is not
  # included in the archive, rather just its contents.
  #
  # Usage:
  #   directory_to_zip = "/tmp/input"
  #   output_file = "/tmp/out.zip"
  #   zf = ZipFileGenerator.new(directory_to_zip, output_file)
  #   zf.write()
  class ZipFileGenerator
    # Initialize with the directory to zip and the location of the output archive.
    def initialize(input_dir)
      @input_dir = input_dir
    end

    # Zip the input directory.
    def write
      entries = Dir.entries(@input_dir) - %w[. ..]
      path = Rails.root.join("tmp", Time.now.to_i.to_s)
      ::Zip::File.open(path, create: true) do |zipfile|
        write_entries entries, "", zipfile
      end
      path
    end

    private

    # A helper method to make the recursion work.
    def write_entries(entries, path, zipfile)
      entries.each do |e|
        zipfile_path = path == "" ? e : File.join(path, e)
        disk_file_path = File.join(@input_dir, zipfile_path)

        if File.directory? disk_file_path
          recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
        else
          put_into_archive(disk_file_path, zipfile, zipfile_path)
        end
      end
    end

    def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
      zipfile.mkdir zipfile_path
      subdir = Dir.entries(disk_file_path) - %w[. ..]
      write_entries subdir, zipfile_path, zipfile
    end

    def put_into_archive(disk_file_path, zipfile, zipfile_path)
      zipfile.add(zipfile_path, disk_file_path)
    end
  end
end
