import {
  Component,
  OnInit,
  Input,
  Output,
  EventEmitter,
  ViewChild,
  ContentChild,
  TemplateRef,
  AfterViewInit,
  ChangeDetectorRef,
  OnDestroy
} from '@angular/core';

import { ucapAnimations } from '@ucap-webmessenger/ui';

import { GroupDetailData, UserInfo } from '@ucap-webmessenger/protocol-sync';
import { MatTreeFlattener, MatTree } from '@angular/material';
import { ExpansionPanelItemDirective } from '../directives/expansion-panel-item.directive';
import {
  UserInfoSS,
  UserInfoF,
  UserInfoDN
} from '@ucap-webmessenger/protocol-query';
import { NGXLogger } from 'ngx-logger';
import { VirtualScrollTreeFlatDataSource } from '@ucap-webmessenger/ui';
import { FlatTreeControl } from '@angular/cdk/tree';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
import { Subscription } from 'rxjs';

enum NodeType {
  None = 'None',
  Favorit = 'Favorit',
  Profile = 'Profile',
  Buddy = 'Buddy',
  Default = 'Default',
  MyDept = 'MyDept'
}

interface GroupNode {
  nodeType: NodeType;
  userInfo?: UserInfo;
  groupDetail?: GroupDetailData;
  children?: GroupNode[];
}

interface FlatNode {
  expandable: boolean;
  level: number;
  nodeType: NodeType;
  countOfChildren?: number;
  userInfo?: UserInfo;
  groupDetail?: GroupDetailData;
}

