import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { demoFunc } from './dees-table.demo.js';
import {
  customElement,
  html,
  DeesElement,
  property,
  type TemplateResult,
  cssManager,
  css,
  unsafeCSS,
  type CSSResult,
  state,
  resolveExec,
} from '@design.estate/dees-element';

import { DeesContextmenu } from './dees-contextmenu.js';

import * as domtools from '@design.estate/dees-domtools';
import { type TIconKey } from './dees-icon.js';

declare global {
  interface HTMLElementTagNameMap {
    'dees-table': DeesTable<any>;
  }
}

// interfaces
export interface ITableAction<T = any> {
  name: string;
  iconName: TIconKey;
  /**
   * the table behaviour to use for this action
   * e.g. upload: allows to upload files to the table
   */
  useTableBehaviour?: 'upload' | 'cancelUpload' | 'none';
  /**
   * the type of the action
   */
  type: (
    | 'inRow'
    | 'contextmenu'
    | 'doubleClick'
    | 'footer'
    | 'header'
    | 'preview'
    | 'keyCombination'
  )[];
  /**
   * allows to check if the action is relevant for the given item
   * @param itemArg
   * @returns
   */
  actionRelevancyCheckFunc?: (itemArg: T) => boolean;
  /**
   * the actual action function implementation
   * @param itemArg
   * @returns
   */
  actionFunc: (actionDataArg: ITableActionDataArg<T>) => Promise<any>;
}

export interface ITableActionDataArg<T> {
  item: T;
  table: DeesTable<T>;
}

export type TDisplayFunction<T = any> = (itemArg: T) => object;

// the table implementation
@customElement('dees-table')
export class DeesTable<T> extends DeesElement {
  public static demo = demoFunc;

  // INSTANCE
  @property({
    type: String,
  })
  public heading1: string = 'heading 1';

  @property({
    type: String,
  })
  public heading2: string = 'heading 2';

  @property({
    type: Array,
  })
  public data: T[] = [];

  // dees-form compatibility -----------------------------------------
  @property({
    type: String,
  })
  public key: string;

  @property({
    type: String,
  })
  public label: string;

  @property({
    type: Boolean,
  })
  public disabled: boolean = false;

  @property({
    type: Boolean,
  })
  public required: boolean = false;

  get value() {
    return this.data;
  }
  set value(valueArg) {}
  public changeSubject = new domtools.plugins.smartrx.rxjs.Subject<DeesTable<T>>();
  // end dees-form compatibility -----------------------------------------

  /**
   * What does a row of data represent?
   */
  @property({
    type: String,
    reflect: true,
  })
  public dataName: string;


  @property({
    type: Boolean,
  })
  searchable: boolean = true;

  @property({
    type: Array,
  })
  public dataActions: ITableAction<T>[] = [];

  @property({
    attribute: false,
  })
  public displayFunction: TDisplayFunction = (itemArg: T) => itemArg as any;

  @property({
    attribute: false,
  })
  public reverseDisplayFunction: (itemArg: any) => T = (itemArg: any) => itemArg as T;

  @property({
    type: Object,
  })
  public selectedDataRow: T;

  @property({
    type: Array,
  })
  public editableFields: string[] = [];

  public files: File[] = [];
  public fileWeakMap = new WeakMap();

  public dataChangeSubject = new domtools.plugins.smartrx.rxjs.Subject();

  constructor() {
    super();
  }

