package mobvista.dmp.datasource.id_mapping

import com.alibaba.fastjson.JSONObject
import mobvista.dmp.common.MobvistaConstant._
import org.apache.commons.lang3.StringUtils
import org.apache.spark.sql.Row
import org.apache.spark.sql.types._

import java.net.URLDecoder
import scala.collection.mutable.ArrayBuffer

/**
 * @package: mobvista.dmp.datasource.id_mapping
 * @author: wangjf
 * @date: 2021/11/30
 * @time: 3:02 下午
 * @email: jinfeng.wang@mobvista.com
 */
object Constant {

  /**
   *
   * @param idfa              IDFA
   * @param sysId             SYSID
   * @param idfv_bundle       IDFV+bundleID
   * @param bmosv_osv_upt     brand+model+os+os_version+osv_upt
   * @param bmosv_upt         brand+model+os+os_version+upt
   * @param bmosv_ipua_bundle brand+model+os+os_version+ip+ua+bundle
   * @param xwho              xwho 热云账号ID
   * @param user_id           user_id 热云账号ID
   * @param bkupid            BKUPID
   * @param cnt               一天内同一事件出现的频次
   */
  case class IosVert(idfa: String, sysId: String, idfv_bundle: String, bmosv_osv_upt: String, bmosv_upt: String,
                     bmosv_ipua_bundle: String, xwho: String, user_id: String, bkupid: String, cnt: Int) extends Serializable

  val iosVertSchema: StructType = StructType(Array(
    StructField("idfa", StringType),
    StructField("sysid", StringType),
    StructField("idfv_bundle", StringType),
    StructField("bmosv_osv_upt", StringType),
    StructField("bmosv_upt", StringType),
    StructField("bmosv_ipua_bundle", StringType),
    StructField("xwho", StringType),
    StructField("user_id", StringType),
    StructField("bkupid", StringType),
    StructField("cnt", LongType)
  ))

  val iosIDSet = Array("idfa", "sysid", "xwho", "user_id", "idfv_bundle", "bmosv_osv_upt", "bmosv_upt", "bmosv_ipua_bundle", "bkupid")

  val iosMainIDSet = Set("idfa", "sysid", "xwho", "user_id")

  case class IosTab(idfa: String, idfv: String, pkg_name: String, sysid: String, bkupid: String, xwho: String, user_id: String,
                    country: String, ip: String, ua: String, brand: String, model: String, os_version: String, osv_upt: String, upt: String)
    extends java.io.Serializable

  val iosSchema: StructType = StructType(Array(
    StructField("idfa", StringType),
    StructField("idfv", StringType),
    StructField("pkg_name", StringType),
    StructField("sysid", StringType),
    StructField("bkupid", StringType),
    StructField("xwho", StringType),
    StructField("user_id", StringType),
    StructField("country", StringType),
    StructField("ip", StringType), //  IP
    StructField("ua", StringType), //  UA
    StructField("brand", StringType), //  品牌
    StructField("model", StringType), //  型号
    StructField("os_version", StringType), //  操作系统版本
    StructField("osv_upt", StringType), //  操作系统更新时间
    StructField("upt", StringType), //  开机时间
    StructField("event_type", StringType), //  事件类型
    StructField("network_type", StringType), //  网络类型，用于判断API/SDK
    StructField("cnt", LongType) //  事件频次
  ))

  /**
   *
   * @param imei           IMEI
   * @param gaid           GAID
   * @param oaid           OAID
   * @param sysId          SYSID
   * @param android_pkg    androidId+bundleID
   * @param bmosv_upt      brand+model+os+os_version+upt
   * @param bmosv_ipua_pkg brand+model+os+os_version+ip+ua+pkg
   * @param xwho           xwho 热云账号ID
   * @param user_id        user_id 热云账号ID
   * @param bkupid         BKUPID
   * @param cnt            一天内同一事件出现的频次
   */
  case class AdrVert(imei: String, gaid: String, oaid: String, sysId: String, android_pkg: String, bmosv_upt: String,
                     bmosv_ipua_pkg: String, xwho: String, user_id: String, bkupid: String, cnt: Int) extends Serializable

  val adrCNVertSchema: StructType = StructType(Array(
    StructField("imei", StringType),
    StructField("oaid", StringType),
    StructField("gaid", StringType),
    StructField("sysid", StringType),
    StructField("android_pkg", StringType),
    StructField("bmosv_upt", StringType),
    StructField("bmosv_ipua_pkg", StringType),
    StructField("xwho", StringType),
    StructField("user_id", StringType),
    StructField("bkupid", StringType),
    StructField("cnt", LongType)
  ))

