import { store } from "@/store/store";
import moment from "moment";
import { isNil, omit } from "lodash-es";
import SlsWebLogger from "@aliyun-sls/web-track-browser";
import { VITE_SLS_STORE_NAME } from "@/constants/constants";
import { electronLog } from "./electronLog";
import getDeviceInfos from "./deviceInfo";
import { isElectron } from "./nativeMethods";

const aliLogger = new SlsWebLogger({
  host: "cn-shenzhen.log.aliyuncs.com",
  project: "examcloud",
  logstore: VITE_SLS_STORE_NAME,
});

type LogDetail = {
  /** default log */
  lvl?: "debug" | "log" | "warn" | "error";
  /** channels. 往哪些渠道放日志？ default: ['console'] */
  cnl: ("console" | "local" | "server" | "bd")[];
  /** 操作的类型，方便在日志里面查找相同类型的操作 */
  key?: string;
  /** page name */
  pgn?: string;
  /** page url: AUTO => 自动从location.path获取 */
  pgu?: "AUTO" | `/${string}`;
  /** action 引发本次日志的操作 */
  act?: string;
  /** statck 错误信息的stack，单条错误信息也放此处 */
  stk?: string;
  /** detail 错误详细信息 */
  dtl?: string;
  /** api url  */
  aul?: string;
  /** api status  */
  aus?: string;
  /** error json */
  ejn?: string;
  /** error name */
  ename?: string;
  /** error message */
  emsg?: string;
  /**  可能是个Error对象，如果是Error，这取出标准字段，否则仅转为JSON */
  possibleError?: any;
  /** 扩展字段的集合。TODO: 提示不允许出去前面的字段 */
  // "not in keyof T" - exclude keyof T from string #42315
  // https://github.com/microsoft/TypeScript/issues/42315
  ext?: Record<string, any>;
};
/** 记录重要日志到多个source
 * 示例：
 * @param detail.key - '本地人脸比对'
 * @param detail.pgn - '登录页面' | '在线考试列表页面'
 * @param detail.act - '点击登录按钮'
 * @param detail.cnl - ['local', 'console']
 * @param detail.stk - error.stack
 * @param detail.dtl - '第一次点击登录' | '第二次点击登录' // 方便查询
 * @param detail.aul - '/api/login'
 * @param detail.aus - '500'
 * @param detail.ejn - JSON.stringify({a: 0})
 * @param detail.ename - error.name
 * @param detail.emsg - error.message
 * @param detail.possibleError - possibleError
 * @param detail.ext - {UA: 'chrome 99'}
 */
export default function createLog(detail: LogDetail) {
  const user = store.user;
  const defaultFileds = {
    /** 供灰度发布识别日志 */
    __ver: "3.0.0",
    lvl: "log",
    uuidForEcs: localStorage.getItem("uuidForEcs"),
    clientDate: moment().format("YYYY-MM-DD HH:mm:ss.SSS"),
    ...(user?.id ? { userId: user.id } : {}),
    ...(detail?.cnl ? { cnl: detail.cnl } : { cnl: ["console"] }),
  };

  let possibleErrorFields = {};
  if (detail.possibleError instanceof Error) {
    possibleErrorFields = {
      ejn: JSON.stringify(detail.possibleError),
      ename: detail.possibleError.name,
      emsg: detail.possibleError.message,
      stk: detail.possibleError.stack,
    };
  } else if (!isNil(detail.possibleError)) {
    possibleErrorFields = { ejn: JSON.stringify(detail.possibleError) };
  }

  const newDetail: Record<string, string> = Object.assign(
    defaultFileds,
    omit(detail, "ext"),
    detail.ext,
    detail.pgu === "AUTO"
      ? { pgu: location.href.replace(location.origin, "") }
      : {},
    possibleErrorFields
  );
  // FIXME: 后期设置条件开启非log级别的日志，此时全部打回。
  // 可考虑阿里云设置一个永久的URL，URL的内容可定制，是一串逗号分割的userId，用于开启这些用户的debug级别日志
  if (import.meta.env.PROD && newDetail.lvl !== "log") {
    return;
  }
  // 开发阶段将日志全部打印出来
  if (import.meta.env.DEV && !detail.cnl.includes("console")) {
    detail.cnl.push("console");
  }
  if (detail.cnl?.includes("console")) {
    if (import.meta.env.DEV) {
      console.log(
        omit(newDetail, ["__ver", "cnl", "lvl", "uuidForEcs", "clientDate"])
      );
    } else {
      console.log(newDetail);
    }
  }
  if (!import.meta.env.DEV && detail.cnl?.includes("server")) {
    aliLogger.send(newDetail);
  }
  if (detail.cnl?.includes("local") && electronLog) {
    void electronLog(newDetail);
  }
}