  public static styles = [
    cssManager.defaultStyles,
    css`
      .mainbox {
        color: ${cssManager.bdTheme('#333', '#fff')};
        font-family: 'Geist Sans', sans-serif;
        font-weight: 400;
        font-size: 14px;
        padding: 16px;
        display: block;
        width: 100%;
        min-height: 50px;
        background: ${cssManager.bdTheme('#ffffff', '#222222')};
        border-radius: 3px;
        border-top: 1px solid ${cssManager.bdTheme('#fff', '#ffffff10')};
        box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3);
        overflow-x: auto;
        cursor: default;
      }

      .header {
        display: flex;
        justify-content: flex-end;
        align-items: center;
        font-family: 'Geist Sans', sans-serif;
      }

      .headingContainer {
      }

      .heading {
      }

      .heading1 {
        font-weight: 600;
      }
      .heading2 {
        opacity: 0.6;
      }

      .headingSeparation {
        margin-top: 7px;
        border-bottom: 1px solid ${cssManager.bdTheme('#bcbcbc', '#444444')};
      }

      .headerActions {
        user-select: none;
        display: flex;
        flex-direction: row;
        margin-left: auto;
      }
      .headerAction {
        display: flex;
        flex-direction: row;
        color: ${cssManager.bdTheme('#333', '#ccc')};
        margin-left: 16px;
      }

      .headerAction:hover {
        color: ${cssManager.bdTheme('#555', '#fff')};
      }

      .headerAction dees-icon {
        margin-right: 8px;
      }

      .searchGrid {
        background: ${cssManager.bdTheme('#fff', '#111111')};
        display: grid;
        grid-gap: 16px;
        grid-template-columns: 1fr 200px;
        margin-top: 16px;
        padding: 0px 16px;
        border-top: 1px solid ${cssManager.bdTheme('#fff', '#ffffff20')};
        border-radius: 8px;
      }

      .searchGrid.hidden {
        height: 0px;
        opacity: 0;
        overflow: hidden;
        margin-top: 0px;
      }

      table,
      .noDataSet {
        margin-top: 16px;
        color: ${cssManager.bdTheme('#333', '#fff')};
        border-collapse: collapse;
        width: 100%;
      }
      .noDataSet {
        text-align: center;
      }
      tr {
        border-bottom: 1px dashed ${cssManager.bdTheme('#999', '#808080')};
        text-align: left;
      }
      tr:last-child {
        border-bottom: none;
        text-align: left;
      }
      tr:hover {
      }
      tr:hover td {
        background: ${cssManager.bdTheme('#22222210', '#ffffff10')};
      }
      tr:first-child:hover {
        cursor: auto;
      }
      tr:first-child:hover .innerCellContainer {
        background: none;
      }
      tr.selected td {
        background: ${cssManager.bdTheme('#22222220', '#ffffff20')};
      }

      tr.hasAttachment td {
        background: ${cssManager.bdTheme('#0098847c', '#0098847c')};
      }

      th {
        text-transform: none;
        font-family: 'Geist Sans', sans-serif;
        font-weight: 500;
      }
      th,
      td {
        position: relative;
        vertical-align: top;

        padding: 0px;
        border-right: 1px dashed ${cssManager.bdTheme('#999', '#808080')};
      }
      .innerCellContainer {
        min-height: 36px;
        position: relative;
        height: 100%;
        width: 100%;
        padding: 6px 8px;
        line-height: 24px;
      }
      th:first-child .innerCellContainer,
      td:first-child .innerCellContainer {
        padding-left: 0px;
      }
      th:last-child .innerCellContainer,
      td:last-child .innerCellContainer {
        padding-right: 0px;
      }
      th:last-child,
      td:last-child {
        border-right: none;
      }
      td input {
        width: 100%;
        height: 100%;
        outline: none;
        border: 2px solid #fa6101;
        top: 0px;
        bottom: 0px;
        right: 0px;
        left: 0px;
        position: absolute;
        background: #fa610140;
        color: ${cssManager.bdTheme('#333', '#fff')};
        font-family: inherit;
        font-size: inherit;
        font-weight: inherit;
        padding: 0px 6px;
      }
      .actionsContainer {
        display: flex;
        flex-direction: row;
        height: 24px;
        transform: translateY(-4px);
        margin-left: -6px;
      }
      .action {
        position: relative;
        padding: 8px 10px;
        line-height: 24px;
        height: 32px;
        size: 16px;
        border-radius: 8px;
      }

      .action:hover {
        background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blue)};
      }

      .action:active {
        background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blueActive)};
      }

      .action:hover dees-icon {
        filter: ${cssManager.bdTheme('invert(1) brightness(3)', '')};
      }

      .footer {
        font-family: 'Geist Sans', sans-serif;
        font-size: 14px;
        color: ${cssManager.bdTheme('#111', '#ffffff90')};
        background: ${cssManager.bdTheme('#eeeeeb', '#00000050')};
        margin: 16px -16px -16px -16px;
        border-bottom-left-radius: 3px;
        border-bottom-right-radius: 3px;
        display: flex;
      }

      .tableStatistics {
        padding: 8px 16px;
      }

      .footerActions {
        margin-left: auto;
      }

      .footerActions .footerAction {
        padding: 8px 16px;
        display: flex;
        user-select: none;
      }

      .footerActions .footerAction:hover {
        background: ${cssManager.bdTheme(colors.bright.blue, colors.dark.blue)};
        color: #fff;
      }

      .footerActions .footerAction dees-icon {
        display: flex;
        margin-right: 8px;
      }

      .footerActions .footerAction:hover dees-icon {
      }
    `,
  ];