  val adrVertSchema: StructType = StructType(Array(
    StructField("gaid", StringType),
    StructField("imei", StringType),
    StructField("oaid", StringType),
    StructField("sysid", StringType),
    StructField("android_pkg", StringType),
    StructField("bmosv_upt", StringType),
    StructField("bmosv_ipua_pkg", StringType),
    StructField("xwho", StringType),
    StructField("user_id", StringType),
    StructField("bkupid", StringType),
    StructField("cnt", LongType)
  ))

  val androidCNIDSet = Array("imei", "oaid", "gaid", "sysid", "xwho", "user_id", "android_pkg", "bmosv_upt", "bmosv_ipua_pkg", "bkupid")

  val androidIDSet = Array("gaid", "imei", "oaid", "sysid", "xwho", "user_id", "android_pkg", "bmosv_upt", "bmosv_ipua_pkg", "bkupid")

  val androidMainIDSet = Set("imei", "gaid", "oaid", "sysid", "xwho", "user_id")

  case class AdrTab(imei: String, android_id: String, pkg_name: String, oaid: String, gaid: String, sysid: String, bkupid: String,
                    xwho: String, user_id: String, country: String, ip: String, ua: String, brand: String, model: String,
                    os_version: String, osv_upt: String, upt: String)
    extends java.io.Serializable

  val adrSchema: StructType = StructType(Array(
    StructField("imei", StringType),
    StructField("android_id", StringType),
    StructField("pkg_name", StringType),
    StructField("oaid", StringType),
    StructField("gaid", StringType),
    StructField("sysid", StringType), //  MTG 自建ID
    StructField("bkupid", StringType), //  MTG 自建ID
    StructField("xwho", StringType), //  tkio 账号
    StructField("user_id", StringType), //  tkio  账号
    StructField("country", StringType), //  国家信息
    StructField("ip", StringType), //  IP
    StructField("ua", StringType), //  UA
    StructField("brand", StringType), //  品牌
    StructField("model", StringType), //  型号
    StructField("os_version", StringType), //  操作系统版本
    StructField("osv_upt", StringType), //  操作系统更新时间
    StructField("upt", StringType), //  开机时间
    StructField("event_type", StringType), //  事件类型
    StructField("network_type", StringType), //  网络类型，用于判断API/SDK
    StructField("cnt", LongType) //  事件频次
  ))

  val otherSchema: StructType = StructType(Array(
    StructField("idfa", StringType),
    StructField("idfv", StringType),
    StructField("pkg_name", StringType),
    StructField("imei", StringType),
    StructField("android_id", StringType),
    StructField("oaid", StringType),
    StructField("gaid", StringType),
    StructField("sysid", StringType), //  MTG 自建ID
    StructField("bkupid", StringType), //  MTG 自建ID
    StructField("xwho", StringType), //  tkio 账号
    StructField("user_id", StringType), //  tkio  账号
    StructField("country", StringType), //  国家信息
    StructField("ip", StringType), //  IP
    StructField("ua", StringType), //  UA
    StructField("brand", StringType), //  品牌
    StructField("model", StringType), //  型号
    StructField("os_version", StringType), //  操作系统版本
    StructField("osv_upt", StringType), //  操作系统更新时间
    StructField("upt", StringType), //  开机时间
    StructField("event_type", StringType), //  事件类型
    StructField("network_type", StringType), //  网络类型，用于判断API/SDK
    StructField("cnt", LongType) //  事件频次

  ))

  val dsp_req_sql =
    """
      |SELECT idfa, gaid, exitid, platform, country
      |  FROM dwh.etl_dsp_request_daily_hours
      |  WHERE dt = '@date'
      |""".stripMargin

  val dsp_req_sql_v2 =
    """
      |SELECT idfa, googleadid gaid, ext5 exitid, os platform, countrycode country, deviceip ip,
      |  parseUA(ext6) ua, make brand, model, osv os_version, '0' osv_upt, '0' upt, cpackagename pkg_name,
      |  cncttype network_type, COUNT(1) cnt
      |  FROM adn_dsp.log_adn_dsp_request_orc_hour
      |  WHERE CONCAT(yr,mt,dt) = '@date' @hour
      |  GROUP BY idfa, googleadid, ext5, os, countrycode, deviceip,
      |  parseUA(ext6), make, model, osv, cpackagename, cncttype
      |""".stripMargin

