import {
  HttpBackend,
  HttpErrorResponse,
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
  HttpResponse,
  HttpResponseBase,
  HTTP_INTERCEPTORS
} from '@angular/common/http';
import { Injectable, Injector } from '@angular/core';
import { Observable, of, throwError } from 'rxjs';
import { delay } from 'rxjs/operators';
import { MOCK_CONFIG } from './mock-config.token';
import { MOCK_DATA_DEFAULT, MOCK_DEFULAT_CONFIG } from './mock.config';
import { MockService } from './mock.service';
import { IMockConfig, IMockRequest, ISafeAny } from './mock.type';

class HttpMockInterceptorHandler implements HttpHandler {
  constructor(private next: HttpHandler, private interceptor: HttpInterceptor) { }

  handle(req: HttpRequest<ISafeAny>): Observable<HttpEvent<ISafeAny>> {
    return this.interceptor.intercept(req, this.next);
  }
}

@Injectable()
export class MockInterceptor implements HttpInterceptor {
  config: IMockConfig;
  constructor(private injector: Injector) {
    const injectConfig: IMockConfig = this.injector.get(MOCK_CONFIG);
    this.config = {
      ...MOCK_DEFULAT_CONFIG,
      ...injectConfig
    };
  }

  intercept(req: HttpRequest<ISafeAny>, next: HttpHandler): Observable<HttpEvent<ISafeAny>> {
    const mockService = this.injector.get(MockService);
    const result = mockService.getCollect(req.method, req.url);
    if (!result) {
      // 跳出拦截器
      return next.handle(req);
    }
    let res: ISafeAny;
    let resultValue = result.value;
    if (typeof resultValue === 'function') {
      resultValue = {
        value: resultValue
      };
    }
    const mockValue = {
      ...MOCK_DATA_DEFAULT,
      ...resultValue
    };

    if (mockValue.disable) {
      return next.handle(req);
    }

    switch (typeof mockValue.value) {
      case 'function':
        const mockReq: IMockRequest = {
          params: result?.params,
          query: result?.query,
          _httpReq: req
        };
        try {
          res = mockValue.value.call(this, mockReq);
        } catch (e) {
          const { status, error } = e;
          res = new HttpErrorResponse({
            url: req.url,
            status,
            error
          });
        }
        break;
      default:
        res = mockValue?.value;
    }

    if (this.config.interceptors?.response) {
      res = this.config.interceptors.response(res);
    }

    if (!(res instanceof HttpResponseBase)) {
      res = new HttpResponse({
        status: 200,
        url: req.url,
        body: res
      });
    }

    if (this.config.log) {
      console.log(`[@bixi/mock]%c${req.method}->${req.urlWithParams}->request`, 'background:#107cee;color:#000', req);
      console.log(`[@bixi/mock]%c${req.method}->${req.urlWithParams}->response`, 'background:#107cee;color:#000', res);
    }

    const res$ = res instanceof HttpErrorResponse ? throwError(res) : of(res);
    if (!this.config.ignoreOtherInterceptors) {
      const interceptors = this.injector.get(HTTP_INTERCEPTORS, []);
      const nextInterceptors = interceptors.slice(interceptors.indexOf(this) + 1);
      if (nextInterceptors.length > 0) {
        return nextInterceptors.reduceRight(
          // 依次执行剩余拦截器
          (_next: HttpHandler, _interceptor: HttpInterceptor) => new HttpMockInterceptorHandler(_next, _interceptor)
          , { handle: () => res$ } as HttpBackend).handle(req).pipe(delay(mockValue.delay || this.config.delay || 300));
      }
    }
    return res$.pipe(delay(mockValue.delay || this.config.delay || 300));
  }
}
