# encoding: utf-8
# frozen_string_literal: true

require 'chatrix/components/state'
require 'chatrix/components/timeline'
require 'chatrix/components/messaging'
require 'chatrix/components/permissions'
require 'chatrix/components/admin'

module Chatrix
  # Provides functionality for interacting with a room.
  class Room
    include Wisper::Publisher

    extend Forwardable

    # @return [String] The ID of this room.
    attr_reader :id

    # @return [State] The state object for this room.
    attr_reader :state

    # @return [Timeline] The timeline object for this room.
    attr_reader :timeline

    # @return [Admin] Administration object for carrying out administrative
    #   actions like kicking and banning of users.
    attr_reader :admin

    # @return [Messaging] Handle various message actions through this object.
    attr_reader :messaging

    def_delegators :state, :canonical_alias, :name, :topic, :creator,
                   :guest_access, :join_rule, :history_visibility, :permissions,
                   :member?

    def_delegators :admin, :join, :leave, :kick, :ban, :unban

    def_delegators :messaging, :send_message, :send_notice, :send_emote,
                   :send_html

    # Initializes a new Room instance.
    #
    # @param id [String] The room ID.
    # @param users [Users] The User manager.
    # @param matrix [Matrix] The Matrix API instance.
    def initialize(id, users, matrix)
      @id = id
      @users = users
      @matrix = matrix

      @state = Components::State.new self, @users
      @timeline = Components::Timeline.new self, @users
      @messaging = Components::Messaging.new self, @matrix
      @admin = Components::Admin.new self, @matrix

      @timeline.on(:message) { |r, m| broadcast(:message, r, m) }
    end

    # Process join events for this room.
    # @param data [Hash] Event data containing state and timeline events.
    def process_join(data)
      @state.update data['state'] if data.key? 'state'
      @timeline.update data['timeline'] if data.key? 'timeline'
    end

    # Process invite events for this room.
    # @param data [Hash] Event data containing special invite data.
    def process_invite(data)
      data['invite_state']['events'].each { |e| process_invite_event e }
    end

    # Process leave events for this room.
    # @param data [Hash] Event data containing state and timeline events up
    #   until the point of leaving the room.
    def process_leave(data)
      @state.update data['state'] if data.key? 'state'
      @timeline.update data['timeline'] if data.key? 'timeline'
    end

    # Gets a string representation of this room.
    # @return [String] If the room has a name, that name is returned.
    #   If it has a canonical alias, the alias is returned.
    #   If it has neither a name nor alias, the room ID is returned.
    def to_s
      name || canonical_alias || @id
    end

    private

    # Process an invite event for this room.
    # @param event [Hash] Event data.
    def process_invite_event(event)
      return unless event['type'] == 'm.room.member'
      return unless event['content']['membership'] == 'invite'
      @users.process_invite self, event
      sender = @users[event['sender']]
      invitee = @users[event['state_key']]
      # Return early if the user is already in the room
      return if @state.member? invitee
      broadcast(:invited, sender, invitee)
    end
  end
end
