import * as colors from '../00colors.js';
import * as plugins from '../00plugins.js';

import { demoFunc } from './dees-speechbubble.demo.js';
import {
  customElement,
  html,
  DeesElement,
  property,
  type TemplateResult,
  cssManager,
  css,
  type CSSResult,
  unsafeCSS,
  domtools,
  directives,
  unsafeHTML,
} from '@design.estate/dees-element';
import { DeesWindowLayer } from '../dees-windowlayer/dees-windowlayer.js';
import { themeDefaultStyles } from '../00theme.js';

declare global {
  interface HTMLElementTagNameMap {
    'dees-speechbubble': DeesSpeechbubble;
  }
}

@customElement('dees-speechbubble')
export class DeesSpeechbubble extends DeesElement {
  public static demo = demoFunc;

  // STATIC
  public static async createAndShow(refElement: HTMLElement, textArg: string) {
    const windowLayer = await DeesWindowLayer.createAndShow({
      blur: false,
    });
    const speechbubble = document.createElement('dees-speechbubble');
    speechbubble.windowLayer = windowLayer;
    speechbubble.reffedElement = refElement;
    speechbubble.text = textArg;
    speechbubble.manifested = true;
    windowLayer.appendChild(speechbubble);
    windowLayer.style.pointerEvents = 'none';
    (windowLayer.shadowRoot.querySelector('.windowOverlay') as HTMLElement).style.pointerEvents = 'none';
    return speechbubble;
  }

  // INSTANCE
  @property({
    type: Object,
  })
  accessor reffedElement: HTMLElement;

  @property({
    type: String,
    reflect: true,
  })
  accessor text: string;

  @property({
    type: Boolean,
  })
  accessor wave: boolean = false;

  @property({
    type: Boolean,
  })
  accessor manifested = false;

  @property({
    type: String,
  })
  accessor status: 'normal' | 'pending' | 'success' | 'error' = 'normal';

  public windowLayer: DeesWindowLayer;

  constructor() {
    super();
  }

  public static styles = [
    themeDefaultStyles,
    cssManager.defaultStyles,
    css`
      /* TODO: Migrate hardcoded values to --dees-* CSS variables */
      :host {
        box-sizing: border-box;
        color: ${cssManager.bdTheme('#333', '#fff')};
        user-select: none;
      }

      .maincontainer {
        position: relative;
        will-change: transform;
        transition: transform 0.2s;
        transform: translateX(0px);
        transition: all 0.2s;
        margin-left: 0px;
        filter: drop-shadow(0px 0px 2px rgba(0, 0, 0, 0.2));
        pointer-events: none;
        opacity: 0;
        transition: all 0.2s;
      }

      .arrow {
        position: absolute;
        transform: rotate(45deg);
        background: ${cssManager.bdTheme('#fff', '#333')};
        height: 15px;
        width: 15px;
        left: 2px;
        top: 12px;
        border-radius: 3px;
      }

      .speechbubble {
        background: ${cssManager.bdTheme('#fff', '#333')};
        padding: 0px 16px;
        border-radius: 3px;
        position: absolute;
        min-width: 240px;
        font-size: 12px;
        top: 0px;
        left: 8px;
      }

      .wave {
        animation-name: wave-animation; /* Refers to the name of your @keyframes element below */
        animation-duration: 2.5s; /* Change to speed up or slow down */
        animation-iteration-count: infinite; /* Never stop waving :) */
        transform-origin: 70% 70%; /* Pivot around the bottom-left palm */
        display: inline-block;
      }

      @keyframes wave-animation {
        0% {
          transform: rotate(0deg);
        }
        10% {
          transform: rotate(14deg);
        } /* The following five values can be played with to make the waving more or less extreme */
        20% {
          transform: rotate(-8deg);
        }
        30% {
          transform: rotate(14deg);
        }
        40% {
          transform: rotate(-4deg);
        }
        50% {
          transform: rotate(10deg);
        }
        60% {
          transform: rotate(0deg);
        } /* Reset for the last half to pause */
        100% {
          transform: rotate(0deg);
        }
      }
    `,
  ];

  public render(): TemplateResult {
    return html`
      ${this.manifested
        ? html`
            <div class="maincontainer" @click=${this.handleClick}>
              <div class="arrow"></div>
              <div class="speechbubble">
                ${this.wave ? html`<span class="wave">👋</span>` : html``}
                ${directives.resolve(this.getHtml())}
              </div>
            </div>
          `
        : html``}
    `;
  }

  public async handleClick() {
    console.log('speechbubble got clicked.');
  }

  public async firstUpdated() {
    // lets make sure we have a ref
    if (!this.reffedElement) {
      this.reffedElement = this.previousElementSibling as HTMLElement;
    }
    if (this.manifested) {
      await this.updatePosition();
      (this.shadowRoot.querySelector('.maincontainer') as HTMLElement).style.opacity = '1';
    } else {
      // lets make sure we instrument it
      let speechbubble: DeesSpeechbubble;
      this.reffedElement.addEventListener('mouseenter', async () => {
        speechbubble = await DeesSpeechbubble.createAndShow(this.reffedElement, this.text);
      });
      this.reffedElement.addEventListener('mouseleave', () => {
        speechbubble.destroy();
      });
    }
  }

  public async updatePosition() {
    const refElement = this.reffedElement;
    const boundingClientRect = refElement.getBoundingClientRect();
    this.style.position = 'fixed';
    this.style.top = `${boundingClientRect.top - 13}px`;
    this.style.left = `${boundingClientRect.left + refElement.clientWidth + 4}px`;
    if (boundingClientRect.right > 250) {
      this.style.width = `250px`;
    }
  }

  public async getHtml(): Promise<any> {
    if (!this.text) {
      return '';
    }
    const normalized = domtools.plugins.smartstring.normalize.standard(this.text);
    const result = await domtools.plugins.smartmarkdown.SmartMarkdown.easyMarkdownToHtml(
      normalized
    );
    return unsafeHTML(result);
  }

  public async show() {}

  public async destroy() {
    (this.shadowRoot.querySelector('.maincontainer') as HTMLElement).style.opacity = '0';
    this.windowLayer.destroy();
  }
}
