import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { MatTooltipDefaultOptions, MAT_TOOLTIP_DEFAULT_OPTIONS } from '@angular/material/tooltip';
import { RouterHandler, RouterResponse } from 'src/app/shared/services/router-handler.service';
import { showWorkspaceTabObserver, sideMenuActiveObserver } from 'src/app/shared/shared-observers';

import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
import { Subject } from 'rxjs';
import { AccountService } from '../../shared/server-services/account.service';
import { AuthService } from '../../shared/server-services/auth.service';
import { DialogueService } from '../../shared/server-services/dialogue.service';
import { ID } from '../../shared/server-services/query-records/common-records';
import { RoomWithData } from '../../shared/server-services/query-records/room-records';
import {
  ResourceType,
  SidebarResource
} from '../../shared/server-services/query-records/sidebar-records';
import { RoomService } from '../../shared/server-services/room.service';
import { SidebarService } from '../../shared/server-services/sidebar.service';
import {
  SubscriptionServiceEvent,
  SubscriptionServiceEventType
} from '../../shared/server-services/subscription-event';
import { SubscriptionService } from '../../shared/server-services/subscription.service';

interface ExtendedSidebarResource extends SidebarResource {
  room?: RoomWithData;
  avatar?: Object; // AccountAvatarRecord;
  hasNewMessages?: Boolean;
  loaded?: Boolean;
}

const customToolTipOptions: MatTooltipDefaultOptions = {
  showDelay: 1000,
  hideDelay: 0,
  // touchGestures: 'auto',
  // position: 'before',
  touchendHideDelay: 0,
  disableTooltipInteractivity: true,
};

@Component({
  selector: 'app-news-list',
  templateUrl: './news-list.component.html',
  styleUrls: ['./news-list.component.scss'],
  providers: [{ provide: MAT_TOOLTIP_DEFAULT_OPTIONS, useValue: customToolTipOptions }],
})
export class NewsListComponent implements OnInit, OnDestroy, AfterViewInit {
  public newsList: ExtendedSidebarResource[] = [];
  public grantedRooms: ID[] = [];
  public rejectedRooms: ID[] = [];
  public ResourceType = ResourceType;
  public isMobile: boolean = false;

  @ViewChild('newsContainer') newsContainer: ElementRef;
  private destroyEvent = new Subject();
  public activeResourceId: ID = null;

