# frozen_string_literal: true

require "pathname"
require "hanami/utils"
require "English"

module Hanami
  module Model
    class Migrator
      # SQLite3 Migrator
      #
      # @since 0.4.0
      # @api private
      class SQLiteAdapter < Adapter
        # No-op for in-memory databases
        #
        # @since 0.4.0
        # @api private
        module Memory
          # @since 0.4.0
          # @api private
          def create
          end

          # @since 0.4.0
          # @api private
          def drop
          end
        end

        # Initialize adapter
        #
        # @since 0.4.0
        # @api private
        def initialize(configuration)
          super
          extend Memory if memory?
        end

        # @since 0.4.0
        # @api private
        def create
          path.dirname.mkpath
          FileUtils.touch(path)
        rescue Errno::EACCES, Errno::EPERM
          raise MigrationError.new("Permission denied: #{path.sub(/\A\/\//, '')}")
        end

        # @since 0.4.0
        # @api private
        def drop
          path.delete
        rescue Errno::ENOENT
          raise MigrationError.new("Cannot find database: #{path.sub(/\A\/\//, '')}")
        end

        # @since 0.4.0
        # @api private
        def dump
          dump_structure
          dump_migrations_data
        end

        # @since 0.4.0
        # @api private
        def load
          load_structure
        end

        private

        # @since 0.4.0
        # @api private
        def path
          root.join(
            @connection.uri.sub(/\A(jdbc:sqlite:\/\/|sqlite:\/\/)/, "")
          )
        end

        # @since 0.4.0
        # @api private
        def root
          Hanami::Model.configuration.root
        end

        # @since 0.4.0
        # @api private
        def memory?
          uri = path.to_s
          uri.match(/sqlite\:\/\z/) ||
            uri.match(/\:memory\:/)
        end

        # @since 0.4.0
        # @api private
        def dump_structure
          execute "sqlite3 #{escape(path)} .schema > #{escape(schema)}"
        end

        # @since 0.4.0
        # @api private
        def load_structure
          execute "sqlite3 #{escape(path)} < #{escape(schema)}" if schema.exist?
        end

        # @since 0.4.0
        # @api private
        #
        def dump_migrations_data
          execute "sqlite3 #{escape(path)} .dump" do |stdout|
            begin
              contents = stdout.read.split($INPUT_RECORD_SEPARATOR)
              contents = contents.grep(/^INSERT INTO "?#{migrations_table}"?/)

              ::File.open(schema, ::File::CREAT | ::File::BINARY | ::File::WRONLY | ::File::APPEND) do |file|
                file.write(contents.join($INPUT_RECORD_SEPARATOR))
              end
            rescue => exception
              raise MigrationError.new(exception.message)
            end
          end
        end
      end
    end
  end
end