@Component({
  selector: 'ucap-group-expansion-panel',
  templateUrl: './expansion-panel.component.html',
  styleUrls: ['./expansion-panel.component.scss'],
  animations: ucapAnimations
})
export class ExpansionPanelComponent
  implements OnInit, OnDestroy, AfterViewInit {
  @Input()
  set myProfileInfo(userInfo: UserInfo) {
    if (!userInfo) {
      return;
    }
    const groupNode: GroupNode = {
      nodeType: NodeType.Profile,
      userInfo,
      children: []
    };

    this.profileNodes = [groupNode];

    this.refreshRootNodeList();
  }

  @Input()
  set favoritBuddyList(userInfoList: UserInfo[]) {
    if (!userInfoList || 0 === userInfoList.length) {
      this.favoritNodes = [];
    } else {
      const groupNode: GroupNode = {
        nodeType: NodeType.Favorit,
        groupDetail: {
          seq: -9999,
          name: NodeType.Favorit,
          isActive: true,
          userSeqs: userInfoList.map(userInfo => userInfo.seq)
        } as GroupDetailData,
        children: []
      };

      userInfoList.forEach(userInfo => {
        groupNode.children.push({
          nodeType: NodeType.Favorit,
          userInfo
        });
      });

      this.favoritNodes = [groupNode];
    }
    this.refreshRootNodeList();
  }

  @Input()
  set groupBuddyList(
    list: { group: GroupDetailData; buddyList: UserInfo[] }[]
  ) {
    if (!list || 0 === list.length) {
      return;
    }

    this.groupList = list;

    this.buddyNodes = [];

    for (const item of list) {
      let nodeType = NodeType.Buddy;
      if (item.group.seq === 0) {
        nodeType = NodeType.Default;
      } else if (item.group.seq === -5) {
        nodeType = NodeType.MyDept;
      }

      const groupNode: GroupNode = {
        nodeType,
        groupDetail: item.group,
        children: []
      };

      item.buddyList.sort((a, b) =>
        a.order < b.order
          ? -1
          : a.order > b.order
          ? 1
          : a.name < b.name
          ? -1
          : a.name > b.name
          ? 1
          : 0
      );

      item.buddyList.forEach(userInfo => {
        groupNode.children.push({
          nodeType,
          groupDetail: item.group,
          userInfo
        });
      });

      this.buddyNodes.push(groupNode);
    }
    this.refreshRootNodeList();
  }

  @Input()
  checkable = false;
  @Input()
  /** 선택된 사용자의 리스트 */
  selectedUserList?: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[] = [];
  @Input()
  /** 선택할 수 없는 사용자 리스트 */
  ignoreUserList?: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[] = [];

  @Output()
  more = new EventEmitter<{
    event: MouseEvent;
    group: GroupDetailData;
  }>();

  @Output()
  checkGroup = new EventEmitter<{
    isChecked: boolean;
    groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
  }>();

  @ContentChild(ExpansionPanelItemDirective, {
    read: TemplateRef,
    static: false
  })
  expansionPanelItemTemplateRef: TemplateRef<ExpansionPanelItemDirective>;

  @ViewChild('groupTree', { static: false })
  groupTree: MatTree<FlatNode>;

  @ViewChild('cvsvGroup', { static: false })
  cvsvGroup: CdkVirtualScrollViewport;

  @ViewChild(PerfectScrollbarDirective, { static: false })
  psDirectiveRef?: PerfectScrollbarDirective;

  NodeType = NodeType;

  profileNodes: GroupNode[] = [];
  favoritNodes: GroupNode[] = [];
  buddyNodes: GroupNode[] = [];

  rootNodeList: GroupNode[] = [];
  treeControl: FlatTreeControl<FlatNode>;
  treeFlattener: MatTreeFlattener<GroupNode, FlatNode>;
  dataSource: VirtualScrollTreeFlatDataSource<GroupNode, FlatNode>;

  groupList: { group: GroupDetailData; buddyList: UserInfo[] }[];

  treeControlExpansionChangeSubscription: Subscription;

  constructor(
    private changeDetectorRef: ChangeDetectorRef,
    private logger: NGXLogger
  ) {
    this.treeControl = new FlatTreeControl<FlatNode>(
      node => node.level,
      node => node.expandable
    );

    this.treeFlattener = new MatTreeFlattener<GroupNode, FlatNode>(
      (node: GroupNode, level: number) => {
        return {
          expandable: !!node.children && node.children.length > 0,
          level,
          nodeType: node.nodeType,
          countOfChildren:
            !!node.children && node.children.length > 0
              ? node.children.length
              : 0,
          userInfo: node.userInfo,
          groupDetail: node.groupDetail
        };
      },
      node => node.level,
      node => node.expandable,
      node => node.children
    );

    this.dataSource = new VirtualScrollTreeFlatDataSource<GroupNode, FlatNode>(
      this.treeControl,
      this.treeFlattener
    );
  }

  ngOnInit() {
    this.treeControlExpansionChangeSubscription = this.treeControl.expansionModel.changed.subscribe(
      () => {
        this.psDirectiveRef.update();
      }
    );
  }

  ngOnDestroy(): void {
    if (!!this.treeControlExpansionChangeSubscription) {
      this.treeControlExpansionChangeSubscription.unsubscribe();
    }
  }

  ngAfterViewInit(): void {
    this.dataSource.cdkVirtualScrollViewport = this.cvsvGroup;
  }

  // hasChild = (_: number, node: FlatNode) => node.expandable;
  isHeader = (_: number, node: FlatNode) =>
    NodeType.Profile !== node.nodeType && 0 === node.level;

  expandMore() {
    this.groupTree.treeControl.expandAll();
    if (!!this.psDirectiveRef) {
      this.psDirectiveRef.scrollToTop();
    }
  }

  expandLess() {
    this.groupTree.treeControl.collapseAll();
    if (!!this.psDirectiveRef) {
      this.psDirectiveRef.scrollToTop();
    }
  }

  onClickMore(event: MouseEvent, group: GroupDetailData) {
    this.more.emit({ event, group });
  }

  /** 그룹리스트가 checkable 할 경우 checkbox 의 change 이벤트를 상위 컴포넌트로 전달한다. */
  onChangeCheck(value: boolean, group: GroupDetailData, nodeType: NodeType) {
    const groupInfos = this.groupList.filter(
      groupInfo => groupInfo.group.seq === group.seq
    );

    if (groupInfos.length > 0) {
      this.checkGroup.emit({
        isChecked: value,
        groupBuddyList: groupInfos[0]
      });
    }
  }

  getCheckableGroup(group: GroupDetailData) {
    if (!!this.ignoreUserList && this.ignoreUserList.length > 0) {
      let allExist = true;
      group.userSeqs.some(seq => {
        if (this.ignoreUserList.filter(item => item.seq === seq).length === 0) {
          allExist = false;
          return true;
        }
      });

      if (allExist) {
        return false;
      }
    }

    return true;
  }

  /** 그룹리스트가 checkable 할 경우 checkbox 의 isChecked 를 관장하며, 하위 부서원들의 전체선택여부를 판단한다. */
  getCheckedGroup(group: GroupDetailData) {
    if (!group || group === undefined) {
      return false;
    }

    if (group.userSeqs.length === 0) {
      return false;
    }

    if (!!this.selectedUserList && this.selectedUserList.length > 0) {
      let allExist = true;
      group.userSeqs.some(seq => {
        if (
          this.selectedUserList.filter(item => item.seq === seq).length === 0
        ) {
          allExist = false;
          return true;
        }
      });
      return allExist;
    }
    return false;
  }

  private refreshRootNodeList(): void {
    this.rootNodeList = [
      ...this.profileNodes,
      ...this.favoritNodes,
      ...this.buddyNodes
    ];
    this.dataSource.data = this.rootNodeList;

    if (!!this.psDirectiveRef) {
      this.psDirectiveRef.scrollToTop();
    }
  }
}
