# -*- encoding : utf-8 -*-

class Card
  # Model for the card_virtuals table.
  # It provides method to get and store content for virtual cards from
  # the card_virtuals table.
  class Virtual < ApplicationRecord
    def update new_content
      update! content: new_content
      new_content
    end

    class << self
      def create card, virtual_content=nil
        validate_card card
        virtual_content ||= block_given? ? yield : card.generate_virtual_content
        create! left_id: left_id(card),
                right_id: right_id(card),
                left_key: card.name.left_key,
                content: virtual_content
      end

      def create_or_update card, virtual_content
        if (virtual_card = find_by_card(card))
          virtual_card.update virtual_content
        else
          create card, virtual_content
        end
      end

      def fetch_content card, &block
        find_content_by_card(card) || create(card, &block).content
      end

      def fetch card, &block
        find_by_card(card) || create(card, &block)
      end

      def refresh card
        virtual = find_by_card(card)
        return create(card) unless virtual

        virtual.update card.generate_virtual_content
      end

      def find_content_by_card card
        where_card(card)&.pluck(:content)&.first
      end

      def find_by_card card
        where_card(card).take
      end

      private

      def where_card card
        query = { right_id: right_id(card) }
        if (lid = left_id(card))
          query[:left_id] = lid
        else
          query[:left_key] = card.name.left_key
        end
        where query
      end

      def left_id card
        if card.compound?
          card.left_id&.positive? ? card.left_id : card.left&.id
        else
          card.id
        end
      end

      def right_id card
        if card.compound?
          card.right_id&.positive? ? card.right_id : card.right&.id
        else
          -2
        end
      end

      def validate_card card
        reason ||=
          if card.compound?
            "needs left_id" unless left_id(card)
            "needs right_id" unless right_id(card)
          elsif !card.id
            "needs id"
          end
        return unless reason

        raise Card::Error, card.name, "count not cacheable: card #{card.name} #{reason}"
      end
    end
  end
end