class Account
  # The nastiness that allows us to treat the Account as a User
  include Curate::DeviseUserShim
  self.wrapped_class = User

  def inspect
    "#<#{self.class} user.id: #{user.id}, user.repository_id: #{user.repository_id}, attributes: #{attributes.inspect}>"
  end

  def is_a?(comparison)
    super || user.is_a?(comparison)
  end

  include ActiveAttr::Model

  def initialize(user, attributes = user.attributes)
    @user = user
    self.attributes = attributes
  end

  attr_reader :user

  delegate :person, to: :user

  def profile
    @profile ||= person.profile || person.build_profile(title: profile_title)
  end

  class_attribute :person_attribute_names
  self.person_attribute_names = []
  class_attribute :user_attribute_names
  self.user_attribute_names = User.attribute_names_for_account

  def self.apply_person_attributes
    Person.editable_attributes.each do |att|
      self.person_attribute_names += [att.name.to_s]
      attribute(att.name)
      att.with_validation_options do |name, opts|
        validates(name, opts)
      end
    end
  end

  def self.apply_user_attributes
    user_attribute_names.each do |attribute_name|
      attribute(attribute_name)
    end
  end

  apply_person_attributes
  apply_user_attributes

  def create
    if create_user &&
        create_person &&
        create_profile
      true
    else
      collect_errors
    end
  end
  # Create is indicative of what is happening, however Devise calls #save
  alias save create

  def collect_errors
    user.errors.each do |key, value|
      errors.add(key, value)
    end
    person.errors.each do |key, value|
      errors.add(key, value)
    end
    profile.errors.each do |key, value|
      errors.add(key, value)
    end
  end
  protected :collect_errors

  def update_with_password(initial_params, *options)
    params = normalize_update_params(initial_params)
    extract_user_and_person_attributes_for_update(params)
    if update_user(*options) &&
        update_person &&
        update_profile
      true
    else
      collect_errors
      false
    end
  end

  delegate :persisted?, :to_param, :to_key, :new_record?, to: :user

  def method_missing(method_name, *args, &block)
    begin
      super
    rescue NoMethodError
      user.send(method_name, *args, &block)
    end
  end

  def respond_to_missing?(*args)
    super || user.respond_to?(*args)
  end

  private

  def normalize_update_params(params)
    options = params.dup.with_indifferent_access
    ['password', 'password_confirmation'].each do |key|
      if options.has_key?(key) && options[key].empty?
        options.delete(key)
      end
    end
    options
  end

  def create_profile
    apply_deposit_authorization(profile)
    profile.save && connect_person_to_profile
  end

  def update_profile
    create_profile unless profile.persisted?
    sync_profile_title_to_name(person_attributes)
  end

  def create_user
    user.attributes = user_attributes
    user.save
  end

  def update_user(*options)
    user.update_with_password(user_attributes, *options)
  end

  def create_person
    person.attributes = person_attributes
    apply_deposit_authorization(person)
    person.save && connect_user_to_person
  end

  def update_person
    if person.persisted?
      person.update(person_attributes)
    else
      create_person
    end
  end

  def apply_deposit_authorization(target)
    target.apply_depositor_metadata(user.user_key)
    target.read_groups = [Hydra::AccessControls::AccessRight::PERMISSION_TEXT_VALUE_PUBLIC]
    target
  end

  def connect_user_to_person
    user.update_column(:repository_id, person.pid)
  end

  def connect_person_to_profile
    person.profile = profile
    person.save
  end

  def user_attributes
    unless @user_attributes
      extract_user_and_person_attributes_for_update
    end
    @user_attributes
  end

  def person_attributes
    unless @person_attributes
      extract_user_and_person_attributes_for_update
    end
    @person_attributes
  end

  def extract_user_and_person_attributes_for_update(from_attributes = attributes)
    person.add_profile_image(from_attributes["files"]) if from_attributes.has_key?("files")
    @user_attributes = {}.with_indifferent_access
    @person_attributes = {}.with_indifferent_access
    from_attributes.each_pair do |key, value|
      if !value.nil?
        if person_attribute_names.include?(key.to_s)
          @person_attributes[key] = value
        end
        if user_attribute_names.include?(key.to_s)
          @user_attributes[key] = value
        end
      end
    end
  end

  def profile_title
    person.name.nil? ? 'Profile' : person.name
  end

  def sync_profile_title_to_name(update_attributes)
    return true unless update_attributes[:name]
    profile.update(title: profile_title)
  end

end