import {
  Component,
  ElementRef,
  Input,
  OnChanges,
  OnDestroy,
  OnInit,
  SimpleChanges,
  ViewChild,
} from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatMenuTrigger } from '@angular/material/menu';
import { marker } from '@biesbjerg/ngx-translate-extract-marker';
import { DeleteGroupDialogComponent } from 'src/app/shared/dialogs/delete-group-dialog/delete-group-dialog.component';
import { RenameGroupDialogComponent } from 'src/app/shared/dialogs/rename-group-dialog/rename-group-dialog.component';
import { AppDragDropService, AppDragMoveEvent } from 'src/app/shared/drag/app-drag-drop.service';
import {
  ResourceType,
  SidebarGroup,
  SidebarResource,
  UNGROUPPED_GROUP_ID,
} from 'src/app/shared/server-services/query-records/sidebar-records';
import {
  WorkspaceSubscriptionDialogueEventRecord,
  WorkspaceSubscriptionResourceGroupEventRecord,
  WorkspaceSubscriptionRoomEventRecord,
} from 'src/app/shared/server-services/query-records/workspace-records';
import { SidebarService } from 'src/app/shared/server-services/sidebar.service';
import {
  SubscriptionServiceEvent,
  SubscriptionServiceEventType,
} from 'src/app/shared/server-services/subscription-event';
import { SubscriptionService } from 'src/app/shared/server-services/subscription.service';
import { DialogService } from 'src/app/shared/services/dialog.service';
import { OrderedList } from '../ordered-list';
import { Subject, Subscription } from 'rxjs';
import { sidebarResourceDragDropped } from '../sidebar-group/sidebar-group.component';
import { BottomSheetGroupSelectorComponent } from '../sidebar-group/bottom-sheet-group-selector/bottom-sheet-group-selector.component';
import { MatBottomSheet } from '@angular/material/bottom-sheet';

@Component({
  selector: 'app-sidebar-ungroupped',
  templateUrl: './sidebar-ungroupped.component.html',
  styleUrls: ['./sidebar-ungroupped.component.scss'],
})
export class SidebarUngrouppedComponent implements OnInit, OnDestroy, OnChanges {
  @Input() allClose?: boolean = false; // all group should be closed, when drag happened on group level
  @Input() ungroupedResources: SidebarResource[]; // detect this change to refresh the ungroup from your workspace after dc/reconnect

  public closed: boolean;
  public groupId: string = UNGROUPPED_GROUP_ID;
  public resources: SidebarResource[];
  // resource data structure
  public orderedResources: OrderedList<SidebarResource>;
  // reference of the showed list
  public showResourceList: SidebarResource[] = [];

  private dropSubscriptionRef: Subscription;

  public isDraggedCursorOverThis = false;
  public currHeight = 0;

  @ViewChild('group', { read: ElementRef }) group: any;

