import { ValidationController } from 'aurelia-validation';
import { Router } from 'aurelia-router';
import * as breeze from 'breeze-client';
import { autoinject, bindable, Binding, BindingEngine, bindingMode, computedFrom, Container, containerless, createOverrideContext, observable, processContent, TaskQueue, View, ViewCompiler, ViewResources, ViewSlot } from "aurelia-framework";
import { UIInternal } from "../../../utils/ui-internal";
import { UIColumn } from "./ui-column";
import { BindingSignaler } from 'aurelia-templating-resources';
import { UITableAction } from './ui-table-action';
import { IMenuItem } from "../../menu/ui-menus";


declare var tableCellsRenderingTime;
@containerless()
@autoinject()
@processContent((compiler, resources, node, instruction) => {
  instruction.inheritBindingContext = true;
  return true;
})

export class BodyCell {
  @bindable({ defaultBindingMode: bindingMode.toView })
  public column: UIColumn | UITableAction;
  @bindable({ defaultBindingMode: bindingMode.toView })
  public indexDrag: number;

  @bindable()
  public onCellClicked?: (obj: { record: KeyValue }) => void;
  @bindable()
  public rowRoute?: string | ((obj: { record: KeyValue }) => string);

  protected el: HTMLElement;
  protected overrideContext: any;
  @bindable()
  public currentRecord: any;
  @bindable
  public render: (cell: any) => {};
  @bindable()
  public tableAuTargetId: number;
  @bindable()
  public colIndex: number;
  @bindable()
  public focusData: { data: any, colIndex: number } | null = null;

  @bindable public rowIndex: number;

  @bindable()
  public onCellSelected: (obj: object) => boolean;


  @observable
  private route: string = "test";
  private hasChangesSubscriptionKey: number;
  private view: View;
  private isRendered: boolean = false;

  public menuItemsBodyCell: IMenuItem[] = [];
  slot: ViewSlot;
  activeField: EventTarget;
  private _title: string;

  constructor(private router: Router, private signaler: BindingSignaler, private validationController: ValidationController, private bindingEngine: BindingEngine) {
  }

  public async bind(bindingContext: Object, overrideContext?: any) {
    this.overrideContext = overrideContext;
    UIInternal.subscribe(UIInternal.EVT_CHANGE_LANGUAGE, () => {
      this.setTitle();
    });
  }

  @computedFrom("route", "onCellClicked", "column.isRowClickIgnore")
  public get isClickable(): boolean {
    return this.route != null || (this.onCellClicked != null && !this.column.isRowClickIgnore);
  }

  @computedFrom("_title")
  public get title() {
    return this._title;
  }

  public setTitle() {
    this._title = '';
    if ((this.view as any)?.lastChild != null) {
      let lastChild: HTMLElement = (this.view as any)?.lastChild;
      if (lastChild.nodeName == "#text") {
        this._title = lastChild.textContent?.trim() ?? '';
      } else if (lastChild.nodeName == "#comment") {
        this._title = (lastChild.previousElementSibling as HTMLElement)?.innerText?.trim() ?? '';
      } else if (lastChild.nodeName == "UI-SELECT") {
        this._title = lastChild.getElementsByTagName("select")?.[0].innerText?.trim() ?? '';
      } else if (lastChild.nodeName == "UI-INPUT" || lastChild.nodeName == "UI-DATE-PICKER") {
        this._title = lastChild.getElementsByTagName("input")?.[0].value?.trim() ?? '';
      } else {
        this._title = lastChild.innerText?.trim() ?? '';
      }
    }
  }

  public isActionColumn() {
    return this.column instanceof UITableAction || this.column.type == "action";
  }

  public async indexDragChanged(newValue, oldValue) {
    if (newValue != oldValue) {
      this.debounceCompile();
    }
  }

  protected async attached() {
    if (this.el) {
      this.slot = new ViewSlot(this.el.firstElementChild || this.el, true);
      UIInternal.subscribe(UIInternal.EVT_UI_TABLE_BODY_CELL_CHANGED + `_${this.tableAuTargetId}`, () => {
        this.tryCompile();
      });
      await this.processCurrentRecord();
      this.tryCompile();
    }
  }

  protected detached() {
    if (this.hasChangesSubscriptionKey != null) {
      (this.currentRecord as breeze.Entity).entityAspect.propertyChanged.unsubscribe(this.hasChangesSubscriptionKey);
    }
  }

  protected async internalOnCellClicked($event: MouseEvent) {
    if ($event.target.nodeName == "A" && $event.target.hasAttribute("href")) {
      this.internalOnLinkClick($event, ($event.target as HTMLAnchorElement).href);
    } else if (this.isRowClickAllowed($event.target)) {
      if (this.route != null) {
        this.internalOnLinkClick($event, this.route);
      }
      else {
        if (this.onCellClicked != null && !this.column.isRowClickIgnore) {
          await this.onCellClicked({ record: this.currentRecord });
        }
      }
    }
  }

  private internalOnLinkClick($event: MouseEvent, route: string) {
    if ($event.ctrlKey) {
      window.open(route, "_blank");
    }
    else {
      this.router.navigate(route);
    }
  }

