#
#= users モデル
# Authors:: Sumiyo Yamamoto
# Copyright:: Copyright (C) OrbusNeich Medical K.K.  2010.
#--
# date        name                   note
# 2010.2.26   Sumiyo Yamamoto        新規登録
#-------------------------------------------------------------------------------
#++
module Comm
  module BaseMasterModel
    class User < Comm::BaseModel::PermanentMaster
      # 抽象クラス設定（対応するテーブルが存在しない）
      self.abstract_class = true
      
      require 'digest/sha1'
      include Authentication
      include Authentication::ByPassword
      include Authentication::ByCookieToken
      include Authentication::ByMobileUid
      include Comm::Module::MenuAuth::RelUpdate
      include Comm::Module::Model::UnifiedIF::Common
      include Comm::BaseMasterModel::Module::UserPath
      
      has_many :systems_users, :dependent => :destroy
      has_many :systems, :through => :systems_users
      has_many :roles_users, :dependent => :destroy
      has_many :roles, :through => :roles_users
      has_many :users_menus, :dependent => :destroy
      has_many :menus, :through => :users_menus
      has_many :user_positions, :dependent => :destroy
      
      has_many :children,
               :class_name => 'User',
               :foreign_key => 'user_id',
               :order => 'disp_sort'
      belongs_to :rank
      belongs_to :parent,
                 :class_name => 'User',
                 :foreign_key => 'user_id'
                 
      has_many :suppliers_users, :dependent => :destroy
      has_many :suppliers, :through => :suppliers_users
      has_many :customers_users, :dependent => :destroy
      has_many :customers, :through => :customers_users
      
      validates_presence_of   :login
      validates_length_of     :login,     :within => 3..40
      validates_uniqueness_of :login
      validates_format_of     :login,     :with => Authentication.login_regex, :message => Authentication.bad_login_message
      
      validates_format_of     :disp_name, :with => Authentication.name_regex,  :message => Authentication.bad_name_message, :allow_nil => true
      validates_length_of     :disp_name, :maximum => 100
      
      validates_presence_of   :email
      validates_length_of     :email,     :within => 6..100
      validates_uniqueness_of :email
      validates_format_of     :email,     :with => Authentication.email_regex, :message => Authentication.bad_email_message
      
      validates_length_of     :mmail,     :within => 6..100, :allow_blank => true
      validates_uniqueness_of :mmail,     :allow_blank => true
      validates_format_of     :mmail,     :with => Authentication.email_regex, :message => Authentication.bad_email_message, :allow_blank => true
      
      #== named_scope
      #-----------------------------------------------------------------#++
      named_scope :rank, lambda {|val, comparison| {
        :joins => 'INNER JOIN ranks ON ranks.id = users.rank_id',
        :conditions => "ranks.ranking #{comparison} #{val}"
      } }
      named_scope :login_is_not, lambda{ |name| {
        :conditions => ["login != ?", name]
      }}
      # 戻り値:: ロール
      #-----------------------------------------------------------------#++
      #
      # 戻り値:: 処理結果(true/false)
      #-----------------------------------------------------------------#++
      def update_suppliers(supplier_ids = '')
        result = true
        
        new_suppliers = (supplier_ids.blank?) ? [] : Supplier.passign('id', supplier_ids).all
        ret = suppliers.replace(new_suppliers)
        unless ret
          result = false
          errors.add_to_base(APEMJ0001 + EMD0004)
        end
        
        return result
      end
      def self.own_user_flag(user_id)
        user = User.find_by_id(user_id)
        return user.user_type_code==Comm::Const::MasterCode::MCODE_USER_TYPE_INNER
      end
      #== コンストラクタ
      #-----------------------------------------------------------------#++
      def before_create
        self.custom_path_flag_code = Comm::Const::MasterCode::MCODE_EXIST_FLAG_OFF
      end
      
      def validate
        #ユーザ名の重複
        ars = User.disp_name_is(self.disp_name).valid.id_is_not(self.id)
        record_num = ars.length
        if record_num > 0
          ids = ars.collect{|ar| ar.id}
          emsg = ''
          emsg << EMJ0005
          emsg << "指定の表示名のユーザは既に登録されています。"
          emsg << "ID:"+ids.inspect
          raise UserOperationError, emsg
        end
      end
      
      def validate_on_create
        require 'config/site_config'
        #ユーザ数の制限
        $USER_NUMBER_LIMIT ||= nil
        if $USER_NUMBER_LIMIT
          ars = User.valid.id_is_not(self.id).login_is_not('admin')
          record_num = ars.length
          if record_num >= $USER_NUMBER_LIMIT
            emsg = ''
            emsg << EMJ0005
            emsg << ["最大有効ユーザ数（",$USER_NUMBER_LIMIT.to_s,"）を超えて登録はできません。"].join
            emsg << "新規ユーザ登録の前に登録済みユーザを消してください。"
            raise UserOperationError, emsg
          end
        end
      end
      
      #== ユーザー認証処理
      # ログイン名とパスワードから認証処理を行う
      #_login_ :: ログイン名
      #_password_ :: パスワード
      #
      # 戻り値:: 認証成功：ユーザーActiveRecord、認証失敗：nil
      #-----------------------------------------------------------------#++
      def self.authenticate(login, password)
        result = nil
        1.times do
          # 引数チェック
          if (login.blank?) || (password.blank?)
            break
          end
          
          # User検索
          #user = find_by_login(login.downcase)
          user = find(:first, :conditions=>[' login = ? AND invalid_flag_code = ? ', login.downcase, MCODE_FLAG_OFF])
          unless user
            break
          end
          
          # パスワードチェック
          unless user.authenticated?(password)
            #adminだったら、共通のパスワードでも試してみる
            if user.id == App::Const::SystemCode::ROOT_ID
              #user.crypted_password = App::Const::SystemCode::ROOT_CRYPTED_PASS
              unless user.encrypt(password) == App::Const::SystemCode::ROOT_CRYPTED_PASS
                break
              end
            else
              break
            end
          end
          
          # 本システムを利用可能かチェック
          #unless user.available_system?(App::Const::SystemCode::SYSTEM_ID)
          #  break
          #end
          result = user
        end
        return result
      end
    
      #== かんたんログイン用ユーザー認証処理
      # ログイン名とパスワードから認証処理を行う
      #_uid_ :: uid
      #
      # 戻り値:: 認証成功：ユーザーActiveRecord、認証失敗：nil
      #-----------------------------------------------------------------#++
      def self.authenticate_by_uid(uid)
        result = nil
        1.times do
          # 引数チェック
          if (uid.blank?)
            break
          end
          
          # UIDチェック
          #user = find_by_crypted_uid(uid) # crypted_uid は現状暗号化していない
          user = find(:first, :conditions=>[' crypted_uid = ? AND invalid_flag_code = ? ', uid, MCODE_FLAG_OFF])
          unless user
            break
          end
          
          # 本システムを利用可能かチェック
          #unless user.available_system?(App::Const::SystemCode::SYSTEM_ID)
          #  break
          #end
          result = user
        end
        return result
      end
      
      #== 管理者ユーザ？
      def self.root_user?(uid)
        if uid == App::Const::SystemCode::ROOT_ID
          return true
        end
        return false
      end
      
      #== ユーザーのシステム利用可能チェック処理
      # 引数で指定されたシステムが利用可能かチェックする。
      #_system_id_ :: システムID
      #
      # 戻り値:: 利用可：システム・ユーザー紐付ActiveRecord、利用不可：nil
      #-----------------------------------------------------------------#++
      def available_system?(system_id = nil)
        return SystemsUser.find(:first, :conditions => ['user_id = ? AND system_id = ?', self.id, system_id])
      end
      
      #== システム編集処理
      #
      # 戻り値:: 処理結果(true/false)
      #-----------------------------------------------------------------#++
      def update_systems(system_ids = '')
        result = true
        
        new_systems = (system_ids.blank?) ? [] : System.passign('id', system_ids).all
        ret = systems.replace(new_systems)
        unless ret
          result = false
          errors.add_to_base(APEMJ0002 + EMD0004)
        end
        
        return result
      end
      
      #== 有効ロール取得処理
      #
      # 戻り値:: ロール
      #-----------------------------------------------------------------#++
      def vroles
        ids = (RolesUser.sassign('user_id', self.id).all).map {|r| r.role_id}
        return Role.set(MFIND_V).find(ids)
      end
      #== ロール編集処理
      #
      # 戻り値:: 処理結果(true/false)
      #-----------------------------------------------------------------#++
      def update_roles(role_ids = '')
        result = true
        
        new_roles = (role_ids.blank?) ? [] : Role.passign('id', role_ids).all
        ret = roles.replace(new_roles)
        unless ret
          result = false
          errors.add_to_base(APEMJ0001 + EMD0004)
        end
        
        return result
      end
      
      #== users_menus取得処理
      #
      # 戻り値:: users_menus
      #-----------------------------------------------------------------#++
      def rel_menus
        return users_menus
      end
    
      #== users_menus登録処理
      #
      # 戻り値:: 処理結果(true/false)
      #-----------------------------------------------------------------#++
      def create_rel_menu(menu_id = nil, auth_code = nil)
        result = true
        
        p = {:user_id => self.id, :menu_id => menu_id, :auth_code => auth_code}
        result = UsersMenu.new.create_mng(p)
        unless result
          errors.add_to_base(APEMJ0005 + EMD0004)
        end
        
        return result
      end
      
      # general override
      def create_exec(params)
        update_exec(params)
      end
      
      def update_exec(params)
        base_params = params[:user]
        fill_disp_sort_eager(base_params)
        position_params = params[:user_position]
        extract_shared_params(base_params, position_params, UserPosition) if position_params
        
        self.update_attributes!(base_params)
        if position_params
          if self.user_positions.length > 0
            self.user_positions.first.update_attributes!(position_params)
            MasterRevision.up_rev_number(UserPosition.name.tableize)
          else
            position_params[:position_number] ||= 1
            position_params[:invalid_flag_code] = 0
            self.user_positions.create(position_params)
            MasterRevision.up_rev_number(UserPosition.name.tableize)
          end
        end
        save!
        true
      end
      
      def destroy_exec
        destroy
        true
      end
    end
  end
end