  val adn_request_sql =
    """
      |SELECT idfa, gaid, oaid, idfv, androidid, extsysid, imei, platform, country
      |  FROM dwh.etl_adn_org_request_daily_hours
      |  WHERE CONCAT(yt,mt,dt) = '@date'
      |""".stripMargin

  //  ext_data
  val adn_request_sql_v2 =
    """
      |SELECT idfa, gaid, ext_oaid oaid, getDevId(cdn_ab) idfv, ext_packagename pkg_name, dev_id androidid, ext_sysid extsysid, imei, platform,
      |  country_code country, ip, parseUA(ext_systemuseragent) ua, ext_brand brand, ext_model model, os_version,
      |  COALESCE(get_json_object(ext_data,'$.osvut'),0) osv_upt, COALESCE(get_json_object(ext_data,'$.upt'),0) upt
      |  FROM dwh.ods_adn_trackingnew_request
      |  WHERE CONCAT(yyyy,mm,dd) = '@date'
      |  GROUP BY idfa, gaid, oaid, cdn_ab, dev_id, ext_sysid, imei, platform, country_code, ip, parseUA(ext_systemuseragent),
      |  ext_brand, ext_model, os_version, COALESCE(get_json_object(ext_data,'$.osvut'),0), COALESCE(get_json_object(ext_data,'$.upt'),0)
      |""".stripMargin

  //  not distinct
  val adn_request_sql_v3 =
    """
      |SELECT idfa, gaid, ext_oaid oaid, getDevId(cdn_ab) idfv, ext_packagename pkg_name, dev_id androidid, ext_sysid extsysid, imei, platform,
      |  country_code country, ip, parseUA(ext_systemuseragent) ua, ext_brand brand, ext_model model, os_version,
      |  COALESCE(get_json_object(ext_data,'$.osvut'),0) osv_upt, COALESCE(get_json_object(ext_data,'$.upt'),0) upt, network_type, COUNT(1) cnt
      |  FROM dwh.ods_adn_trackingnew_request
      |  WHERE yyyy = '@year' AND mm = '@month' AND dd = '@day'
      |  GROUP BY idfa, gaid, ext_oaid, getDevId(cdn_ab), ext_packagename, dev_id, ext_sysid, imei, platform,
      |  country_code, ip, parseUA(ext_systemuseragent), ext_brand, ext_model, os_version,
      |  COALESCE(get_json_object(ext_data,'$.osvut'),0), COALESCE(get_json_object(ext_data,'$.upt'),0), network_type
      |""".stripMargin

  val ios_id_mapping_sql: String =
    """
      |SELECT idfa, idfv, pkg_name, sysid, bkupid, xwho, user_id, country, ip, ua, brand, model, os_version, osv_upt, upt, count(1) cnt
      |  FROM dwd.dwd_device_ios_ids_inc_daily WHERE dt = '@date' @filter_country
      |  GROUP BY idfa, idfv, pkg_name, sysid, bkupid, xwho, user_id, country, ip, ua, brand, model, os_version, osv_upt, upt
      |""".stripMargin

  val android_id_mapping_sql: String =
    """
      |SELECT imei, android_id, pkg_name, oaid, gaid, sysid, bkupid, xwho, user_id, country, ip, ua, brand, model, os_version, osv_upt, upt, count(1) cnt
      |  FROM dwd.dwd_device_android_ids_inc_daily WHERE dt = '@date' @filter_country
      |  GROUP BY imei, android_id, pkg_name, oaid, gaid, sysid, bkupid, xwho, user_id, country, ip, ua, brand, model, os_version, osv_upt, upt
      |""".stripMargin

  val ios_id_mapping_sql_v2: String =
    """
      |SELECT idfa, idfv, pkg_name, sysid, bkupid, xwho, user_id, country, ip, ua, brand, model, os_version, osv_upt, upt, SUM(cnt) cnt
      |  FROM dws.dws_device_id_ios_frequency WHERE dt = '@date' @filter_country
      |  GROUP BY idfa, idfv, pkg_name, sysid, bkupid, xwho, user_id, country, ip, ua, brand, model, os_version, osv_upt, upt
      |""".stripMargin

  val android_id_mapping_sql_v2: String =
    """
      |SELECT imei, android_id, pkg_name, oaid, gaid, sysid, bkupid, xwho, user_id, country, ip, ua, brand, model, os_version, osv_upt, upt, SUM(cnt) cnt
      |  FROM dws.dws_device_id_android_frequency WHERE dt = '@date' @filter_country
      |  GROUP BY imei, android_id, pkg_name, oaid, gaid, sysid, bkupid, xwho, user_id, country, ip, ua, brand, model, os_version, osv_upt, upt
      |""".stripMargin

