require 'mongo'

require 'json'
require 'rubygems'
require 'nokogiri'
require 'rails'
require "imperituroard/projects/iot/internal_functions"

#class for communication with mongo database for iot API
class MongoIot

  attr_accessor :mongo_ip, :mongo_port, :client, :mongo_database, :internal_func

  def initialize(mongo_ip, mongo_port, mongo_database)
    @mongo_database = mongo_database
    @mongo_ip = mongo_ip
    @mongo_port = mongo_port
    client_host = [mongo_ip + ":" + mongo_port]
    @client = Mongo::Client.new(client_host, :database => mongo_database)
    @internal_func = InternalFunc.new
  end

  def audit_logger(proc_name, src_ip, input_json, output_json, real_ip)
    out_resp = {}
    begin
      current = internal_func.datetimenow
      collection = client[:audit]
      doc = {
          :proc_name => proc_name,
          :date => current,
          :sender => {:src_ip => src_ip, :real_ip => real_ip},
          :input_params => input_json,
          :output_params => output_json
      }
      result = collection.insert_one(doc)
      out_resp = {:code => 200, :result => "audit_logger: Request completed successfully", :body => result}
    rescue
      out_resp = {:code => 507, :result => "audit_logger: Unknown SDK error"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end

  def audit_iot_logger(proc_name, url_str, input_json, output_json)
    out_resp = {}
    p "iiiiiiiot"
    p input_json
    begin
      current = internal_func.datetimenow
      collection = client[:audit_iot_platform]
      doc = {
          :proc_name => proc_name,
          :date => current,
          :url => url_str,
          :iot_platform_request => input_json,
          :iot_platform_responce => output_json
      }
      p doc
      result = collection.insert_one(doc)
      out_resp = {:code => 200, :result => "audit_logger: Request completed successfully", :body => result}
    rescue
      out_resp = {:code => 507, :result => "audit_logger: Unknown SDK error"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end

  #:code => 507, :result => "Unknown SDK error"
  #{:code => 200, :result => "Request completed successfully", :body => result_ps}
  def get_profiles_by_login(login)
    out_resp = {}
    begin
      p "get_profiles_by_login get_profiles_by_login"
      login_profiles = []
      req2 = []
      result_ps = []
      collection = client[:users]
      collection2 = client[:device_profiles]
      collection.find({:login => login}).each {|row|
        login_profiles = row["permit_profiles"]
      }
      p login_profiles
      if login_profiles !=[]
        for i in login_profiles
          req2.append({:profile_id => i})
        end
        collection2.find({:$or => req2}, {:_id => 0}).each {|row|
          result_ps.append(row)
        }
        out_resp = {:code => 200, :result => "get_profiles_by_login: Request completed successfully", :body => result_ps}
      else
        out_resp = {:code => 404, :result => "get_profiles_by_login: Access denied. Incorrect login"}
      end
    rescue
      out_resp = {:code => 507, :result => "get_profiles_by_login: Unknown SDK error"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end


  def get_login_info(login)
    out_resp = {}
    begin
      login_profiles = []
      req2 = []
      result_ps = []
      collection = client[:users]
      collection2 = client[:device_profiles]
      collection.find({:login => login}).each {|row|
        login_profiles = row
      }
      p "login_profiles"
      p login_profiles
      if login_profiles != [] && login_profiles["login"] != nil && login_profiles["login"] != ""
        passw_check = internal_func.check_pass_format(login_profiles["password"])
        if passw_check[:code]==200
          out_resp = {:code => 200, :result => "get_login_info: Request completed successfully", :body => login_profiles}
        else
          out_resp = {:code => 405, :result => "get_login_info: incorrect password"}
        end
      else
        out_resp = {:code => 404, :result => "get_login_info: login not found in database"}
      end
    rescue
      out_resp = {:code => 507, :result => "get_login_info: Unknown SDK error"}
    end
    p out_resp
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end

  def get_login_inf2_select(login)
    out_resp = {}
    begin
      login_profiles = []
      req2 = []
      result_ps = []
      collection = client[:users]
      collection2 = client[:device_profiles]
      collection.find({:login => login}).each {|row|
        login_profiles = row
      }
      if login_profiles != [] && login_profiles["login"] != nil && login_profiles["login"] != ""
        out_resp = {:code => 200, :result => "get_login_info: Request completed successfully", :body => login_profiles}
      else
        out_resp = {:code => 404, :result => "get_login_info: login not found in database"}
      end
    rescue
      out_resp = {:code => 507, :result => "get_login_info: Unknown SDK error"}
    end
    p out_resp
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end


  def get_imei_info_from_db(imeilist)
    out_resp = {}
    p imeilist
    p "imeilist"
    begin
      req2 = []
      result_ps = []
      collection = client[:device_imei]
      for i in imeilist
        req2.append({:imei => i})
      end
      collection.find({:$or => req2}, {:_id => 0}).each {|row|
        result_ps.append(row)
      }
      out_resp = {:code => 200, :result => "get_imei_info_from_db: Request completed successfully", :body => result_ps}
    rescue
      out_resp = {:code => 507, :result => "get_imei_info_from_db: Unknown SDK error"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end

  def get_profile_id_by_name(profile_name)
    begin
      result_ps = []
      collection = client[:device_profiles]
      collection.find({"profile" => profile_name}).each {|row|
        result_ps.append(row)
      }
      result_ps[0]
    rescue
      []
    end
  end

  def get_profile_name_by_id(profile_id)
    begin
      result_ps = []
      collection = client[:device_profiles]
      collection.find({"profile_id" => profile_id.to_i}).each {|row|
        result_ps.append(row)
      }
      result_ps[0]
    rescue
      []
    end
  end



  def get_profile_list_by_id(profile_id_list)
    begin
      result_ps = []
      collection = client[:device_profiles]
      list_id = internal_func.prof_id_list_transform_mongo(profile_id_list)
      collection.find({:$or => list_id}).each {|row|
        result_ps.append(row)
      }
      result_ps
    rescue
      []
    end
  end


  def get_type_list_by_id(type_id_list)
    begin
      result_ps = []
      collection = client[:device_types]
      list_id = internal_func.type_id_list_transform_mongo(type_id_list)
      collection.find({:$or => list_id}).each {|row|
        result_ps.append(row)
      }
      result_ps
    rescue
      []
    end
  end

  def get_type_by_id(type_id)
    begin
      result_ps = []
      collection = client[:device_types]
      collection.find({"type_id" => type_id.to_i}).each {|row|
        result_ps.append(row)
      }
      result_ps[0]
    rescue
      []
    end
  end

  def get_type_by_name(type_name)
    begin
      result_ps = []
      collection = client[:device_types]
      collection.find({"model" => type_name}).each {|row|
        result_ps.append(row)
      }
      result_ps[0]
    rescue
      []
    end
  end

  #universal procedure. might be profile id or name
  def check_login_profile_permiss(login, profile)
    out_resp = {}
    get_login_info = get_profiles_by_login(login)
    if get_login_info[:code] == 200
      dst_profile = get_profile_universal(profile)
      access = 1
      if get_login_info[:body] != []
        if dst_profile != []
          for j in get_login_info[:body]
            if j["profile_id"].to_i == dst_profile["profile_id"].to_i
              access = 0
            end
            if access == 0
              out_resp = {:code => 200, :result => "check_login_profile_permiss: Permission granted"}
            else
              out_resp = {:code => 400, :result => "check_login_profile_permiss: Access denied. This incident will be reported."}
            end
          end
        else
          out_resp = {:code => 501, :result => "check_login_profile_permiss: Profile not found"}
        end
      else
        out_resp = {:code => 500, :result => "check_login_profile_permiss: Access denied. Login not found"}
      end
    else
      out_resp = {:code => 500, :result => "check_login_profile_permiss: Access denied. Login not found"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end


  #universal procedure. You can use profile id or profile name as profile_id (string or int)
  def check_login_prof_perm_id_one(login, profile_id)
    out_resp = {}
    get_login_info = get_profiles_by_login(login)
    profile_data = self.get_profile_universal(profile_id)
    access=1
    if get_login_info[:body]!=[]
      for j in get_login_info[:body]
        if j["profile_id"].to_i==profile_data["profile_id"]
          access=0
        end
        if access==0
          out_resp = {:code => 200, :result => "check_login_prof_perm_id_one: Permission granted"}
        else
          out_resp = {:code => 400, :result => "check_login_prof_perm_id_one: Access denied. This incident will be reported."}
        end
      end
    else
      out_resp = {:code => 500, :result => "check_login_prof_perm_id_one: Login not found"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end

  def check_imei_exists(imei_list)
    out_resp = {}
    res_exists = []
    imei_list_res = get_imei_info_from_db(imei_list)
    for k in imei_list_res[:body]
      res_exists.append(k["imei"])
    end
    not_ex = imei_list - res_exists
    out_resp = {:code => 200, :result => "check_imei_exists: Request completed successfully",
                :body => {:exists => res_exists, :not_exists => not_ex}}
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end


  def imei_insert_list(imei_list)
    begin
      collection = client[:device_imei]
      for l in imei_list
        doc = {
            imei: l,
            imsi: "unknown",
            msisdn: "unknown",
            description: "test imei",
            note: "second description",
            profile: 0,
            device_type: 0,
            address: "unknown"
        }
        result = collection.insert_one(l)
        p result
      end
    rescue
      nil
    end
  end


  def imei_insert_list2(imei_list)
    begin
      collection = client[:device_imei]
      for l in imei_list
        doc = {
            imei: l,
            imsi: "unknown",
            msisdn: "unknown",
            description: "test imei",
            note: "second description",
            profile: 0,
            device_type: 0,
            address: "unknown"
        }
        result = collection.insert_one(l)
        p result
      end
    rescue
      nil
    end
  end


  def imei_insert_model(model)
    begin
      collection = client[:device_types]
      result = collection.insert_one(model)
      p result
    rescue
      nil
    end
  end


  def get_profile_name_from_imei(imei)
    out_resp = {}
    info = {}
    begin
      id = ""
      begin
        info = get_imei_info_from_db([imei])
        if info[:body]==[]
          out_resp = {:code => 505, :result => "get_profile_name_from_imei: get_imei_info_from_db returned empty list from database. IMEIS not found"}
        else
          id = info[:body][0]["profile"]
          begin
            p id
            res = get_profile_name_by_id(id)
            p res
            if res.key?("profile")
              res=res
            else
              out_resp = {:code => 505, :result => "get_profile_name_from_imei: Function get_profile_name_by_id not returned profile. Invalid data in database and returned: #{res.to_s}"}
            end
          rescue
            out_resp = {:code => 506, :result => "get_profile_name_from_imei: Function get_profile_name_by_id not processed correctly and returned: #{res.to_s}"}

          end
          begin
            if res["profile"]!=nil
              out_resp = {:code => 200, :result => "get_profile_name_from_imei: Request completed successfully", :body => res, :imei_info => info}
            end
          rescue
            out_resp = {:code => 506, :result => "get_profile_name_from_imei: Function get_profile_name_by_id not processed correctly and returned: #{res.to_s}"}
          end
        end
      rescue
        out_resp = {:code => 506, :result => "get_profile_name_from_imei: Function get_imei_info_from_db not processed correctly and returned: #{info.to_s}"}
      end

    rescue
      out_resp = {:code => 507, :result => "get_profile_name_from_imei: Unknown SDK error"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end


  def device_remove_single_mongo(imei)
    out_resp = {}
    begin
      collection = client[:device_imei]
      doc = {
          "imei" => imei
      }
      result = collection.delete_many(doc)
      p result
      out_resp = {:code => 200, :result => "device_remove_single_mongo: Request completed successfully"}
    rescue
      out_resp = {:code => 507, :result => "device_remove_single_mongo: Unknown SDK error"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end

  def device_modify_attr_mongo(imei, address)
    out_resp = {}
    begin
      collection = client[:device_imei]
      doc = {
          "imei" => imei
      }
      sett = {'$set' => {address: address}}
      result = collection.update_one(doc, sett)
      out_resp = {:code => 200, :result => "device_modify_attr_mongo: Request completed successfully"}
    rescue
      out_resp = {:code => 507, :result => "device_modify_attr_mongo: Unknown SDK error"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end

  def device_modify_any_attr_mongo(imei, attr_list)
    out_resp = {}
    begin
      collection = client[:device_imei]
      doc = {
          "imei" => imei
      }
      sett = {'$set' => attr_list}
      result = collection.update_one(doc, sett)
      p result
    rescue
      continue
    end
  end


  def get_iot_oceanconnect_credent(login)
    out_resp = {}
    begin
      result_ps = []
      collection = client[:users]
      collection.find({"login" => login}).each {|row|
        result_ps.append(row)
      }
      p result_ps[0]
      app_id = result_ps[0][:iot_data][:app_id]
      secret = result_ps[0][:iot_data][:secret]
      out_resp = {:code => 200, :result => "get_iot_oceanconnect_credent: Request completed successfully", :body => {:app_id => app_id, :secret => secret}}
    rescue
      out_resp = {:code => 500, :result => "get_iot_oceanconnect_credent: Process failed"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end


  #function for get iot platform credentials from profile
  #universal procedure. use profile or profile id
  def get_iot_oceanconn_credent_2(profile)
    out_resp = {}
    begin
      result_ps = []
      collection = client[:device_profiles]

      if internal_func.if_digit_or_string(profile.to_s)[:body][:string]
        collection.find({"profile" => profile}).each {|row|
          result_ps.append(row)
        }
      else
        collection.find({"profile_id" => profile.to_i}).each {|row|
          result_ps.append(row)
        }
      end

      app_id = result_ps[0][:iot_data][:app_id]
      secret = result_ps[0][:iot_data][:secret]
      out_resp = {:code => 200, :result => "get_iot_oceanconn_credent_2: Request completed successfully", :body => {:app_id => app_id, :secret => secret}}
    rescue
      out_resp = {:code => 500, :result => "get_iot_oceanconn_credent_2: Process failed"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end

  def get_device_type_info_by_model(device_model)
    out_resp = {}
    begin
      result_ps = []
      collection = client[:device_types]
      collection.find({"model" => device_model}).each {|row|
        result_ps.append(row)
      }
      dattaa = result_ps[0]
      if dattaa != nil
        out_resp = {:code => 200, :result => "get_device_type_info_by_model: Request completed successfully", :body => dattaa}
      else
        out_resp = {:code => 404, :result => "get_device_type_info_by_model: Device info not found", :body => {"model" => device_model, "ManufacturerID" => "unknown", "ManufacturerNAME" => "unknown", "device_type" => "unknown"}}
      end
    rescue
      out_resp = {:code => 500, :result => "get_device_type_info_by_model: procedure error", :body => {"model" => device_model, "ManufacturerID" => "unknown", "ManufacturerNAME" => "unknown", "device_type" => "unknown"}}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end

  #universal procedure. Use id or name
  def get_device_type_info_universal(device_model)
    out_resp = {}
    begin
      as = internal_func.if_digit_or_string(device_model)

      if as[:body][:string]
        result_ps = []
        collection = client[:device_types]
        collection.find({"model" => device_model}).each {|row|
          result_ps.append(row)
        }
        dattaa = result_ps[0]
        if dattaa!=nil
          out_resp = {:code => 200, :result => "get_device_type_info_universal: Request completed successfully", :body => dattaa}
        else
          out_resp = {:code => 404, :result => "get_device_type_info_universal: Device info not found", :body => {"model" => device_model, "ManufacturerID" => "unknown", "ManufacturerNAME" => "unknown", "device_type" => "unknown"}}
        end
      else
        result_ps2 = []
        collection = client[:device_types]
        collection.find({"type_id" => device_model.to_i}).each {|row|
          result_ps2.append(row)
        }
        dattaa = result_ps2[0]
        if dattaa!=nil
          out_resp = {:code => 200, :result => "get_device_type_info_universal: Request completed successfully", :body => dattaa}
        else
          out_resp = {:code => 404, :result => "get_device_type_info_universal: Device info not found", :body => {"model" => device_model, "ManufacturerID" => "unknown", "ManufacturerNAME" => "unknown", "device_type" => "unknown"}}
        end
      end

    rescue
      out_resp = {:code => 500, :result => "get_device_type_info_universal: procedure error", :body => {"model" => device_model, "ManufacturerID" => "unknown", "ManufacturerNAME" => "unknown", "device_type" => "unknown"}}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end



  def compare_profiles(profile1, profile2)
    out_answ = {}
    ch1 = internal_func.if_digit_or_string(profile1)
    ch2 = internal_func.if_digit_or_string(profile2)
    if ch1[:body][:string] == ch2[:body][:string] && ch1[:body][:string] == false
      if profile1.to_i == profile2.to_i
        out_answ = {:code => 200, :result => "compare_profiles: profiles are the same", :is_the_same => true, :data_type => "integer"}
      else
        out_answ = {:code => 200, :result => "compare_profiles: profiles are different", :is_the_same => false, :data_type => "integer"}
      end
    elsif ch1[:body][:string] == ch2[:body][:string] && ch1[:body][:string] == true
      if profile1 == profile2
        out_answ = {:code => 200, :result => "compare_profiles: profiles are the same", :is_the_same => true, :data_type => "string"}
      else
        out_answ = {:code => 200, :result => "compare_profiles: profiles are different", :is_the_same => false, :data_type => "string"}
      end
    else
      ch1_data = {}
      ch2_data = {}
      if ch1[:body][:string]
        ch1_data = self.get_profile_id_by_name(profile1)
      else
        ch1_data = self.get_profile_name_by_id(profile1.to_i)
      end

      if ch2[:body][:string]
        ch2_data = self.get_profile_id_by_name(profile2)
      else
        ch2_data = self.get_profile_name_by_id(profile2.to_i)
      end

      if ch1_data["profile_id"] == ch2_data["profile_id"]
        out_answ = {:code => 200, :result => "compare_profiles: profiles are the same", :is_the_same => true, :data_type => "different"}
      else
        out_answ = {:code => 200, :result => "compare_profiles: profiles are different", :is_the_same => false, :data_type => "different"}
      end
    end
    out_answ
  end

  def compare_device_types(type1, type2)
    out_answ = {}
    tp1 = internal_func.if_digit_or_string(type1)
    tp2 = internal_func.if_digit_or_string(type2)
    p tp1
    p tp2
    p "tp"

    if tp1[:body][:string] == tp2[:body][:string] && tp1[:body][:string] == false
      if type1.to_i == type2.to_i
        out_answ = {:code => 200, :result => "compare_device_types: types are the same", :is_the_same => true, :data_type => "integer"}
      else
        out_answ = {:code => 200, :result => "compare_device_types: types are different", :is_the_same => false, :data_type => "integer"}
      end
    elsif tp1[:body][:string] == tp2[:body][:string] && tp1[:body][:string] == true
      if type1 == type2
        out_answ = {:code => 200, :result => "compare_device_types: types are the same", :is_the_same => true, :data_type => "string"}
      else
        out_answ = {:code => 200, :result => "compare_device_types: types are different", :is_the_same => false, :data_type => "string"}
      end
    else
      tp1_data = {}
      tp2_data = {}
      p "gggggggg"
      if tp1[:body][:string]
        tp1_data = self.get_type_by_name(type1)
      else
        tp1_data = self.get_type_by_id(type1.to_i)
      end

      if tp2[:body][:string]
        tp2_data = self.get_type_by_name(type2)
      else
        tp2_data = self.get_type_by_id(type2.to_i)
      end

      p tp1_data
      p tp2_data
      p type1
      p type2

      if tp1_data["type_id"] == tp2_data["type_id"]
        out_answ = {:code => 200, :result => "compare_device_types: profiles are the same", :is_the_same => true, :data_type => "different"}
      else
        out_answ = {:code => 200, :result => "compare_device_types: profiles are different", :is_the_same => false, :data_type => "different"}
      end
    end
    out_answ
  end


  #universal procedure. put there profile name or id in string or int
  def get_profile_universal(profile)
    inn = internal_func.if_digit_or_string(profile)
    out = {}
    if inn[:body][:string]
      out = self.get_profile_id_by_name(profile)
    else
      out = self.get_profile_name_by_id(profile.to_i)
    end
    out
  end

  def modify_attr_mongo_universal(imei, attribute)
    out_resp = {}
    begin
      collection = client[:device_imei]
      doc = {
          "imei" => imei
      }
      sett = {'$set' => attribute}
      result = collection.update_one(doc, sett)
      out_resp = {:code => 200, :result => "modify_attr_mongo_universal: Request completed successfully"}
    rescue
      out_resp = {:code => 507, :result => "modify_attr_mongo_universal: Unknown SDK error"}
    end
    internal_func.printer_texter(out_resp, "debug")
    out_resp
  end

end