  public render(): TemplateResult {
    return html`
      <div class="mainbox">
        <!-- the heading part -->
        <div class="header">
          <div class="headingContainer">
            <div class="heading heading1">${this.label || this.heading1}</div>
            <div class="heading heading2">${this.heading2}</div>
          </div>
          <div class="headerActions">
            ${resolveExec(async () => {
              const resultArray: TemplateResult[] = [];
              for (const action of this.dataActions) {
                if (!action.type.includes('header')) continue;
                resultArray.push(
                  html`<div
                    class="headerAction"
                    @click=${() => {
                      action.actionFunc({
                        item: this.selectedDataRow,
                        table: this,
                      });
                    }}
                  >
                    ${action.iconName
                      ? html`<dees-icon .iconSize=${14} .iconFA=${action.iconName}></dees-icon>
                          ${action.name}`
                      : action.name}
                  </div>`
                );
              }
              return resultArray;
            })}
          </div>
        </div>
        <div class="headingSeparation"></div>
        <div class="searchGrid hidden">
          <dees-input-text
            .label=${'lucene syntax search'}
            .description=${`
              You can use the lucene syntax to search for data, e.g.:
              
              \`\`\`
              name: "john" AND age: 18
              \`\`\`
              
            `}
          ></dees-input-text>
          <dees-input-multitoggle
            .label=${'search mode'}
            .options=${['table', 'data', 'server']}
            .selectedOption=${'table'}
            .description=${`
              There are three basic modes:
              
              * table: only searches data already in the table
              * data: searches original data, ignoring table transforms
              * server: searches data on the server
              
            `}
          ></dees-input-multitoggle>
        </div>