  val old_id_mapping_sql: String =
    """
      |
      |""".stripMargin

  val sss_sql =
    """
      |""".stripMargin

  def parseUA(ua: String): String = {
    if (StringUtils.isNotBlank(ua)) {
      URLDecoder.decode(ua.replaceAll("%(?![0-9a-fA-F]{2})", "%25"), "UTF-8")
    } else {
      ""
    }
  }

  def getDevId(devId: String): String = {
    var dev_id = ""
    if (StringUtils.isNotBlank(devId) && !",".equals(devId)) {
      val ids = devId.split(",", -1)
      if (StringUtils.isNotBlank(ids(0)) && ids(0).matches(didPtn) && !ids(0).matches(allZero)) {
        dev_id = ids(0)
      } else if (ids.length == 2 && !ids(1).matches(didPtn) && !ids(1).matches(allZero)) {
        dev_id = ids(1)
      }
    }
    dev_id
  }

  val iosIDScoreMap: Map[String, Double] = Map("idfa" -> 1000, "sysid" -> 1, "xwho" -> 0.9, "user_id" -> 0.9, "bmosv_osv_upt" -> 0.9,
    "idfv_bundle" -> 0.8, "bmosv_upt" -> 0.7, "bmosv_ipua_bundle" -> 0.6, "bkupid" -> 0.3)

  val androidIDScoreMap: Map[String, Double] = Map("gaid" -> 1000, "imei" -> 1000, "oaid" -> 1000, "sysid" -> 1, "xwho" -> 0.9, "user_id" -> 0.9,
    "android_pkg" -> 0.8, "bmosv_upt" -> 0.7, "bmosv_ipua_pkg" -> 0.6, "bkupid" -> 0.3)

  def process(idfa: String, idfv: String, pkg_name: String, imei: String, androidId: String, oaid: String, gaid: String, sysId: String,
              bkupId: String, country: String, ip: String, ua: String, brand: String, model: String, os_version: String, osv_upt: String,
              upt: String, network_type: String, platform: String, cnt: Long): (String, Row) = {
    val f_idfa = if (StringUtils.isNotBlank(idfa) && (idfa.matches(didPtn) && !idfa.matches(allZero) || idfa.matches(md5Ptn))) {
      idfa
    } else {
      ""
    }
    val f_idfv = if (StringUtils.isNotBlank(idfv) && (idfv.matches(didPtn) && !idfa.matches(allZero) || idfv.matches(md5Ptn))) {
      idfv
    } else {
      ""
    }
    val f_imei = if (StringUtils.isNotBlank(imei) && (imei.matches(imeiPtn) && !imei.matches(imeiPtnAll) || imei.matches(md5Ptn))) {
      imei
    } else {
      ""
    }
    val f_androidId = if (StringUtils.isNotBlank(androidId) && (androidId.matches(andriodIdPtn) && !androidId.matches(andriodIdAll) || androidId.matches(md5Ptn))) {
      androidId
    } else {
      ""
    }
    val f_oaid = if (StringUtils.isNotBlank(oaid) && (oaid.length >= 16 && oaid.length <= 64 && !oaid.matches(allZero) || oaid.matches(md5Ptn))) {
      oaid
    } else {
      ""
    }
    val f_gaid = if (StringUtils.isNotBlank(gaid) && (gaid.matches(didPtn) && !gaid.matches(allZero) || gaid.matches(md5Ptn))) {
      gaid
    } else {
      ""
    }
    val f_sysId = if (StringUtils.isNotBlank(sysId) && (sysId.matches(didPtn) && !sysId.matches(allZero) || sysId.matches(md5Ptn))) {
      sysId
    } else {
      ""
    }
    val f_bkupId = if (StringUtils.isNotBlank(bkupId) && (bkupId.matches(didPtn) && !bkupId.matches(allZero) || bkupId.matches(md5Ptn))) {
      bkupId
    } else {
      ""
    }
    val f_country = if (StringUtils.isNotBlank(country)) {
      country.toUpperCase
    } else {
      ""
    }
    val f_ip = if (StringUtils.isNotBlank(ip) && ip.matches(ipPtn)) {
      ip
    } else {
      ""
    }
    var f_platform = if (StringUtils.isNotBlank(platform)) {
      platform.toLowerCase()
    } else {
      ""
    }
    val f_ua = if (StringUtils.isNotBlank(ua)) {
      ua.trim
    } else {
      ""
    }
    val f_brand = if (StringUtils.isNotBlank(brand)) {
      brand.trim
    } else {
      ""
    }
    val f_model = if (StringUtils.isNotBlank(model)) {
      model.trim
    } else {
      ""
    }
    val f_osv = if (StringUtils.isNotBlank(os_version)) {
      os_version.trim
    } else {
      ""
    }
    val f_osv_upt = if (StringUtils.isNotBlank(osv_upt) && osv_upt.length > 5) {
      osv_upt.trim
    } else {
      ""
    }
    val f_upt = if (StringUtils.isNotBlank(upt) && upt.length > 5) {
      upt.trim
    } else {
      ""
    }
    val f_network_type = if (StringUtils.isNotBlank(network_type) && !network_type.equalsIgnoreCase("unknown")) {
      network_type.trim
    } else {
      ""
    }

    f_platform = if (f_platform.contains("ios") || f_platform.contains("iphone") || f_brand.toLowerCase.contains("apple")
      || f_model.toLowerCase.contains("iphone") || f_model.toLowerCase.contains("ipad") || f_osv.toLowerCase.contains("ios")
      || f_ua.toLowerCase.contains("iphone") || f_ua.toLowerCase.contains("ipad")) {
      "ios"
    } else if (f_platform.contains("android") || f_osv.toLowerCase.contains("android") || f_ua.toLowerCase.contains("android")
      || f_imei.length >= 14 || (f_oaid.length >= 16 && f_oaid.length <= 64) || f_androidId.length >= 15 || f_gaid.length == 36 || f_gaid == 32) {
      "android"
    } else {
      "other"
    }
    val rw = if ("ios".equals(f_platform)) {
      Row(f_idfa, f_idfv, pkg_name, f_sysId, f_bkupId, "", "", f_country, f_ip, f_ua, f_brand, f_model, f_osv, f_osv_upt, f_upt, "", f_network_type, cnt)
    } else if ("android".equals(f_platform)) {
      Row(f_imei, f_androidId, pkg_name, f_oaid, f_gaid, f_sysId, f_bkupId, "", "", f_country, f_ip, f_ua, f_brand, f_model, f_osv, f_osv_upt, f_upt, "", f_network_type, cnt)
    } else {
      Row(f_idfa, f_idfv, pkg_name, f_imei, f_androidId, f_oaid, f_gaid, f_sysId, f_bkupId, "", "", f_country, f_ip, f_ua, f_brand, f_model, f_osv, f_osv_upt, f_upt, "", f_network_type, cnt)
    }
    (f_platform, rw)
  }

