/**
 * @file 全局网络拦截器
 * @author Joker.X
 */

import { AxiosResponse, InternalAxiosRequestConfig, AxiosHeaders, AxiosError } from "@ohos/axios";
import { HttpInterceptor } from "@core/network";
import { getUserState } from "@shared/state";
import { Logger } from "@core/util";
import { Unknown } from "@core/common";

const TAG: string = "NetLog";
const MAX_LOG_LENGTH: number = 800;

/**
 * 认证拦截器
 * 自动在请求头中添加 Authorization token
 */
export class AuthInterceptor implements HttpInterceptor {
  name = "AuthInterceptor";
  priority = 10;

  async onRequest(config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> {
    const token = getUserState().getToken();
    if (token) {
      const headers: AxiosHeaders = AxiosHeaders.from(config.headers);
      headers.set("Authorization", token);
      config.headers = headers;
    }
    return config;
  }

  onRequestError(error: Error): Promise<never> {
    return Promise.reject(error);
  }
}

/**
 * 日志拦截器
 * 记录所有请求和响应的详细信息
 */
export class LogInterceptor implements HttpInterceptor {
  name = "LogInterceptor";
  priority = 1; // 优先级最高,最先执行

  onRequest(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig {
    this.logRequest(config);
    return config;
  }

  onRequestError(error: AxiosError): Promise<never> {
    this.logRequestError(error);
    return Promise.reject(error);
  }

  onResponse(response: AxiosResponse): AxiosResponse {
    this.logResponse(response);
    return response;
  }

  onResponseError(error: AxiosError): Promise<never> {
    this.logResponseError(error);
    return Promise.reject(error);
  }

  /**
   * 打印请求日志
   */
  private logRequest(config: InternalAxiosRequestConfig): void {
    const method: string = this.normalizeMethod(config.method);
    const url: string = this.buildRequestUrl(config);
    const params: string = this.safeStringify(config.params as Unknown, true);
    const data: string = this.safeStringify(config.data as Unknown, true);
    this.logInfoLines([
      "[Network] Request",
      `method=${method}`,
      `url=${url}`,
      `params=${params}`,
      `data=${data}`
    ]);
  }

  /**
   * 打印请求错误日志
   */
  private logRequestError(error: AxiosError): void {
    const config: InternalAxiosRequestConfig | undefined = error.config as InternalAxiosRequestConfig | undefined;
    //const config = (error as ESObject)?.config as InternalAxiosRequestConfig | undefined;
    const method: string = config ? this.normalizeMethod(config.method) : "UNKNOWN";
    const url: string = config ? this.buildRequestUrl(config) : "unknown";
    const message: string = error.message || "unknown";
    this.logErrorLines([
      "[Network] Request Error",
      `method=${method}`,
      `url=${url}`,
      `message=${message}`
    ]);
  }

  /**
   * 打印响应日志
   */
  private logResponse(response: AxiosResponse): void {
    const config: InternalAxiosRequestConfig = response.config as InternalAxiosRequestConfig;
    const url: string = this.buildRequestUrl(config);
    const status: string = String(response.status);
    const data: string = this.safeStringify(response.data as Unknown, true);
    this.logInfoLines([
      "[Network] Response",
      `status=${status}`,
      `url=${url}`,
      `data=${data}`
    ]);
  }

  /**
   * 打印响应错误日志
   */
  private logResponseError(error: AxiosError): void {
    const config: InternalAxiosRequestConfig | undefined = error.config as InternalAxiosRequestConfig | undefined;
    const response: AxiosResponse | undefined = error.response as AxiosResponse | undefined;
    const url: string = response ? this.buildRequestUrl(response.config as InternalAxiosRequestConfig)
      : (config ? this.buildRequestUrl(config) : "unknown");
    const status: string = response?.status !== undefined ? String(response.status) : "UNKNOWN";
    const data: string = response ? this.safeStringify(response.data as Unknown, true) : "";
    const message: string = error.message || "unknown";
    this.logErrorLines([
      "[Network] Response Error",
      `status=${status}`,
      `url=${url}`,
      `message=${message}`,
      `data=${data}`
    ]);
  }

  /**
   * 规范化请求方法
   */
  private normalizeMethod(method?: string): string {
    return method ? method.toUpperCase() : "UNKNOWN";
  }

  /**
   * 拼接请求地址
   */
  private buildRequestUrl(config: InternalAxiosRequestConfig): string {
    const baseUrl: string = config.baseURL ?? "";
    const url: string = config.url ?? "";
    if (url.startsWith("http://") || url.startsWith("https://")) {
      return url;
    }
    // 处理 baseURL 与 url 的拼接,避免双斜杠
    if (baseUrl.endsWith("/") && url.startsWith("/")) {
      return `${baseUrl}${url.slice(1)}`;
    }
    if (!baseUrl.endsWith("/") && url && !url.startsWith("/")) {
      return `${baseUrl}/${url}`;
    }
    return `${baseUrl}${url}`;
  }

  /**
   * 安全序列化
   */
  private safeStringify(value: Unknown, pretty: boolean = false): string {
    if (value === undefined) {
      return "undefined";
    }
    if (typeof value === "string") {
      return value;
    }
    try {
      const result: string | undefined = JSON.stringify(value, null, pretty ? 2 : undefined);
      return result !== undefined ? result : String(value);
    } catch (err) {
      return "[Unserializable]";
    }
  }

  /**
   * 输出 info 日志(支持多行与分段)
   */
  private logInfoLines(lines: string[]): void {
    this.logLines("info", lines);
  }

  /**
   * 输出 error 日志(支持多行与分段)
   */
  private logErrorLines(lines: string[]): void {
    this.logLines("error", lines);
  }

  /**
   * 按行与长度分割日志,避免单条日志过长被截断
   */
  private logLines(level: "info" | "error", lines: string[]): void {
    const logger: (content: string, tag: string) => void =
      level === "error" ? Logger.error : Logger.info;
    lines.forEach((line: string) => {
      this.splitLines(line).forEach((segment: string) => {
        this.splitChunks(segment, MAX_LOG_LENGTH).forEach((chunk: string) => {
          logger(chunk, TAG);
        });
      });
    });
  }

  /**
   * 按换行符拆分字符串
   */
  private splitLines(value: string): string[] {
    if (!value) {
      return [""];
    }
    return value.split("\n");
  }

  /**
   * 按最大长度拆分字符串
   */
  private splitChunks(value: string, maxLength: number): string[] {
    if (value.length <= maxLength) {
      return [value];
    }
    const result: string[] = [];
    for (let start: number = 0; start < value.length; start += maxLength) {
      result.push(value.slice(start, start + maxLength));
    }
    return result;
  }
}