package com.red.circle.gateway.logging;

import com.red.circle.component.tencent.cls.request.ClsLogItem;
import com.red.circle.component.tencent.cls.service.ToClsLog;
import com.red.circle.framework.core.request.RequestHeaderConstant;
import com.red.circle.framework.core.security.UserCredential;
import com.red.circle.tool.core.date.DateUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import com.red.circle.tool.core.text.StringPool;
import com.red.circle.tool.core.text.StringUtils;
import java.io.Serial;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * @author pengliang on 2024/1/12
 */
@Slf4j
@Data
public class GatewayLogging implements ToClsLog, Serializable {

  @Serial
  private static final long serialVersionUID = 1L;

  /**
   * 请求id.
   */
  private String id;

  /**
   * 请求信息.
   */
  private GatewayLoggingRequest request = new GatewayLoggingRequest();

  /**
   * 响应信息.
   */
  private GatewayLoggingResponse response = new GatewayLoggingResponse();

  /**
   * 目标服务.
   */
  private String targetService;

  /**
   * 执行时间.
   */
  private Long executeTime;

  public void calculateExecuteTime() {
    this.executeTime = response.getTime() - request.getTime();
  }

  public void uploadLog(LoggingUploadService loggingUploadService) {

    if (log.isInfoEnabled()) {
      log.info("{}", JacksonUtils.toJsonPrettyFormat(this));
      return;
    }

    if (log.isDebugEnabled()) {
      log.debug("{}", JacksonUtils.toJsonPrettyFormat(this));
      return;
    }

    if (!Objects.equals(this.getResponse().getCode(), "200")) {
      loggingUploadService.upload(this);
    }
  }

  @Override
  public ClsLogItem toLog() {
    long cTime = DateUtils.now().getTime();
    Map<String, String> headers = Objects.nonNull(request.getHeaders())
        ? request.getHeaders()
        : new HashMap<>();

    UserCredential userCredential = parseToken(headers.get(RequestHeaderConstant.AUTHORIZATION));

    return ClsLogItem.create(cTime)
        .put("id", id)
        .put("userCredential", JacksonUtils.toJson(userCredential))
        .put("reqUserId", Optional.ofNullable(userCredential)
            .map(UserCredential::getUserId).map(Objects::toString).orElse(StringPool.EMPTY)
        )
        // Request
        .put("reqMethod", request.getMethod())
        .put("reqHost", request.getHost())
        .put("reqPath", request.getPath())
        .put("reqBody", request.getBody())
        .put("reqIp", request.getIp())
        .put("reqHeaders", JacksonUtils.toJson(headers))
        .put("reqTime", Objects.toString(request.getTime()))
        // Response
        .put("resCode", response.getCode())
        .put("resBody", response.getBody())
        .put("resTime", Objects.toString(response.getTime()))

        .put("targetService", targetService)
        .put("executeTime",
            Objects.isNull(executeTime) ? StringPool.EMPTY : Objects.toString(executeTime))
        .put("createTime", Objects.toString(cTime))
        ;
  }

  private UserCredential parseToken(String token) {
    try {
      return StringUtils.isNotBlank(token)
          ? UserCredential.parseToken(token)
          : null;
    } catch (Exception ignore) {
      // ignore
    }
    return null;
  }
}
