require_relative './stats'
require_relative '../visualization/collection'
require_relative './support_tables'
require_relative '../map'
require_relative '../layer'

module CartoDB
  module Visualization
    class Relator
      LAYER_SCOPES = {
        base:             :user_layers,
        cartodb:          :carto_layers,
        data:             :data_layers,
        others:           :other_layers,
        named_map:        :named_maps_layers
      }.freeze

      INTERFACE = %w{ overlays user table related_templates related_tables related_canonical_visualizations
                      layers stats mapviews total_mapviews data_layers synchronization synced? permission
                      parent children support_tables prev_list_item next_list_item likes reload_likes
                      estimated_row_count actual_row_count }.freeze

      def initialize(map, attributes = {})
        @id             = attributes.fetch(:id)
        @user_id        = attributes.fetch(:user_id)
        @permission_id  = attributes.fetch(:permission_id)
        @parent_id      = attributes.fetch(:parent_id)
        @kind           = attributes.fetch(:kind)
        @support_tables = nil
        @likes          = nil
        @prev_id        = attributes.fetch(:prev_id)
        @next_id        = attributes.fetch(:next_id)
        @map            = map
      end

      # @return []
      def children
        ordered = []
        children_vis = Visualization::Collection.new.fetch(parent_id: @id)
        if children_vis.count > 0
          ordered << children_vis.select { |vis| vis[:prev_id].nil? }.first
          while !ordered.last[:next_id].nil?
            target = ordered.last[:next_id]
            unless target.nil?
              ordered << children_vis.select { |vis| vis[:id] == target }.first
            end
          end
        end
        ordered
      end

      # @return CartoDB::Visualization::Member
      def parent
        @parent ||= Visualization::Member.new(id: @parent_id).fetch unless @parent_id.nil?
      end

      # @return CartoDB::Visualization::Member
      def prev_list_item
        @prev_vis ||= Visualization::Member.new(id: @prev_id).fetch unless @prev_id.nil?
      end

      # @return CartoDB::Visualization::Member
      def next_list_item
         @next_vis ||= Visualization::Member.new(id: @next_id).fetch unless @next_id.nil?
      end

      def support_tables
        @support_tables ||= Visualization::SupportTables.new(
          user.in_database, parent_id: @id, parent_kind: @kind, public_user_roles: user.db_service.public_user_roles)
      end

      def overlays
        @overlays ||= Carto::Overlay.where(visualization_id: id).all
      end

      def user
        @user ||= ::User[@user_id] unless @user_id.nil?
      end

      def table
        return nil if map.nil?
        @table ||= ::UserTable.from_map_id(map.id).try(:service)
      end

      def estimated_row_count
        table.nil? ? nil : table.estimated_row_count
      end

      def actual_row_count
        table.nil? ? nil : table.actual_row_count
      end

      def related_templates
        Carto::Template.where(source_visualization_id: @id).all
      end

      def related_tables
        @related_tables ||= layers(:data).flat_map { |layer| layer.user_tables.map(&:service) }.uniq(&:id)
      end

      def related_canonical_visualizations
        @related_canonical_visualizations ||= get_related_canonical_visualizations
      end

      def layers(kind)
        return [] unless map
        map.send(LAYER_SCOPES.fetch(kind))
      end

      def synchronization
        CartoDB::Synchronization::Member.new(visualization_id: @id).fetch_by_visualization_id
      rescue KeyError
        {}
      end

      def synced?
        !synchronization.is_a?(Hash)
      end

      def stats(user=nil)
        @stats ||= Visualization::Stats.new(self, user).to_poro
      end

      def mapviews(user=nil)
        @mapviews ||= stats(user).collect { |o| o[1] }.reduce(:+)
      end

      def total_mapviews(user=nil)
        @total_mapviews ||= Visualization::Stats.new(self, user).total_mapviews
      end

      def data_layers
        layers(:data)
      end

      def permission
        @permission ||= Carto::Permission.find(@permission_id) if @permission_id
      end

      def likes
        @likes ||= likes_search.all.to_a
      end

      def reload_likes
        @likes = nil
        likes
      end

      attr_reader :id, :map

      private

      def likes_search
        Carto::Like.where(subject: @id)
      end

      def get_related_canonical_visualizations
        get_related_visualizations_by_types([CartoDB::Visualization::Member::TYPE_CANONICAL])
      end

      def get_related_visualizations_by_types(types)
        related_map_ids = related_tables.map(&:map_id)
        CartoDB::Visualization::Collection.new.fetch(map_id: related_map_ids, type: types)
      end

    end
  end
end
