import { Injectable, Injector, Inject } from "@angular/core";
import { Router } from "@angular/router";
import {
  HttpInterceptor,
  HttpRequest,
  HttpHandler,
  HttpErrorResponse,
  HttpEvent,
  HttpResponseBase,
  HttpResponse
} from "@angular/common/http";
import { Observable, of, throwError } from "rxjs";
import { mergeMap, catchError, map, tap } from "rxjs/operators";

import { DA_SERVICE_TOKEN, ITokenService } from "@delon/auth";
import { Toast } from "ng-zorro-antd-mobile";
import { environment } from "src/environments/environment";

export const CODEMESSAGE = {
  200: "服务器成功返回请求的数据。",
  201: "新建或修改数据成功。",
  202: "一个请求已经进入后台排队（异步任务）。",
  204: "删除数据成功。",
  400: "发出的请求有错误，服务器没有进行新建或修改数据的操作。",
  401: "用户没有权限（令牌、用户名、密码错误）。",
  403: "用户得到授权，但是访问是被禁止的。",
  404: "发出的请求针对的是不存在的记录，服务器没有进行操作。",
  406: "请求的格式不可得。",
  410: "请求的资源被永久删除，且不会再得到的。",
  422: "当创建一个对象时，发生一个验证错误。",
  500: "服务器发生错误，请检查服务器。",
  502: "网关错误。",
  503: "服务不可用，服务器暂时过载或维护。",
  504: "网关超时。"
};

/**
 * 默认HTTP拦截器，其注册细节见 `app.module.ts`
 */
@Injectable()
export class DefaultInterceptor implements HttpInterceptor {
  constructor(
    @Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
    private injector: Injector,
    private toast: Toast
  ) {}

  // private get notification(): NzNotificationService {
  //   return this.injector.get(NzNotificationService);
  // }

  private goTo(url: string) {
    setTimeout(() => this.injector.get(Router).navigateByUrl(url));
  }

  private checkStatus(ev: HttpResponseBase) {
    if ((ev.status >= 200 && ev.status < 300) || ev.status === 401) {
      return;
    }

    const errortext = CODEMESSAGE[ev.status] || ev.statusText;
    // this.notification.error(`请求错误 ${ev.status}: ${ev.url}`, errortext);
  }

  private handleData(ev: HttpResponseBase): Observable<any> {
    // 可能会因为 `throw` 导出无法执行 `_HttpClient` 的 `end()` 操作
    console.log(ev);
    if (ev.status > 0) {
      // this.injector.get(_HttpClient).end();
    }
    this.checkStatus(ev);
    // 业务处理：一些通用操作
    switch (ev.status) {
      case 200:
        // 业务层级错误处理，以下是假定restful有一套统一输出格式（指不管成功与否都有相应的数据格式）情况下进行处理
        // 例如响应内容：
        //  错误内容：{ errorcode: -1, msg: '非法参数',success:false,body:{} }
        //  正确内容：{ errorcode: -1, msg: '合法参数',success:true,body:{ }
        // 则以下代码片断可直接适用
        const body: any = ev["body"];

        // 重新修改 `body` 内容为 `response` 内容，对于绝大多数场景已经无须再关心业务状态码
        return of(new HttpResponse(Object.assign(ev, { body: body })));
        // 或者依然保持完整的格式
        // return of(event);

        break;
      case 401:
        // this.notification.error(`未登录或登录已过期，请重新登录。`, ``);
        // 清空 token 信息
        (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).clear();
        this.goTo("/passport/login");
        break;
      case 403:
      case 404:
      case 500:
        this.goTo(`/exception/${ev.status}`);
        break;
      default:
        if (ev instanceof HttpErrorResponse) {
          console.warn(
            "未可知错误，大部分是由于后端不支持CORS或无效配置引起",
            ev
          );
          // return throwError(ev);
          this.goTo("/passport/login");
        }
        break;
    }
    return of(ev);
  }

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    // 统一加上服务端前缀
    let url = req.url;
    if (!url.startsWith("https://") && !url.startsWith("http://")) {
      url = environment.SERVER_URL + url;
      const api = /asset\/a\/[^(login)].*/g;
      if (api.test(url)) {
        var query = url.split("?");
        url = query[0];
        url += `;JSESSIONID=${
          this.tokenService.get().token
        }?_ajax=true&mobileLogin=true`;
        if (query.length >= 2) {
          url += "&" + query[1];
        }
      }
    }

    const newReq = req.clone({ url });
    return next.handle(newReq).pipe(
      mergeMap((event: any) => {
        // 允许统一对请求错误处理
        if (event instanceof HttpResponseBase) {
          Toast.loading("loading", 200, () => {
            return this.handleData(event);
          });
        }
        // 若一切都正常，则后续操作
        return of(event);
      }),
      catchError((err: HttpErrorResponse) => this.handleData(err))
    );
  }
}
