import { ChangeDetectorRef, Component, Input, NgZone, OnDestroy, OnInit, TrackByFunction } from '@angular/core';
import { IContainerModel } from '@fleio-api/openstack/object-store/model/container.model';
import { ObjectsApiService } from '@fleio-api/openstack/object-store/objects/objects-api.service';
import { FleioObjectsList } from '@fleio-api/fleio-objects-list';
import { IObjectModel } from '@fleio-api/openstack/object-store/model/object.model';
import { MatTableDataSource } from '@angular/material/table';
import { ConfigService } from '@shared/config/config.service';
import { RefreshTimer } from '@shared/ui-api/helpers/refresh-timer';
import { IIcon } from '@shared/ui/common/interfaces/icon';
import { IAction } from '@objects-view/interfaces/actions/action';
import { RouterLinkAction } from '@objects-view/actions/router-link-action';
import { ActivatedRoute, Router } from '@angular/router';
import { FormatHelper } from '@shared/ui-api/helpers/format-helper';
import { StaticObjectController } from '@objects-view/static-object-controller';
import { ObjectDetailsOverviewComponent } from '../../tabs/object-details-overview/object-details-overview.component';
import { MatDialog } from '@angular/material/dialog';
import { CallbackAction } from '@objects-view/actions/callback-action';
import { catchError, map } from 'rxjs/operators';
import { IActionResult } from '@objects-view/interfaces/actions/action-result';
import { Observable, of } from 'rxjs';
import {
  ObjectDetailsTabDialogComponent
} from '@objects-view/object-details-tab-dialog/object-details-tab-dialog.component';

@Component({
  selector: 'app-container-objects-manager',
  templateUrl: './container-objects-manager.component.html',
  styleUrls: ['./container-objects-manager.component.scss'],
  providers: [ObjectsApiService],
})
export class ContainerObjectsManagerComponent implements OnInit, OnDestroy {
  @Input() container: IContainerModel;
  objectList: FleioObjectsList<IObjectModel>;
  displayedColumns = ['(image)', 'name', 'size', 'last_modified', '(actions)'];
  dataSource: MatTableDataSource<IObjectModel>;
  prefix = '';
  objectMeta: {
    [key: string]: {
      icon: IIcon;
      actions: IAction[];
    };
  };
  formatHelper = new FormatHelper();
  breadcrumbs: {
    display: string;
    path: string;
  }[];
  private refreshTimer: RefreshTimer;
  trackByFunction: TrackByFunction<IObjectModel> = (_index, obj: IObjectModel) => obj.id;

  constructor(
    private objectsApi: ObjectsApiService,
    public config: ConfigService,
    private ngZone: NgZone,
    private changeDetectorRef: ChangeDetectorRef,
    private router: Router,
    private activatedRoute: ActivatedRoute,
    private matDialog: MatDialog,
  ) {
  }

  ngOnInit(): void {
    if (this.container) {
      const additionalQueryParams = Object.assign(
        {}, {container: this.container.id}, this.activatedRoute.snapshot.queryParams,
      );
      this.objectsApi.setAdditionalQueryParams(additionalQueryParams);
      this.activatedRoute.queryParams.subscribe(() => this.refreshObjects());
      this.refreshTimer = new RefreshTimer(this.config.getDefaultRefreshInterval(true), () => {
        this.refreshObjects();
      }, this.config, this.ngZone, this.changeDetectorRef, true, true, true);
    }
  }

  ngOnDestroy() {
    if (this.refreshTimer) {
      this.refreshTimer.stop();
    }
  }

  refreshObjects() {
    this.prefix = this.activatedRoute.snapshot.queryParams.path || '';
    this.updateBreadcrumbs();
    this.objectsApi.list({prefix: this.prefix}).subscribe((objectList) => {
        this.objectList = objectList;
        this.objectMeta = {};

        for (const obj of this.objectList.objects) {
          this.objectMeta[obj.id] = {
            icon: obj.is_folder ? {name: 'folder'} : {name: 'description'},
            actions: this.getObjectActions(obj),
          };
        }
      }
    );
  }

  getRegionParam(): string {
    if (this.activatedRoute.snapshot.queryParams?.region) {
      return `&region=${this.activatedRoute.snapshot.queryParams.region}`;
    } else {
      return '';
    }
  }