        <!-- the actual table -->
        <style></style>
        ${this.data.length > 0
          ? (() => {
              // Only pick up the keys from the first transformed data object
              // as all data objects are assumed to have the same structure
              const firstTransformedItem = this.displayFunction(this.data[0]);
              const headings: string[] = Object.keys(firstTransformedItem);
              return html`
                <table>
                  <tr>
                    ${headings.map(
                      (headingArg) => html`
                        <th>
                          <div class="innerCellContainer">${headingArg}</div>
                        </th>
                      `
                    )}
                    ${(() => {
                      if (this.dataActions && this.dataActions.length > 0) {
                        return html`
                          <th>
                            <div class="innerCellContainer">Actions</div>
                          </th>
                        `;
                      }
                    })()}
                  </tr>
                  ${this.data.map((itemArg) => {
                    const transformedItem = this.displayFunction(itemArg);
                    const getTr = (elementArg: HTMLElement): HTMLElement => {
                      if (elementArg.tagName === 'TR') {
                        return elementArg;
                      } else {
                        return getTr(elementArg.parentElement);
                      }
                    };
                    return html`
                      <tr
                        @click=${() => {
                          this.selectedDataRow = itemArg;
                        }}
                        @dragenter=${async (eventArg: DragEvent) => {
                          eventArg.preventDefault();
                          eventArg.stopPropagation();
                          const realTarget = getTr(eventArg.target as HTMLElement);
                          console.log('dragenter');
                          console.log(realTarget);
                          setTimeout(() => {
                            realTarget.classList.add('hasAttachment');
                          }, 0);
                        }}
                        @dragleave=${async (eventArg: DragEvent) => {
                          eventArg.preventDefault();
                          eventArg.stopPropagation();
                          const realTarget = getTr(eventArg.target as HTMLElement);
                          realTarget.classList.remove('hasAttachment');
                        }}
                        @dragover=${async (eventArg: DragEvent) => {
                          eventArg.preventDefault();
                        }}
                        @drop=${async (eventArg: DragEvent) => {
                          eventArg.preventDefault();
                          const newFiles = [];
                          for (const file of Array.from(eventArg.dataTransfer.files)) {
                            this.files.push(file);
                            newFiles.push(file);
                            this.requestUpdate();
                          }
                          const result: File[] = this.fileWeakMap.get(itemArg as object);
                          if (!result) {
                            this.fileWeakMap.set(itemArg as object, newFiles);
                          } else {
                            result.push(...newFiles);
                          }
                        }}
                        @contextmenu=${async (eventArg: MouseEvent) => {
                          DeesContextmenu.openContextMenuWithOptions(
                            eventArg,
                            this.getActionsForType('contextmenu').map((action) => {
                              const menuItem: plugins.tsclass.website.IMenuItem = {
                                name: action.name,
                                iconName: action.iconName as any,
                                action: async () => {
                                  await action.actionFunc({
                                    item: itemArg,
                                    table: this,
                                  });
                                  return null;
                                },
                              };
                              return menuItem;
                            })
                          );
                        }}
                        class="${itemArg === this.selectedDataRow ? 'selected' : ''}"
                      >
                        ${headings.map(
                          (headingArg) => html`
                            <td
                              @dblclick=${(e: Event) => {
                                if (this.editableFields.includes(headingArg)) {
                                  this.handleCellEditing(e, itemArg, headingArg);
                                } else {
                                  const wantedAction = this.dataActions.find((actionArg) =>
                                    actionArg.type.includes('doubleClick')
                                  );
                                  if (wantedAction) {
                                    wantedAction.actionFunc({
                                      item: itemArg,
                                      table: this,
                                    });
                                  }
                                }
                              }}
                            >
                              <div class="innerCellContainer">${transformedItem[headingArg]}</div>
                            </td>
                          `
                        )}
                        ${(() => {
                          if (this.dataActions && this.dataActions.length > 0) {
                            return html`
                              <td>
                                <div class="innerCellContainer">
                                  <div class="actionsContainer">
                                    ${this.getActionsForType('inRow').map(
                                      (actionArg) => html`
                                        <div
                                          class="action"
                                          @click=${() =>
                                            actionArg.actionFunc({
                                              item: itemArg,
                                              table: this,
                                            })}
                                        >
                                          ${actionArg.iconName
                                            ? html`
                                                <dees-icon
                                                  .iconFA=${actionArg.iconName}
                                                ></dees-icon>
                                              `
                                            : actionArg.name}
                                        </div>
                                      `
                                    )}
                                  </div>
                                </div>
                              </td>
                            `;
                          }
                        })()}
                      </tr>
                    `;
                  })}
                </table>
              `;
            })()
          : html` <div class="noDataSet">No data set!</div> `}
        <div class="footer">
          <div class="tableStatistics">
            ${this.data.length} ${this.dataName || 'data rows'} (total) |
            ${this.selectedDataRow ? '# ' + `${this.data.indexOf(this.selectedDataRow) + 1}` : `No`}
            selected
          </div>
          <div class="footerActions">
            ${resolveExec(async () => {
              const resultArray: TemplateResult[] = [];
              for (const action of this.dataActions) {
                if (!action.type.includes('footer')) continue;
                resultArray.push(
                  html`<div
                    class="footerAction"
                    @click=${() => {
                      action.actionFunc({
                        item: this.selectedDataRow,
                        table: this,
                      });
                    }}
                  >
                    ${action.iconName
                      ? html`<dees-icon .iconSize=${14} .iconFA=${action.iconName}></dees-icon>
                          ${action.name}`
                      : action.name}
                  </div>`
                );
              }
              return resultArray;
            })}
          </div>
        </div>
      </div>
    `;
  }

  public async firstUpdated() {
    
  }

  public async updated(changedProperties: Map<string | number | symbol, unknown>): Promise<void> {
    super.updated(changedProperties);
    this.determineColumnWidths();
    if (this.searchable) {
      const existing = this.dataActions.find((actionArg) => actionArg.type.includes('header') && actionArg.name === 'Search');
      if (!existing) {
        this.dataActions.unshift({
          name: 'Search',
          iconName: 'magnifyingGlass',
          type: ['header'],
          actionFunc: async () => {
            console.log('open search');
            const searchGrid = this.shadowRoot.querySelector('.searchGrid');
            searchGrid.classList.toggle('hidden');
          }
        });
        console.log(this.dataActions);
        this.requestUpdate();
      };
    }
  }

  public async determineColumnWidths() {
    const domtools = await this.domtoolsPromise;
    await domtools.convenience.smartdelay.delayFor(0);
    // Get the table element
    const table = this.shadowRoot.querySelector('table');
    if (!table) return;

    // Get the first row's cells to measure the widths
    const cells = table.rows[0].cells;

    const handleColumnByIndex = async (i: number, waitForRenderArg: boolean = false) => {
      const done = plugins.smartpromise.defer();
      const cell = cells[i];

      // Get computed width
      const width = window.getComputedStyle(cell).width;
      if (cell.textContent.includes('Actions')) {
        const neededWidth =
          this.dataActions.filter((actionArg) => actionArg.type.includes('inRow')).length * 36;
        cell.style.width = `${Math.max(neededWidth, 68)}px`;
      } else {
        cell.style.width = width;
      }
      if (waitForRenderArg) {
        requestAnimationFrame(() => {
          done.resolve();
        });
        await done.promise;
      }
    };

    if (cells[cells.length - 1].textContent.includes('Actions')) {
      await handleColumnByIndex(cells.length - 1, true);
    }

    for (let i = 0; i < cells.length; i++) {
      if (cells[i].textContent.includes('Actions')) {
        continue;
      }
      await handleColumnByIndex(i);
    }
    table.style.tableLayout = 'fixed';
  }

  getActionsForType(typeArg: ITableAction['type'][0]) {
    const actions: ITableAction[] = [];
    for (const action of this.dataActions) {
      if (!action.type.includes(typeArg)) continue;
      actions.push(action);
    }
    return actions;
  }

  async handleCellEditing(event: Event, itemArg: T, key: string) {
    const domtools = await this.domtoolsPromise;
    const target = event.target as HTMLElement;
    const originalColor = target.style.color;
    target.style.color = 'transparent';
    const transformedItem = this.displayFunction(itemArg);
    const initialValue = (transformedItem[key] as unknown as string) || '';
    // Create an input element
    const input = document.createElement('input');
    input.type = 'text';
    input.value = initialValue;

    const blurInput = async (blurArg = true, saveArg = false) => {
      if (blurArg) {
        input.blur();
      }
      if (saveArg) {
        itemArg[key] = input.value as any; // Convert string to T (you might need better type casting depending on your data structure)
        this.changeSubject.next(this);
      }
      input.remove();
      target.style.color = originalColor;
      this.requestUpdate();
    };

    // When the input loses focus or the Enter key is pressed, update the data
    input.addEventListener('blur', () => {
      blurInput(false, false);
    });
    input.addEventListener('keydown', (e: KeyboardEvent) => {
      if (e.key === 'Enter') {
        blurInput(true, true); // This will trigger the blur event handler above
      }
    });

    // Replace the cell's content with the input
    target.appendChild(input);
    input.focus();
  }
}