  constructor(
    private sidebarService: SidebarService,
    private subscriptionService: SubscriptionService,
    private authService: AuthService,
    private roomService: RoomService,
    private accountService: AccountService,
    private dialogueService: DialogueService,
    private routerHandler: RouterHandler,
    private breakpointObserver: BreakpointObserver
  ) {
    this.subscriptionService.subscribeWorkspaceLoaded(this.onWorkspaceLoaded);

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.refreshNewsList
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.refreshNewsList
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.refreshNewsAndGrantedRooms
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.refreshNewsAndGrantedRooms
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.refreshNewsAndGrantedRooms
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.refreshNewsAndGrantedRooms
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.refreshNewsList
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_EVENT,
      SubscriptionServiceEventType.ALL,
      this.refreshNewsList
    );
    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.refreshNewsList
    );

    this.routerHandler.subscribeAll(this.setActiveResourceId);
    this.setActiveResourceId(this.routerHandler.getRoute());
  }

  setActiveResourceId = (route: RouterResponse) => {
    this.activeResourceId = route.params.id || null;
  };

  ngAfterViewInit(): void {
    // this feature breaks the touch pad scrolling
    /*fromEvent(this.newsContainer.nativeElement, 'wheel')
      .pipe(throttleTime(25), takeUntil(this.destroyEvent)) //throttle mouse wheels scrolls that are -too- fast
      .subscribe((event: WheelEvent) => {
        this.newsContainer.nativeElement.scrollBy({
          left: event.deltaY < 0 ? -56 : 56,
        });
      });*/
  }
  ngOnInit(): void {
    this.breakpointObserver
      .observe(['(max-width: 761px)']) //TODO this burnt-in variable is not optimal. Better would be to use global options. Best would be -> See issue #298
      .subscribe((state: BreakpointState) => {
        this.isMobile = state.matches;
      });
  }

  ngOnDestroy(): void {
    this.routerHandler.unsubscribeAll(this.setActiveResourceId);

    this.subscriptionService.unsubscribeWorkspaceLoaded(this.onWorkspaceLoaded);

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.refreshNewsList
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.refreshNewsList
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.refreshNewsAndGrantedRooms
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.refreshNewsAndGrantedRooms
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.refreshNewsAndGrantedRooms
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.refreshNewsAndGrantedRooms
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.refreshNewsList
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.DIALOGUE_EVENT,
      SubscriptionServiceEventType.ALL,
      this.refreshNewsList
    );
    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.DIALOGUE_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.refreshNewsList
    );

    this.destroyEvent.next();
  }

  public onMouseWheel(event: WheelEvent): void {
    if (event.deltaY === 0)
      //touchscroll
      return;

    var parent = <Element>event.target;
    while (parent) {
      if (parent.classList.contains('news-container')) {
        if (event.deltaY > 0) parent.scrollLeft += 25;
        else parent.scrollLeft -= 25;
        break;
      } else {
        parent = parent.parentElement;
      }
    }
    event.preventDefault();
  }

  private onWorkspaceLoaded = (res) => {
    if (!this.authService.isAnonym()) {
      this.refreshNewsAndGrantedRooms();
    }
  };

  // make a callback which can subscribe/unsubscribe to events
  private refreshNewsAndGrantedRooms = () => {
    return this.refreshGrantedRooms().then(() => {
      this.refreshNewsList();
    });
  };

  private refreshGrantedRooms = () => {
    return Promise.all([
      this.sidebarService.getGrantedRooms().then((rooms) => {
        this.grantedRooms = rooms;
      }),

      this.sidebarService.getRejectedRooms().then((rooms) => {
        this.rejectedRooms = rooms;
      }),
    ]);
  };

  /**
   * Refreshes state of newsList
   */
  private refreshNewsList = () => {
    this.sidebarService.getSidebarNews().then((news) => {
      for (const resource of <ExtendedSidebarResource[]>news.resources) {
        if (resource.type === ResourceType.DIALOGUE) {
          this.dialogueService.getData(resource.id).then((msg) => {
            resource.hasNewMessages =
              (msg.seenMessageId == null && msg.topMessageId !== null) ||
              msg.seenMessageId < msg.topMessageId;
          });
        }
        if (resource.type === ResourceType.ROOM) {
          this.roomService.getRoomRecord(resource.id).then((msg) => {
            if (msg.seenMessageId || msg.topMessageId) {
              resource.hasNewMessages =
                (msg.seenMessageId == null && msg.topMessageId !== null) ||
                msg.seenMessageId < msg.topMessageId;
            }
          });
        }
      }
      this.newsList = news.resources;
    });
  };

  // event on
  public visibilityChange = (event, resource) => {
    if (event) {
      this.loadData(resource);
    }
  };

  // singular resource (avatar) loader
  private loadData = (resource) => {
    const editableResource = this.newsList.find((singleNew) => singleNew.id === resource.id);
    if (editableResource?.loaded) {
      // skip
    } else {
      if (resource.type === ResourceType.ROOM) {
        this.roomService
          .getRoom(resource.id)
          .then((room) => {
            editableResource.room = room;
            // @todo
            // resource.posterId = room.posterId
          })
          .catch((err) => {
            console.warn('can not find room', err);
          });
      }
      if (resource.type === ResourceType.DIALOGUE) {
        this.accountService.getAvatar(resource.id).then((avatar) => {
          editableResource.avatar = avatar;
        });
      }
      editableResource.loaded = true;
    }
  };

  public navigateTo = (resource) => {
    const routeFragmentsArray = [
      'room',
      resource?.id,
      'chat',
      resource?.type === 'room' ? 'room' : 'private',
    ];
    const routeUrl = routeFragmentsArray.join('/');
    console.log('Trying to navigate to: ', routeUrl);
    this.routerHandler.navigate([routeUrl]);
    // closing sidebar if needed
    sideMenuActiveObserver.next(false);
    showWorkspaceTabObserver.next();
  };
}