  constructor(
    private subscriptionService: SubscriptionService,
    public dialog: MatDialog,
    private sidebarService: SidebarService,
    private dialogService: DialogService,
    private el: ElementRef,
    private appDragDropService: AppDragDropService,
    private bottomSheet: MatBottomSheet
  ) {
    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateResource
    );

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

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

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateResource
    );

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

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

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteResource
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.RESOURCE_GROUP_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteGroup
    );

    this.dropSubscriptionRef = sidebarResourceDragDropped.subscribe((ev) => {
      if (ev.groupId == this.groupId) {
        let ordering = this.orderedResources.calcOrderingByPosition(ev.resource, ev.pos);
        this.sidebarService.editPlacement(ev.resource.id, this.groupId, ordering, ev.resource.type);
      }

      // TODO optimize ordering numbers
    });
  }

  /**
   * Note: This called on the original parent group, we have to pass the event to the
   * destionation group (or ungroupped group)
   */
  public resourceDragEnded(ev: AppDragMoveEvent, resource: SidebarResource) {
    if (ev.containerId && ev.containerId.startsWith('group-')) {
      let destGroupId = ev.containerId.replace('group-', '');
      if (destGroupId == '') {
        destGroupId = UNGROUPPED_GROUP_ID;
      }

      sidebarResourceDragDropped.next({
        groupId: destGroupId,
        pos: ev.pos,
        resource,
      });
    } else {
      this.dialogService.openAlertDialog(
        marker('Error'),
        marker('Error during the operation'),
        "Error: container id should start with 'group-', found " + ev.containerId
      );
    }
  }

  private refreshState(): Promise<void> {
    return this.sidebarService.getSidebarUngroupped().then((resources) => {
      this.resources = resources;
    });
  }

  ngOnInit(): void {
    this.refreshState().then(() => {
      this.orderedResources = new OrderedList<SidebarResource>(this.resources);
      this.showResourceList = this.orderedResources.getInorderedDataRef();

      this.updateContainerHeight();

      this.appDragDropService.subscribeDragMove(this.onDragAutoOpenMove);
    });
  }

  ngOnChanges(changes: SimpleChanges): void {
    console.log('changes ungr', changes);
    if (changes.ungroupedResources) {
      this.refreshState().then(() => {
        console.log('okkk');
        this.orderedResources = new OrderedList<SidebarResource>(this.resources);
        this.showResourceList = this.orderedResources.getInorderedDataRef();

        this.updateContainerHeight();
      });
    }
  }

  // handler, trigger when an element is dragged
  // auto open the group, when the mouse moved over this component
  public onDragAutoOpenMove = (pos, comp) => {
    // close the submenu, you can drag the component and open the submenu in the same time on mobile, when you use longpress on the title

    this.updateContainerHeight();
    if (!this.closed) return; // we already opened

    if (this.checkCursorIsOverThis(pos)) {
      this.isDraggedCursorOverThis = true;

      // if we are still waiting over the element after x sec, lets open this
      setTimeout(() => {
        if (this.isDraggedCursorOverThis) {
          this.closed = false;
          this.isDraggedCursorOverThis = false;
          this.sidebarService.editGroupClosed(this.groupId, this.closed); // emit on server side
        }
      }, 500);
    } else {
      this.isDraggedCursorOverThis = false;
    }
  };

  private checkCursorIsOverThis(pos) {
    let bond = this.el.nativeElement.getBoundingClientRect();
    return (
      pos.x >= bond.left &&
      pos.y > bond.top &&
      pos.x <= bond.left + bond.width &&
      pos.y <= bond.top + bond.height
    );
  }

  private updateContainerHeight() {
    setTimeout(() => {
      // call this async, so it can be called immediatly after data structure change
      if (!this.closed && !this.allClose) {
        this.currHeight =
          48 +
          Math.max(
            24,
            this.group.nativeElement.querySelector('.group-body').getBoundingClientRect().height
          );
      } else {
        this.currHeight = 24;
      }

      this.group.nativeElement.style.height = this.currHeight + 'px';
    }, 1);
  }

  ngOnDestroy(): void {
    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateResource
    );

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

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

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.DIALOGUE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateResource
    );

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

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

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.DIALOGUE_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteResource
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.RESOURCE_GROUP_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteGroup
    );

    this.dropSubscriptionRef.unsubscribe();
  }

  public toggle() {
    this.closed = !this.closed;
    this.updateContainerHeight();
  }

  private onModifyDialogue = (event: WorkspaceSubscriptionDialogueEventRecord) => {
    if (event.hidden === true) {
      // remove it if it is in here
      this.orderedResources.remove(event.id);
      this.updateContainerHeight();
    } else if (event.hidden === false) {
      this.sidebarService.getResource(event.id).then((res) => {
        if (res.groupId === this.groupId) {
          this.orderedResources.pushByOrdering(res);
          this.updateContainerHeight();
        }
      });
    }
  };

  private onCreateResource = (
    event: WorkspaceSubscriptionRoomEventRecord | WorkspaceSubscriptionDialogueEventRecord
  ) => {
    this.sidebarService.getResource(event.id).then((res) => {
      this.orderedResources.pushByOrdering(res);
      setTimeout(() => {
        // let the child element initialize
        this.updateContainerHeight();
      }, 100);
    });
  };

  private onEditResource = (
    event: WorkspaceSubscriptionRoomEventRecord | WorkspaceSubscriptionDialogueEventRecord
  ) => {
    if (event.ordering !== undefined) {
      // null means ungrouped
      this.sidebarService.getResource(event.id).then((res) => {
        let includes = this.orderedResources.includes(event.id);

        if (includes) {
          if (this.groupId == event.groupId) {
            // the references ordering attr changed via cache service
            // but it stays in the same group
            this.orderedResources.reorder();
            if (OrderedList.isPositionNeedToOptimize(event.ordering)) {
              this.optimizeOrdering();
            }
          } else {
            // group changed
            this.orderedResources.remove(event.id);
            this.updateContainerHeight();
          }
        } else {
          if (this.groupId == event.groupId) {
            // got a new element
            this.orderedResources.pushByOrdering(res);
            if (OrderedList.isPositionNeedToOptimize(event.ordering)) {
              this.optimizeOrdering();
            }
            this.updateContainerHeight();
          } else {
            // skip
          }
        }
      });
    }
  };

  private optimizeOrdering() {
    let optimize = this.orderedResources.getOptimizedOrdering();

    console.log('optimize ungroupped resources', optimize);
    optimize.forEach((el) => {
      this.sidebarService.editPlacement(el.element.id, this.groupId, el.ordering, el.element.type);
    });
  }

  private onDeleteResource = (
    event: WorkspaceSubscriptionRoomEventRecord | WorkspaceSubscriptionDialogueEventRecord
  ) => {
    this.orderedResources.remove(event.id);
    this.updateContainerHeight();
  };

  private onDeleteGroup = (event: WorkspaceSubscriptionResourceGroupEventRecord) => {
    this.refreshState().then(() => {
      this.orderedResources = new OrderedList<SidebarResource>(this.resources);
      this.showResourceList = this.orderedResources.getInorderedDataRef();

      this.updateContainerHeight();
    });
  };

  public moveIntoOtherGroup(resource: SidebarResource) {
    var sheetRef = this.bottomSheet.open(BottomSheetGroupSelectorComponent, {
      data: {
        resource,
        group: UNGROUPPED_GROUP_ID,
      },
    });

    sheetRef.afterDismissed().subscribe((result: { groupId: string }) => {
      result && this.sidebarService.editPlacement(resource.id, result.groupId, 0, resource.type);
    });
  }
}