/** 要在用户登录后调用，仅需调用一次 */
export function createUserDetailLog() {
  const ext: Record<string, any> = {};
  const user = store.user;
  const deviceInfos = getDeviceInfos();

  const { displayName, identityNumber, rootOrgName, rootOrgId } = store.user;

  Object.assign(
    ext,
    { displayName, identityNumber, rootOrgName, rootOrgId },
    { userStudentCodeList: user.studentCodeList.join(",") },
    { UA: navigator.userAgent },
    deviceInfos
  );

  createLog({ cnl: ["server"], pgn: "登录页面", act: "登录成功日志", ext });
}

export function createEncryptLog() {
  // 非 electron 返回
  if (!isElectron()) return;

  try {
    const uuidForEcs = localStorage.getItem("uuidForEcs");
    // 没有 uuidForEcs 日志没法查询
    if (!uuidForEcs) return;

    let log = null;
    let lastLogIndex: number = +(localStorage.getItem("lastLogIndex") || 0);
    log = window.nodeRequire("electron-log");
    // const filePath = log.getFile().path;
    // eslint-disable-next-line @typescript-eslint/no-unsafe-call
    const filePath: string = log.transports.file.findLogPath();
    const fs: typeof import("fs") = window.nodeRequire("fs");
    const content = fs.readFileSync(filePath, "utf-8");
    const ary = content.toString().split("\r\n").join("\n").split("\n");
    // 重复上传没有时间的行：跨过非时间戳的行; 错误识别：全部重新执行
    // let lastIndex = ary.findIndex(v => v === lastLog);
    // console.log({ lastIndex });
    if (ary.length < lastLogIndex) {
      lastLogIndex = 0;
    }

    const logLen = 10;
    const newAry = ary
      .slice(lastLogIndex, lastLogIndex + logLen)
      .filter((v) => v);
    // 如果没有上传的内容，则不修改lastLog, 也不上传
    if (!newAry.length) return;
    lastLogIndex = lastLogIndex + newAry.length;

    localStorage.setItem("lastLogIndex", lastLogIndex + "");
    createLog({ cnl: ["server"], ext: { encryptLog: newAry.join("\n") } });
  } catch (error) {
    console.debug(error);
    return;
  }
}

/** 获得页面的dimension
 * @argument pgn - 页面名称
 */
export function dimensionLog(pgn: string) {
  logger({
    cnl: ["server", "local"],
    pgn,
    ext: {
      scrollX: window.scrollX,
      scrollY: window.scrollY,
      width: window.screen.width,
      height: window.screen.height,
      screenX: window.screen.availWidth,
      screenY: window.screen.availHeight,
      clientWidth: document.documentElement.clientWidth,
      clientHeight: document.documentElement.clientHeight,
      windowInnerWidth: window.innerWidth,
      windowInnerHeight: window.innerHeight,
      windowOuterWidth: window.outerWidth,
      windowOuterHeight: window.outerHeight,
      // 是否全屏
      equal1:
        "dimesion1" +
        (window.screen.width === window.outerWidth &&
          window.screen.height === window.outerHeight),
      // 是否打开了调试窗口
      equal2:
        "dimesion2" +
        (window.innerWidth === window.outerWidth &&
          window.innerHeight === window.outerHeight),
    },
  });
}