  private rowClickIgnoreElements = ["A", "BUTTON", "SELECT", "INPUT", "UI-SELECT"];
  protected isRowClickAllowed(target: HTMLElement) {
    return target == this.el || target == this.el.parentElement || (this.rowClickIgnoreElements.includes((this.view as any).firstChild.nodeName) == false && this.rowClickIgnoreElements.includes(target.nodeName) == false);
  }

  async currentRecordChanged(newValue, oldValue) {
    if (newValue == null) return;
    await this.processCurrentRecord();
    this.debounceCompile(true);
  }

  tableAuTargetIdChanged() {
    UIInternal.subscribe(UIInternal.EVT_UI_TABLE_BODY_CELL_CHANGED + `_${this.tableAuTargetId}`, () => {
      this.tryCompile();
      this.signaler.signal('refresh_cell');
    });

  }

  debounceCompile = UIInternal.debounce((newVersion = false) => {
    this.tryCompile(newVersion);
  }, 50);

  tryCompile(newVersion = false) {
    UIInternal.queueMicroTask(() => {
      this.compileCell(this.el, newVersion);
      this.validateCurrentRecord();
    });
  }

  validateCurrentRecord() {
    if (this.currentRecord == null) return;
    UIInternal.nextTick(() => {
      const toValidate = {
        object: this.currentRecord
      }
      if (this.column.validatePropertyName?.length > 0) {
        Object.assign(toValidate, { propertyName: this.column.validatePropertyName });
      }
      this.validationController.validate(toValidate);
    });
  }

  compileCell(el: Element, newVersion = false) {
    if (this.currentRecord == null) {
      if (this.render) {
        this.render(this);
      }
      return;
    }
    if (!newVersion) {
      this.cleanUp();
    }
    if (this.cellDisabled != null && this.column instanceof UIColumn && this.column.type == 'checkbox') {
      this.currentRecord.cellDisabled = this.cellDisabled(this.currentRecord);
    }
    if (el) {
      if (this.column instanceof UITableAction) {
        this.compileActions();
      }
      if (this.column.templateCell == null) throw new Error("Template cell is missing");
      const tpl = this.column.templateCell.outerHTML;
      if (this.overrideContext.parentOverrideContext?.parentOverrideContext?.__parentOverrideContext != null) {
        if (!newVersion || !this.isRendered) {
          this.column.compileCell();
          this.view = this.column.view;
          this.slot.add(this.column.view);
        } else {
          this.slot.unbind();
        }
        this.slot.bind(this, createOverrideContext(this.currentRecord, this.overrideContext.parentOverrideContext.parentOverrideContext.__parentOverrideContext));
        if (!newVersion || !this.isRendered) {
          this.slot.attached();
        }
        if (this.render) {
          this.render(this);
        }
        if (this.focusData != null && this.focusData.colIndex == this.colIndex && this.focusData.data == this.currentRecord) {
          const input = (this.slot as { anchor: HTMLElement } & ViewSlot).anchor.querySelector('input');
          if (input != null) {
            input.focus();
          }
        }
        this.isRendered = true;
      }
    }
    this.setTitle();
    return;
  }

  setActiveField() {
    if (this.isRendered === false) {
      return;
    }
    this.focusData = { data: this.currentRecord, colIndex: this.colIndex };
  }

  private cleanUp(): void {
    if (this.isRendered === true) {
      try {
        this.slot.detached();
      } catch (e) { }
      try {
        this.slot.unbind();
      } catch (e) { }
      try {
        this.slot.removeAll();
      } catch (e) { }

      this.isRendered = false;
    }
  }

  compileActions() {
    this.menuItemsBodyCell.splice(0);
    this.menuItemsBodyCell.push(...(this.column as UITableAction).compileTemplate(this.currentRecord));
  }

  async processCurrentRecord() {
    if (this.currentRecord != null) {
      if (isFunction(this.rowRoute)) {
        if (this.currentRecord.entityAspect?.entityState?.isAdded() ?? false) {
          this.hasChangesSubscriptionKey = (this.currentRecord as breeze.Entity).entityAspect.propertyChanged?.subscribe(async (data) => {
            if (data.propertyName == "id") {
              this.route = await (this.rowRoute as Function)({ record: this.currentRecord });
              (this.currentRecord as breeze.Entity).entityAspect.propertyChanged?.unsubscribe(this.hasChangesSubscriptionKey);
              this.hasChangesSubscriptionKey = null;
            }
            if (data.propertyName != null) {
              this.signaler.signal('refresh_cell');
            }
          });
        }
        else {
          this.route = await this.rowRoute({ record: this.currentRecord });
        }
      }
      else {
        this.route = this.rowRoute;
      }
      (this.currentRecord as breeze.Entity).entityAspect?.propertyChanged?.subscribe(data => {
        if (data.propertyName != null) {
          this.signaler.signal('refresh_cell');
        }
      });
    }
  }

  cellDisabled(record) {
    return this.onCellSelected != null ? this.onCellSelected({ record }) == false : false;
  }
}
