# frozen_string_literal: true

module CMSScanner
  module Cache
    # Cache Implementation using files
    class FileStore
      attr_reader :storage_path, :serializer

      # The serializer must have the 2 methods #load and #dump
      #   (Marshal and YAML have them)
      # YAML is Human Readable, contrary to Marshal which store in a binary format
      # Marshal does not need any "require"
      #
      # @param [ String ] storage_path
      # @param [ Constant ] serializer
      def initialize(storage_path, serializer = Marshal)
        @storage_path = File.expand_path(storage_path)
        @serializer   = serializer

        FileUtils.mkdir_p(@storage_path) unless Dir.exist?(@storage_path)
      end

      # TODO: rename this to clear ?
      def clean
        Dir[File.join(storage_path, '*')].each do |f|
          File.delete(f) unless File.symlink?(f)
        end
      end

      # @param [ String ] key
      #
      # @return [ Mixed ]
      def read_entry(key)
        return if expired_entry?(key)

        serializer.load(File.read(entry_path(key)))
      rescue StandardError
        nil
      end

      # @param [ String ] key
      # @param [ Mixed ] data_to_store
      # @param [ Integer ] cache_ttl
      def write_entry(key, data_to_store, cache_ttl)
        return unless cache_ttl.to_i.positive?

        File.write(entry_path(key), serializer.dump(data_to_store))
        File.write(entry_expiration_path(key), Time.now.to_i + cache_ttl)
      end

      # @param [ String ] key
      #
      # @return [ String ] The file path associated to the key
      def entry_path(key)
        File.join(storage_path, key)
      end

      # @param [ String ] key
      #
      # @return [ String ] The expiration file path associated to the key
      def entry_expiration_path(key)
        entry_path(key) + '.expiration'
      end

      private

      # @param [ String ] key
      #
      # @return [ Boolean ]
      def expired_entry?(key)
        File.read(entry_expiration_path(key)).to_i <= Time.now.to_i
      rescue StandardError
        true
      end
    end
  end
end
