import { css, CSSResult, html, PropertyValues, TemplateResult } from "lit";
import { property, state } from "lit/decorators.js";
import { live } from "lit/directives/live.js";
import { DataEntity, DataNamespace, EventUtils } from "@api-client/core/build/browser.js";
import ApiElement from "../ApiElement.js";
import EditorCommon from './CommonStyles.js';
import theme from '../theme.js';
import typography from "../../styles/m3/typography.module.js";
import type Input from "../../ui/input/Input.js";
import type CheckboxElement from "../../ui/input/CheckboxElement.js";
import '../../define/ui/ui-text-field.js';
import '../../define/ui/ui-checkbox.js';
import '../../define/ui/ui-divider.js';
import '../../define/ui/ui-icon-button.js';
import '../../define/ui/ui-icon.js';

/**
 * An editor for a data entity.
 * 
 * @fires change - When the entity has changed.
 * @fires namechange - When a name of the entity has changed.
 */
export default class EntityFormElement extends ApiElement {
  static override get styles(): CSSResult[] {
    return [
      theme,
      EditorCommon,
      typography,
      css`
      :host {
        display: block;
      }

      .no-info {
        margin: 12px 20px;
      }

      .parents-list {
        margin: 0;
        padding: 0;
      }

      .parent-item {
        list-style: none;
        height: var(--list-item-height, 40px);
        display: flex;
        align-items: center;
        padding: 0 20px;
      }

      .parent-item.two-line {
        height: var(--list-item-two-line-height);
      }

      .parent-item-wrapper {
        flex: 1;
      }
      `
    ]
  }

  /**
   * The key of the entity to edit.
   * @attribute
   */
  @property({ type: String, reflect: true }) key?: string;

  /**
   * The read data namespace.
   */
  @property({ type: Object }) root?: DataNamespace;

  /**
   * When set it activates the drop zone in the parents list to support drag and drop.
   * @attribute
   */
  @property({ type: Boolean }) parentDropZone?: boolean;

  /**
   * The computed entity when the key or root change.
   */
  @state() protected _entity?: DataEntity;

  protected override willUpdate(cp: PropertyValues<this>): void {
    if (cp.has('key')) {
      this._computeEntity();
    }
  }

  /**
   * Adds a parent entity to this entity.
   * @param parentKey The key of the parent to add.
   */
  addParent(parentKey: string): void {
    const { key, _entity } = this;
    if (!parentKey || parentKey === key || !_entity) {
      return;
    }
    if (!_entity.parents.includes(parentKey)) {
      _entity.parents.push(parentKey);
    }
    this.notifyChange();
  }

  /**
   * Computes the view value for the property.
   * This should be called before the update is complete so this won't trigger another update.
   */
  protected _computeEntity(): void {
    const { root, key } = this;
    if (!root || !key) {
      this._entity = undefined;
      return;
    }
    this._entity = root.definitions.entities.find(i => i.key === key);
  }

  protected _infoChangeHandler(e: Event): void {
    const { _entity } = this;
    if (!_entity) {
      return;
    }
    const node = e.target as Input;
    const { name, value } = node;
    if (!['name', 'displayName', 'description'].includes(name as string)) {
      return;
    }
    if (name === 'name' && !value) {
      return;
    }
    _entity.info[name as 'name' | 'description' | 'displayName'] = value;
    this.notifyChange();
    if (name === 'name' || name === 'displayName') {
      this.dispatchEvent(new Event('namechange'));
    }
  }

  protected _checkedHandler(e: Event): void {
    const { _entity } = this;
    if (!_entity) {
      return;
    }
    const input = e.target as CheckboxElement;
    const name = input.name as 'deprecated';
    const { checked } = input;
    if (_entity[name] === checked) {
      return;
    }
    _entity[name] = checked;
    this.notifyChange();
    this.requestUpdate();
  }

  protected _dragoverHandler(e: DragEvent): void {
    const { dataTransfer } = e;
    if (!dataTransfer) {
      return;
    }
    e.preventDefault();
    e.preventDefault();
    dataTransfer.dropEffect = "copy";
  }

