import { Injectable, Injector, OnDestroy, Optional } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { IBreadcrumb } from './layout.type';

export const LAYOUT = 'layout';
interface IOptions {
  breadFn?: (items: IBreadcrumb[]) => IBreadcrumb[];
}

const uniqueElementsBy = (breads: IBreadcrumb[], fn: (bread1: IBreadcrumb, bread2: IBreadcrumb) => boolean) =>
  breads.reduce((result: IBreadcrumb[], v: IBreadcrumb) => {
    if (!result.some((x: IBreadcrumb) => fn(v, x))) { // 判断是否重复
      result.push(v);
    }
    return result;
  }, []);

@Injectable({ providedIn: 'root' })
export class BixiLayoutService implements OnDestroy {
  openChange = new BehaviorSubject<boolean>(true);
  breadcrumb$ = new BehaviorSubject<IBreadcrumb[]>([]);
  menuVisible$ = new BehaviorSubject<boolean>(true);
  headerVisible$ = new BehaviorSubject<boolean>(true);

  // 这里记录路由切换时，组件加载时，传入的参数
  options$ = new BehaviorSubject<IOptions>({});
  routerChange = new Subscription();

  constructor(
    private injector: Injector,
    private router: Router,
    private title: Title,
    @Optional() private i18n: TranslateService
  ) {
    this.routerChange = this.router.events
      .pipe(filter((e) => e instanceof NavigationEnd))
      .subscribe(() => {
        this.updateBreads();
      });
  }

  private updateBreads() {
    let route = this.injector.get(ActivatedRoute);
    let titles: IBreadcrumb[] = [];
    while (route.firstChild) {
      route = route.firstChild;
      const data = (route.snapshot && route.snapshot.data) || {};
      if (data.breadcrumbs) {
        if (typeof data.breadcrumbs === 'function') {
          const breadcrumbs = data.breadcrumbs(route);
          titles = [...titles, ...breadcrumbs];
        } else if (Array.isArray(data.breadcrumbs)) {
          titles = [...titles, ...data.breadcrumbs];
        } else {
          console.warn('route breadcrumbs 只支持 Array 和 function, 请查看路由配置');
        }

        titles = titles.map((bread: IBreadcrumb) => {
          if (bread.titleI18n) {
            return {
              name: this.i18n.instant(bread.titleI18n),
              link: bread.link || bread.path
            };
          }
          return bread;
        });
      } else if (data.title || data.titleI18n) {
        if (this.i18n) {
          titles.push({
            name: data.title ? data.title : this.i18n.instant(data.titleI18n),
            link: data.path
          });
        } else {
          titles.push({
            name: data.title,
            link: data.path
          });
        }

      } else {
        // nothing
      }
    }
    this.breadcrumb$.next(uniqueElementsBy(this.formatBreakcrumb(titles), (bread1: IBreadcrumb, bread2: IBreadcrumb) => {
      return bread1.name === bread2.name && bread1.path === bread2.path && bread1.link === bread2.link;
    }));
    this.options$.next({});
  }

  toggleMenu(open?: boolean) {
    if (open === undefined) {
      this.openChange.next(!this.openChange.value);
    } else {
      this.openChange.next(!!open);
    }
  }

  toggleHeader(show?: boolean) {
    if (show === undefined) {
      this.headerVisible$.next(!this.headerVisible$.value);
    } else {
      this.headerVisible$.next(!!show);
    }
  }

  private formatBreakcrumb(breads: IBreadcrumb[]) {
    const options = this.options$.value;
    if (typeof options.breadFn === 'function') {
      return options.breadFn(breads);
    }
    return breads;
  }

  setBreadcrumbs(breadFn: (items: IBreadcrumb[]) => IBreadcrumb[]) {
    // 由于 router 变化钩子函数在组件的 constructor 之后执行，这里为了保证 setBreadcrumbs 的时效性，使用定时器
    setTimeout(() => {
      this.options$.next({
        ...this.options$.value,
        breadFn
      });
      this.updateBreads();
    });
  }

  private getByRoute(): string {
    let next = this.injector.get(ActivatedRoute);
    while (next.firstChild) next = next.firstChild;
    const data = (next.snapshot && next.snapshot.data) || {};
    if (typeof data.breadcrumbs === 'function') {
      const breadcrumbs = data.breadcrumbs(next);
      data.title = breadcrumbs && breadcrumbs.length ? breadcrumbs[breadcrumbs.length - 1].name : '';
    }
    if (data.titleI18n && this.i18n && !data.title) {
      data.title = this.i18n.instant(data.titleI18n);
    }
    return data.title;
  }

  setPageTitle(titleFn?: (breads: IBreadcrumb[]) => string) {
    if (typeof titleFn === 'function') {
      this.title.setTitle(titleFn(this.breadcrumb$.value));
      return;
    }
    const title = this.getByRoute();
    if (title) {
      this.title.setTitle(`${this.getByRoute()}`);
    } else {
      this.title.setTitle('Bixi');
    }
  }

  ngOnDestroy(): void {
    this.routerChange.unsubscribe();
  }
}
