require_relative 'json_printer'

module ForestLiana
  class SchemaFileUpdater
    include JsonPrinter

    # TODO: Remove nameOld attribute once the lianas versions older than 2.0.0 are minority.
    KEYS_COLLECTION = [
      'name',
      'name_old',
      'icon',
      'integration',
      'is_read_only',
      'is_searchable',
      'is_virtual',
      'only_for_relationships',
      'pagination_type',
      'fields',
      'segments',
      'actions',
    ]
    KEYS_COLLECTION_FIELD = [
      'field',
      'type',
      'default_value',
      'enums',
      'integration',
      'is_filterable',
      'is_read_only',
      'is_required',
      'is_sortable',
      'is_virtual',
      'reference',
      'inverse_of',
      'relationship',
      'widget',
      'validations',
      'polymorphic_referenced_models',
    ]
    KEYS_VALIDATION = [
      'message',
      'type',
      'value',
    ]
    KEYS_ACTION = [
      'name',
      'type',
      'base_url',
      'endpoint',
      'http_method',
      'redirect',
      'download',
      'fields',
      'hooks',
      'description',
      'submit_button_label',
    ]
    KEYS_ACTION_FIELD = [
      'field',
      'type',
      'default_value',
      'enums',
      'is_required',
      'is_read_only',
      'reference',
      'description',
      'position',
      'widget',
      'hook',
    ]
    KEYS_SEGMENT = ['name']

    def initialize filename, collections, meta
      @filename = filename
      @meta = meta

      # NOTICE: Remove unecessary keys
      @collections = collections.map do |collection|
        collection['fields'] = collection['fields'].map do |field|
          unless field['validations'].nil?
            field['validations'] = field['validations'].map { |validation| validation.slice(*KEYS_VALIDATION) }
          end

          field['type'] = 'String' unless field.has_key?('type')
          field['default_value'] = nil unless field.has_key?('default_value')
          field['enums'] = nil unless field.has_key?('enums')
          field['integration'] = nil unless field.has_key?('integration')
          field['is_filterable'] = false unless field.has_key?('is_filterable')
          field['is_read_only'] = true unless field.has_key?('is_read_only')
          field['is_required'] = false unless field.has_key?('is_required')
          field['is_sortable'] = false unless field.has_key?('is_sortable')
          field['is_virtual'] = false unless field.has_key?('is_virtual')
          field['reference'] = nil unless field.has_key?('reference')
          field['inverse_of'] = nil unless field.has_key?('inverse_of')
          field['relationships'] = nil unless field.has_key?('relationships')
          field['widget'] = nil unless field.has_key?('widget')
          field['validations'] = [] unless field.has_key?('validations')

          field.slice(*KEYS_COLLECTION_FIELD)
        end

        collection['actions'] = collection['actions'].map do |action|
          begin
            SmartActionFieldValidator.validate_smart_action_fields(action['fields'], action['name'], action['hooks']['change'])
          rescue ForestLiana::Errors::SmartActionInvalidFieldError => invalid_field_error
            FOREST_LOGGER.warn invalid_field_error.message
          rescue ForestLiana::Errors::SmartActionInvalidFieldHookError => invalid_hook_error
            FOREST_REPORTER.report invalid_hook_error
            FOREST_LOGGER.error invalid_hook_error.message
          end
          action['fields'] = action['fields'].map { |field| field.slice(*KEYS_ACTION_FIELD) }
          action.slice(*KEYS_ACTION)
        end

        collection['segments'] = collection['segments'].map do |segment|
          segment.slice(*KEYS_SEGMENT)
        end

        collection.slice(*KEYS_COLLECTION)
      end

      # NOTICE: Sort keys
      @collections = @collections.map do |collection|
        collection['fields'].sort! do |field1, field2|
          [field1['field'], field1['type'].inspect] <=> [field2['field'], field2['type'].inspect]
        end

        collection['fields'] = collection['fields'].map do |field|
          unless field['validations'].nil?
            field['validations'] = field['validations'].map do |validation|
              validation.sort_by { |key, value| KEYS_VALIDATION.index key }.to_h
            end
          end
          field.sort_by { |key, value| KEYS_COLLECTION_FIELD.index key }.to_h
        end
        collection['actions'] = collection['actions'].map do |action|
          action.sort_by { |key, value| KEYS_ACTION.index key }.to_h
        end
        collection.sort_by { |key, value| KEYS_COLLECTION.index key }.to_h
      end

      @collections.sort! { |collection1, collection2| collection1['name'] <=> collection2['name'] }
    end

    def perform
      File.open(@filename, 'w') do |file|
        # NOTICE: Escape '\' characters to ensure the generation of valid JSON files. It fixes
        #         potential issues if some fields have validations using complex regexp.
        file.puts pretty_print({
          collections: @collections,
          meta: @meta
        }).gsub(/(?<!\\)(\\)(?!["\\])/, '\\\\\\1')
      end
    end
  end
end