  case class Result(device_id: String, device_type: String, one_id: String, update_date: String) extends Serializable

  case class OneIDScore(one_id: String, one_type: String, one_score: Double, one_version: String) extends Serializable

  class CustomInterator(active_date: String, iter: Iterator[((String, String), Set[(String, String, Long)])],
                        idArray: Array[String], mainIDSet: Set[String]) extends Iterator[ArrayBuffer[((String, String), (String, String))]] {
    def hasNext: Boolean = {
      iter.hasNext
    }

    def next: ArrayBuffer[((String, String), (String, String))] = {
      val kv = iter.next
      val array = new ArrayBuffer[((String, String), (String, String))]()
      val tmpOneId = kv._1._1
      val tmpOneIdType = kv._1._2
      val iters = kv._2
      val oneID = new JSONObject()
      var minTypeIndex = idArray.indexOf(tmpOneIdType)
      iters.foreach(t => {
        if (idArray.indexOf(t._2) < minTypeIndex) {
          minTypeIndex = idArray.indexOf(t._2)
        }
        if (tmpOneId.equals(t._1) || mainIDSet.contains(t._2)) {
          val json = new JSONObject()
          json.put("one_type", t._2)
          json.put("one_date", active_date)
          json.put("one_cnt", t._3)
          oneID.put(t._1, json)
        }
        finalize()
      })
      array += (((tmpOneId, tmpOneIdType), (oneID.toJSONString, active_date)))
      //  if (idArray.indexOf(tmpOneIdType) > minTypeIndex) {
      iters.foreach(itr => {
        var oneJSON = new JSONObject()
        if (oneID.containsKey(itr._1)) {
          oneJSON.put(itr._1, oneID.getJSONObject(itr._1))
        } else {
          oneJSON = oneID
        }
        array += (((itr._1, itr._2), (oneJSON.toJSONString, active_date)))
        finalize()
      })
      //  }
      array
    }
  }
}