  getObjectActions(obj: IObjectModel):
    IAction[] {
    const actions: IAction[] = [];
    if (obj.is_folder) {
      actions.push(new CallbackAction(
        {
          object: obj,
          icon: {name: 'delete'},
          tooltip: $localize`Delete folder`,
          name: $localize`Delete`,
          displayMessages: false,
          refreshAfterExecute: false,
          confirmOptions: {
            confirm: true,
            title: $localize`Delete folder '${obj.name}'`,
            message: $localize`Are you sure you want to delete folder '${obj.name}'?`,
            importantMessage: $localize`All files and sub-folders will also be deleted.`,
            yesButtonName: $localize`Delete folder`,
            noButtonName: $localize`Cancel`,
          },
          callback: () => {
            return this.objectsApi.delete(obj.id).pipe(map(() => {
                this.refreshObjects();
                return {success: true, message: 'Folder deleted'};
              }), catchError(() => {
                return of({success: false, message: 'Failed to delete folder, please contact support'});
              })
            );
          }
        }
      ));
    } else {
      actions.push(new CallbackAction(
        {
          object: obj,
          icon: {name: 'cloud_download'},
          tooltip: $localize`Download file`,
          name: $localize`Download`,
          callback: () => {
            this.config.authService.generateOtpToken().subscribe(token => {
              const downloadUrl = this.config.getPanelApiUrl('openstack/object-store-objects/download') +
                `?fleio-token=${token}&container=${this.container.id}&object_id=${obj.id}` + this.getRegionParam();
              window.location.href = downloadUrl;
            });
            return of(null);
          }
        }
      ));
      actions.push(new CallbackAction(
        {
          name: $localize`View details`,
          icon: {name: 'info'},
          callback: () => this.objectDetails(obj),
        }
      ));
      let editUrl = this.config.getPanelUrl(`openstack/object-store/${this.container.id}/object-upload`);
      editUrl += `?path=${this.prefix}`;
      editUrl += `&fileName=${obj.name}` + this.getRegionParam();
      actions.push(new RouterLinkAction({
          icon: {name: 'edit'},
          name: $localize`Edit`,
          tooltip: $localize`Edit file`,
          routerUrl: editUrl,
          router: this.router,
        }
      ));
      let copyUrl = this.config.getPanelUrl(`openstack/object-store/${this.container.id}/object-copy`);
      copyUrl += `?fileId=${obj.id}` + this.getRegionParam();
      actions.push(new RouterLinkAction({
          icon: {name: 'content_copy'},
          name: $localize`Copy`,
          tooltip: $localize`Copy file`,
          routerUrl: copyUrl,
          router: this.router,
        }
      ));
      actions.push(new CallbackAction(
        {
          object: obj,
          icon: {name: 'delete'},
          tooltip: $localize`Delete file`,
          name: $localize`Delete`,
          refreshAfterExecute: false,
          displayMessages: true,
          confirmOptions: {
            confirm: true,
            title: $localize`Delete file ${obj.name}`,
            message: $localize`Are you sure you want to delete file '${obj.name}'?`,
            yesButtonName: $localize`Delete file`,
            noButtonName: $localize`Cancel`,
          },
          callback: () => {
            return this.objectsApi.delete(obj.id).pipe(map(() => {
                this.refreshObjects();
                return {success: true, message: 'File deleted'};
              }), catchError(() => {
                return of({success: false, message: 'Failed to delete file, please contact support'});
              })
            );
          }
        }
      ));
    }

    return actions;
  }

  expandFolder(obj: IObjectModel) {
    this.ngZone.run(() => {
      this.objectList = null;
      this.router.navigate(
        [], {queryParams: {path: obj.id}, queryParamsHandling: 'merge', relativeTo: this.activatedRoute},
      ).then(() => {
      });
    });
  }

  breadCrumbNavigation(path?: string) {
    this.ngZone.run(() => {
      this.objectList = null;
      this.router.navigate(
        [], {queryParams: {path}, queryParamsHandling: 'merge', relativeTo: this.activatedRoute},
      ).then(() => {
      });
    });
  }

  private updateBreadcrumbs() {
    const prefixParts = this.prefix.split('/');
    let path = '';
    this.breadcrumbs = [];
    if (this.prefix.length > 0) {
      for (const prefixPart of prefixParts) {
        if (prefixPart.length > 0) {
          path += prefixPart + '/';
          this.breadcrumbs.push({
            display: prefixPart,
            path,
          });
        }
      }
      delete this.breadcrumbs[prefixParts.length - 2].path;
    }
  }

  objectDetails(obj: IObjectModel): Observable<IActionResult> {
    const objectController = new StaticObjectController(
      {
        tabs: [
          {
            tabName: $localize`Overview`,
            tabIdentifier: 'overview',
            component: ObjectDetailsOverviewComponent,
          },
        ],
      },
      obj,
      {
        container: this.container
      },
    );

    return this.matDialog.open(
      ObjectDetailsTabDialogComponent, {
        data: {
          dialogTitle: $localize`Object details`,
          objectController,
          additionalClasses: [],
        },
        autoFocus: false,
      }).afterClosed().pipe(map(() => null));
  }

  getQueryParams(): {
    [key: string]: any
  } {
    const queryParams: {
      [key: string]: any
    } = {};
    if (this.prefix) {
      queryParams.path = this.prefix;
    }

    if (this.activatedRoute.snapshot.queryParams?.region) {
      queryParams.region = this.activatedRoute.snapshot.queryParams.region;
    }

    return queryParams;
  }

}
