import {
  Component,
  ElementRef,
  Input,
  OnChanges,
  OnDestroy,
  OnInit,
  SimpleChanges,
  ViewChild,
} from '@angular/core';
import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { MatDialog } from '@angular/material/dialog';
import { MatMenuTrigger } from '@angular/material/menu';
import { marker } from '@biesbjerg/ngx-translate-extract-marker';
import { Subject, Subscription } from 'rxjs';
import { CreateRoomDialogComponent } from 'src/app/shared/dialogs/create-room-dialog/create-room-dialog.component';
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 { ID } from 'src/app/shared/server-services/query-records/common-records';
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 { RoomService } from 'src/app/shared/server-services/room.service';
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 { SnackBarService } from 'src/app/shared/services/snackbar.service';
import { OrderedList } from '../ordered-list';
import { BottomSheetGroupSelectorComponent } from './bottom-sheet-group-selector/bottom-sheet-group-selector.component';

export type SidebarResourceDragDroppedParam = {
  resource: SidebarResource;
  groupId: ID;
  pos: number;
};
export let sidebarResourceDragDropped: Subject<SidebarResourceDragDroppedParam> =
  new Subject<SidebarResourceDragDroppedParam>();

@Component({
  selector: 'app-sidebar-group',
  templateUrl: './sidebar-group.component.html',
  styleUrls: ['./sidebar-group.component.scss'],
})
export class SidebarGroupComponent implements OnInit, OnDestroy, OnChanges {
  @Input() sidebarGroupData: SidebarGroup;
  @Input() allClose?: boolean = false; // all group should be closed, when drag happened on group level

  @ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;

  public closed: boolean;
  public name: string;
  public error: boolean;
  public groupId: string;
  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 snackbarService: SnackBarService,
    private bottomSheet: MatBottomSheet,
    private roomService: RoomService
  ) {
    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.RESOURCE_GROUP_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onEditResourceGroup
    );

    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.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.getGroup(this.sidebarGroupData.id).then((group) => {
      this.closed = group.closed;
      this.groupId = group.id;
      this.resources = group.resources;

      if (group.decryptionError) {
        this.error = true;
        this.name = group.decryptionErrorMessage;
      } else {
        this.name = group.name;
      }
    });
  }

  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) {
    if (changes.allClose !== undefined) {
      this.updateContainerHeight();
    }
  }

  public onDragAutoOpenMove = (pos, comp) => {
    // handler, trigger when an element is dragged
    // auto open the group, when the mouse moved over this component
    // 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.trigger.closeMenu();
    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);
  }

  private onEditResourceGroup = (data: WorkspaceSubscriptionResourceGroupEventRecord) => {
    if (data.id == this.sidebarGroupData.id) {
      this.refreshState();
      this.updateContainerHeight();
    }
  };

  ngOnDestroy(): void {
    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.RESOURCE_GROUP_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onEditResourceGroup
    );

    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.dropSubscriptionRef.unsubscribe();
  }

  public toggle() {
    this.closed = !this.closed;
    this.updateContainerHeight();
    this.sidebarService.editGroupClosed(this.groupId, this.closed);
  }

  public openMenu(event: any) {
    event.stopPropagation();
    this.trigger.openMenu();
  }

  public editName() {
    const dialogRef = this.dialog.open(RenameGroupDialogComponent, {
      data: { name: this.name },
    });

    dialogRef.afterClosed().subscribe((result) => {
      if (result) {
        this.sidebarService
          .editGroupName(this.groupId, result)
          .then((res) => {
            // wait for async set
          })
          .catch((err) => {
            console.log('rename error', err);
            if (err.message === 'Message error: 33')
              this.snackbarService.showSnackbar(marker('Group name too long!'));
          });
      }
    });
  }

  public delete() {
    const dialogRef = this.dialog.open(DeleteGroupDialogComponent, {
      data: { name: this.name },
    });

    dialogRef.afterClosed().subscribe((result) => {
      if (result) {
        //console.log('client>server, delete group: id', id);
        return this.sidebarService.deleteGroup(this.sidebarGroupData.id).then((res) => {});
      }
    });
  }

  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
  ) => {
    if (event.groupId == this.groupId) {
      this.sidebarService.getResource(event.id).then((res) => {
        this.orderedResources.pushByOrdering(res);
        this.updateContainerHeight();
      });
    }
  };

  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 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();
  };

  public moveIntoOtherGroup(resource: SidebarResource) {
    console.log('SidebarGroupComponent', 'press', resource);
    var sheetRef = this.bottomSheet.open(BottomSheetGroupSelectorComponent, {
      data: {
        resource,
        group: this.sidebarGroupData,
      },
    });

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

  openNewRoomDialog(): void {
    const dialogRef = this.dialog.open(CreateRoomDialogComponent);

    dialogRef.afterClosed().subscribe((result) => {
      if (!result) return;

      this.roomService
        .newRoom(result)
        .then((newRoomId) => {
          this.sidebarService.getSidebarUngroupped().then((newUngroupped) => {
            const foundRoom = newUngroupped.find((room) => room.id === newRoomId);

            if (foundRoom) {
              let ordering = 0;
              if (this.resources.length) {
                ordering = this.orderedResources.calcOrderingByPosition(foundRoom, 0);
              }
              this.sidebarService
                .editPlacement(foundRoom.id, this.groupId, ordering, ResourceType.ROOM)
                .then((edited) => {
                  this.snackbarService.showSnackbar(marker('Room created successfully!'));
                  if (this.closed) {
                    this.toggle();
                  }
                });
            }
          });
        })
        .catch((error) => {
          if (error.message === 'Own room limit is reached (200)') {
            this.dialogService.openAlertDialog(
              marker('Room limit'),
              marker('Own room limit is reached.')
            );
          } else {
            this.dialogService.openAlertDialog(
              marker('Create room'),
              marker('Could not create room.')
            );
          }
          console.error('Room creation error', error);
        });
    });
  }
}
