require 'date'
require 'yajl'
require_relative 'base_view_model'

module AdminUI
  class ServicesViewModel < AdminUI::BaseViewModel
    def do_items
      services = @cc.services

      # services have to exist. Other record types are optional
      return result unless services['connected']

      events                       = @cc.events
      route_bindings               = @cc.route_bindings
      service_bindings             = @cc.service_bindings
      service_brokers              = @cc.service_brokers
      service_instances            = @cc.service_instances
      service_instance_shares      = @cc.service_instance_shares
      service_keys                 = @cc.service_keys
      service_offering_annotations = @cc.service_offering_annotations
      service_offering_labels      = @cc.service_offering_labels
      service_plans                = @cc.service_plans
      service_plan_visibilities    = @cc.service_plan_visibilities

      events_connected                    = events['connected']
      route_bindings_connected            = route_bindings['connected']
      service_bindings_connected          = service_bindings['connected']
      service_instances_connected         = service_instances['connected']
      service_instance_shares_connected   = service_instance_shares['connected']
      service_keys_connected              = service_keys['connected']
      service_plans_connected             = service_plans['connected']
      service_plan_visibilities_connected = service_plan_visibilities['connected']

      service_broker_hash        = service_brokers['items'].to_h { |item| [item[:id], item] }
      service_instance_guid_hash = service_instances['items'].to_h { |item| [item[:guid], item] }
      service_instance_id_hash   = service_instances['items'].to_h { |item| [item[:id], item] }
      service_plan_hash          = service_plans['items'].to_h { |item| [item[:id], item] }

      service_offering_annotations_hash = {}
      service_offering_annotations['items'].each do |service_offering_annotation|
        return result unless @running

        Thread.pass

        service_offering_guid = service_offering_annotation[:resource_guid]
        service_offering_annotations_array = service_offering_annotations_hash[service_offering_guid]
        if service_offering_annotations_array.nil?
          service_offering_annotations_array = []
          service_offering_annotations_hash[service_offering_guid] = service_offering_annotations_array
        end

        # Need rfc3339 dates
        wrapper =
          {
            annotation:         service_offering_annotation,
            created_at_rfc3339: service_offering_annotation[:created_at].to_datetime.rfc3339,
            updated_at_rfc3339: service_offering_annotation[:updated_at].nil? ? nil : service_offering_annotation[:updated_at].to_datetime.rfc3339
          }

        service_offering_annotations_array.push(wrapper)
      end

      service_offering_labels_hash = {}
      service_offering_labels['items'].each do |service_offering_label|
        return result unless @running

        Thread.pass

        service_offering_guid = service_offering_label[:resource_guid]
        service_offering_labels_array = service_offering_labels_hash[service_offering_guid]
        if service_offering_labels_array.nil?
          service_offering_labels_array = []
          service_offering_labels_hash[service_offering_guid] = service_offering_labels_array
        end

        # Need rfc3339 dates
        wrapper =
          {
            label:              service_offering_label,
            created_at_rfc3339: service_offering_label[:created_at].to_datetime.rfc3339,
            updated_at_rfc3339: service_offering_label[:updated_at].nil? ? nil : service_offering_label[:updated_at].to_datetime.rfc3339
          }

        service_offering_labels_array.push(wrapper)
      end

      event_counters = {}
      events['items'].each do |event|
        return result unless @running

        Thread.pass

        next unless event[:actee_type] == 'service'

        actee = event[:actee]
        event_counters[actee] = 0 if event_counters[actee].nil?
        event_counters[actee] += 1
      end

      service_plan_counters = {}
      service_plan_public_active_counters = {}
      service_plans['items'].each do |service_plan|
        return result unless @running

        Thread.pass

        service_id = service_plan[:service_id]
        service_plan_counters[service_id] = 0 if service_plan_counters[service_id].nil?
        service_plan_counters[service_id] += 1

        next unless service_plan[:public] && service_plan[:active]

        service_plan_public_active_counters[service_id] = 0 if service_plan_public_active_counters[service_id].nil?
        service_plan_public_active_counters[service_id] += 1
      end

      service_plan_visibility_counters = {}
      service_plan_visibilities['items'].each do |service_plan_visibility|
        return result unless @running

        Thread.pass

        service_plan_id = service_plan_visibility[:service_plan_id]
        next if service_plan_id.nil?

        service_plan = service_plan_hash[service_plan_id]
        next if service_plan.nil?

        service_id = service_plan[:service_id]
        service_plan_visibility_counters[service_id] = 0 if service_plan_visibility_counters[service_id].nil?
        service_plan_visibility_counters[service_id] += 1
      end

      service_instance_counters = {}
      service_instances['items'].each do |service_instance|
        return result unless @running

        Thread.pass

        service_plan_id = service_instance[:service_plan_id]
        next if service_plan_id.nil?

        service_plan = service_plan_hash[service_plan_id]
        next if service_plan.nil?

        service_id = service_plan[:service_id]
        service_instance_counters[service_id] = 0 if service_instance_counters[service_id].nil?
        service_instance_counters[service_id] += 1
      end

      service_instance_share_counters = {}
      service_instance_shares['items'].each do |service_instance_share|
        return result unless @running

        Thread.pass

        service_instance_guid = service_instance_share[:service_instance_guid]
        next if service_instance_guid.nil?

        service_instance = service_instance_guid_hash[service_instance_guid]
        next if service_instance.nil?

        service_plan_id = service_instance[:service_plan_id]
        next if service_plan_id.nil?

        service_plan = service_plan_hash[service_plan_id]
        next if service_plan.nil?

        service_id = service_plan[:service_id]
        service_instance_share_counters[service_id] = 0 if service_instance_share_counters[service_id].nil?
        service_instance_share_counters[service_id] += 1
      end

      service_binding_counters = {}
      service_bindings['items'].each do |service_binding|
        return result unless @running

        Thread.pass

        service_instance_guid = service_binding[:service_instance_guid]
        next if service_instance_guid.nil?

        service_instance = service_instance_guid_hash[service_instance_guid]
        next if service_instance.nil?

        service_plan_id = service_instance[:service_plan_id]
        next if service_plan_id.nil?

        service_plan = service_plan_hash[service_plan_id]
        next if service_plan.nil?

        service_id = service_plan[:service_id]
        service_binding_counters[service_id] = 0 if service_binding_counters[service_id].nil?
        service_binding_counters[service_id] += 1
      end

      service_key_counters = {}
      service_keys['items'].each do |service_key|
        return result unless @running

        Thread.pass

        service_instance_id = service_key[:service_instance_id]
        next if service_instance_id.nil?

        service_instance = service_instance_id_hash[service_instance_id]
        next if service_instance.nil?

        service_plan_id = service_instance[:service_plan_id]
        next if service_plan_id.nil?

        service_plan = service_plan_hash[service_plan_id]
        next if service_plan.nil?

        service_id = service_plan[:service_id]
        service_key_counters[service_id] = 0 if service_key_counters[service_id].nil?
        service_key_counters[service_id] += 1
      end

      route_binding_counters = {}
      route_bindings['items'].each do |route_binding|
        return result unless @running

        Thread.pass

        service_instance_id = route_binding[:service_instance_id]
        next if service_instance_id.nil?

        service_instance = service_instance_id_hash[service_instance_id]
        next if service_instance.nil?

        service_plan_id = service_instance[:service_plan_id]
        next if service_plan_id.nil?

        service_plan = service_plan_hash[service_plan_id]
        next if service_plan.nil?

        service_id = service_plan[:service_id]
        route_binding_counters[service_id] = 0 if route_binding_counters[service_id].nil?
        route_binding_counters[service_id] += 1
      end

      items = []
      hash  = {}

      services['items'].each do |service|
        return result unless @running

        Thread.pass

        guid           = service[:guid]
        id             = service[:id]
        service_broker = service_broker_hash[service[:service_broker_id]]

        event_counter                      = event_counters[guid]
        route_binding_counter              = route_binding_counters[id]
        service_binding_counter            = service_binding_counters[id]
        service_instance_counter           = service_instance_counters[id]
        service_instance_share_counter     = service_instance_share_counters[id]
        service_key_counter                = service_key_counters[id]
        service_offering_annotation_array  = service_offering_annotations_hash[guid] || []
        service_offering_label_array       = service_offering_labels_hash[guid] || []
        service_plan_counter               = service_plan_counters[id]
        service_plan_public_active_counter = service_plan_public_active_counters[id]
        service_plan_visibility_counter    = service_plan_visibility_counters[id]

        row = []

        row.push(guid)
        row.push(service[:label])
        row.push(guid)
        row.push(service[:unique_id])
        row.push(service[:created_at].to_datetime.rfc3339)

        if service[:updated_at]
          row.push(service[:updated_at].to_datetime.rfc3339)
        else
          row.push(nil)
        end

        row.push(service[:bindable])
        row.push(service[:plan_updateable])
        row.push(service[:instances_retrievable])
        row.push(service[:bindings_retrievable])
        row.push(service[:allow_context_updates])

        shareable             = nil
        provider_display_name = nil
        display_name          = nil
        if service[:extra]
          begin
            json = Yajl::Parser.parse(service[:extra])

            shareable             = json['shareable']
            provider_display_name = json['providerDisplayName']
            display_name          = json['displayName']

            shareable = false if shareable.nil?
          rescue
            shareable             = nil
            provider_display_name = nil
            display_name          = nil
          end
        end

        row.push(shareable)
        row.push(service[:active])
        row.push(provider_display_name)
        row.push(display_name)

        requires = nil
        if service[:requires]
          begin
            requires = Yajl::Parser.parse(service[:requires]).sort
          rescue
            requires = nil
          end
        end

        if requires && requires.length.positive?
          row.push(requires)
        else
          row.push(nil)
        end

        if event_counter
          row.push(event_counter)
        elsif events_connected
          row.push(0)
        else
          row.push(nil)
        end

        if service_plan_counter
          row.push(service_plan_counter)
        elsif service_plans_connected
          row.push(0)
        else
          row.push(nil)
        end

        if service_plan_public_active_counter
          row.push(service_plan_public_active_counter)
        elsif service_plans_connected
          row.push(0)
        else
          row.push(nil)
        end

        if service_plan_visibility_counter
          row.push(service_plan_visibility_counter)
        elsif service_plan_visibilities_connected && service_plans_connected
          row.push(0)
        else
          row.push(nil)
        end

        if service_instance_counter
          row.push(service_instance_counter)
        elsif service_instances_connected && service_plans_connected
          row.push(0)
        else
          row.push(nil)
        end

        if service_instance_share_counter
          row.push(service_instance_share_counter)
        elsif service_instance_shares_connected && service_instances_connected && service_plans_connected
          row.push(0)
        else
          row.push(nil)
        end

        if service_binding_counter
          row.push(service_binding_counter)
        elsif service_bindings_connected && service_instances_connected && service_plans_connected
          row.push(0)
        else
          row.push(nil)
        end

        if service_key_counter
          row.push(service_key_counter)
        elsif service_keys_connected && service_instances_connected && service_plans_connected
          row.push(0)
        else
          row.push(nil)
        end

        if route_binding_counter
          row.push(route_binding_counter)
        elsif route_bindings_connected && service_instances_connected && service_plans_connected
          row.push(0)
        else
          row.push(nil)
        end

        if service_broker
          row.push(service_broker[:name])
          row.push(service_broker[:guid])
          row.push(service_broker[:created_at].to_datetime.rfc3339)

          if service_broker[:updated_at]
            row.push(service_broker[:updated_at].to_datetime.rfc3339)
          else
            row.push(nil)
          end
        else
          row.push(nil, nil, nil, nil)
        end

        items.push(row)

        hash[guid] =
          {
            'annotations'    => service_offering_annotation_array,
            'labels'         => service_offering_label_array,
            'service'        => service,
            'service_broker' => service_broker
          }
      end

      result(true, items, hash, (1..28).to_a, (1..28).to_a - [16, 17, 18, 19, 20, 21, 22, 23, 24])
    end
  end
end