  protected _dropHandler(e: DragEvent): void {
    const { dataTransfer } = e;
    if (!dataTransfer) {
      return;
    }

    const node = e.target as HTMLElement;
    const { type } = node.dataset;
    if (type === 'entity-parent') {
      e.preventDefault();
      this.parentDropZone = false;
      const key = dataTransfer.getData('text/key');
      this.addParent(key);
    }
  }

  protected _parentRemoveHandler(e: Event): void {
    const node = e.currentTarget as HTMLElement;
    if (node.dataset.key) {
      this.removeParent(node.dataset.key);
    }
  }

  /**
   * Removes a parent with the given key.
   * @param key The key of the parent to remove.
   */
  removeParent(key: string): void {
    const { _entity } = this;
    if (!key || !_entity) {
      return;
    }
    const index = _entity.parents.indexOf(key);
    if (index < 0) {
      return;
    }
    _entity.parents.splice(index, 1);
    this.notifyChange();
    this.requestUpdate();
  }

  protected override render(): TemplateResult {
    const { _entity: item } = this;
    if (!item) {
      return html``;
    }

    const { info, deprecated = false } = item;
    return html`
    <form name="entity" @submit="${EventUtils.cancelEvent}" data-key="${item.key}">
      <ui-text-field class="input" name="name" .value="${live(info.name || '')}" label="Entity name" required @change="${this._infoChangeHandler}"></ui-text-field>
      <ui-text-field class="input" name="displayName" .value="${live(info.displayName || '')}" label="Display name (optional)" @change="${this._infoChangeHandler}"></ui-text-field>
      <ui-text-field class="input" name="description" .value="${live(info.description || '')}" label="Entity description (optional)" @change="${this._infoChangeHandler}"></ui-text-field>

      <div class="checkbox-group">
        <label class="label-large">
          <ui-checkbox name="deprecated" .checked="${deprecated}" title="Makes this entity deprecated." @change="${this._checkedHandler}"></ui-checkbox>
          Deprecated
        </label>
      </div>

      ${this._parentsEditorTemplate(item)}
    </form>
    `
  }

  protected _parentsEditorTemplate(entity: DataEntity): TemplateResult {
    return html`
    <ui-divider type="full"></ui-divider>
    <div class="editor-title title-medium">Parents</div>
    ${this.parentDropZone ? this._parentDropZoneTemplate() : this._parentsTemplate(entity)}
    `;
  }

  protected _parentDropZoneTemplate(): TemplateResult {
    return html`<div 
      class="drop-zone" 
      data-type="entity-parent" 
      @dragover="${this._dragoverHandler}" 
      @drop="${this._dropHandler}">
      Drop a parent entity here.
    </div>`;
  }

  protected _parentsTemplate(entity: DataEntity): TemplateResult {
    const parents = entity.getComputedParents();
    return html`
    ${parents.length ? this._parentsListTemplate(parents) : this.renderEmptyParentsMessage()}
    `;
  }

  protected renderEmptyParentsMessage(): TemplateResult {
    return html`
      <p class="no-description no-info body-medium">No parent entities. You can drop an entity here to create an association.</p>
    `;
  }

  protected _parentsListTemplate(parents: DataEntity[]): TemplateResult {
    return html`
    <ul class="parents-list">
      ${parents.map(p => this._parentItemTemplate(p))}
    </ul>
    `;
  }

  protected _parentItemTemplate(item: DataEntity): TemplateResult {
    const parent = item.getParent();
    const label = item.info.name || item.key;
    return html`
    <li class="parent-item${parent ? ' two-line' : ''}" data-key="${item.key}">
      <div class="parent-item-wrapper">
        <span class="parent-label">${label}</span>
        ${parent ? html`<div class="secondary">From data model: ${parent.info.name}</div>` : ''}
      </div>
      <ui-icon-button aria-label="Remove this parent" title="Removes this parent" data-key="${item.key}" @click="${this._parentRemoveHandler}">
        <ui-icon icon="deleteOutline" role="presentation"></ui-icon>
      </ui-icon-button>
    </li>
    `;
  }
}
