import { LitElement, css, html, TemplateResult, PropertyValues } from 'lit';
import { customElement, state, property } from "lit/decorators.js";
import { styleMap } from 'lit/directives/style-map.js';

import { HomeAssistant, hasConfigOrEntityChanged } from 'custom-card-helpers';

import { fillConfig, TimerBarEntityRow } from './timer-bar-entity-row';
import { fillMushroomConfig, TimerBarMushroomRow, mushroomStyle } from './timer-bar-mushroom-row';

import type { TimerBarConfig, TimerBarEntityConfig, AttributeConfig, Mode } from './types';
import { findMode, gatherEntitiesFromConfig, haveEntitiesChanged } from './helpers';
import { version } from '../package.json';

// This puts your card into the UI card picker dialog
(window as any).customCards = (window as any).customCards || [];
(window as any).customCards.push({
  type: 'timer-bar-card',
  name: 'Timer Bar Card',
  description: 'Display timer-related information as a progress bar',
});

window.customElements.define('timer-bar-entity-row', TimerBarEntityRow);
window.customElements.define('timer-bar-mushroom-row', TimerBarMushroomRow);
console.info(
  `%c TIMER-BAR-CARD %c Version ${version} `,
  'font-weight: bold; color: #000; background: #aeb',
  'font-weight: bold; color: #000; background: #ddd',
);

@customElement('timer-bar-card')
export class TimerBarCard extends LitElement {

  @property() public hass?: HomeAssistant;
  @property({ attribute: false }) public editMode?: boolean;
  @state() private config!: TimerBarConfig;

  @property({ reflect: true, type: String })
  public layout: string | undefined;

  public static async getConfigElement() {
    await import('./timer-bar-card-editor');
    return document.createElement('timer-bar-editor');
  }

  public static getStubConfig(): object {
    return {};
  }

  setConfig(config: TimerBarConfig): void {
    if (!config) {
      throw new Error("Invalid configuration");
    }
    if ('mushroom' in config && config.mushroom)
      this.config = fillMushroomConfig(config, config.mushroom);
    else
      this.config = fillConfig(config);
  }

  protected render(): TemplateResult | void {
    const config = this.config;
    if (config.entity && config.entities) {
      return html`<hui-warning>Both entity and entities cannot be defined</hui-warning>`;
    }

    if (config.entity) {
      if ('mushroom' in config)
        return html`<timer-bar-mushroom-row .layout=${this.layout} .config=${config} .mushroom=${config.
mushroom??{}} style=${mushroomStyle(config.mushroom??{})} .hass=${this.hass}></timer-bar-mushroom-row>`
      else
        return html`<timer-bar-entity-row .config=${config} .hass=${this.hass}></timer-bar-entity-row>`
    } else if (config.entities && !this._filteredEntities().length) {
      if (this.editMode || config.show_empty) {
        const content = typeof config.show_empty == 'undefined' ? 'No entities match the filter. This card will disappear when you finish editing.' : config.show_empty;
        return html`<ha-card>
          <h1 class="card-header">${config.name}</h1>
          <div class="card-content">${content}</div>
        </ha-card>`;
      } else {
        return html``; // Return a blank card
      }
    } else if (config.entities) {
      return html`<ha-card>
        ${config.name && !config.header_entity ? html`<h1 class="card-header">${this.config.name}</h1>` : ''}
        <div class="card-content">
          ${config.header_entity ? this._renderTitle() : ''}
          ${this._renderContent()}
        </div>
      </ha-card>`;
    } else {
      return html`<hui-warning>Neither entity nor entities are defined</hui-warning>`;
    }
  }

  private _hasEntityChanged(oldHass: HomeAssistant, ...entities: (string | AttributeConfig | TimerBarEntityConfig | undefined)[]) {
    for (const entity of entities) {
      if (!entity) continue;
      if (typeof entity === 'string') {
        if (oldHass.states[entity] !== this.hass!.states[entity]) return true;
      } else if ('entity' in entity) {
        if (entity.entity && oldHass.states[entity.entity] !== this.hass!.states[entity.entity]) return true;
      } else if ('script' in entity) {
        if (entity.script && oldHass.states[entity.script] !== this.hass!.states[entity.script]) return true;
      }
    }
    return false;
  }

  protected shouldUpdate(changedProps: PropertyValues): boolean {
    if (!this.config) return false;
    if (changedProps.has('config')) return true;
    if (this.config.entity) {
      return hasConfigOrEntityChanged(this, changedProps, false);
    }

    this.updateComplete.then(() => this._patchFontSize());

    const oldHass = changedProps.get('hass') as HomeAssistant | undefined;
    if (!oldHass || !this.hass) return true;

    const entities = gatherEntitiesFromConfig(this.config)
    if (this.config.header_entity) entities.push(this.config.header_entity)
    for (const entity of this.config.entities!) {
      if (typeof entity === 'string') entities.push(entity)
      else entities.push(...gatherEntitiesFromConfig(entity))
    }

    return haveEntitiesChanged(entities, oldHass, this.hass)
  }

  /** Merges global and per-entity configuration */
  private _configFor(entity: string | TimerBarEntityConfig) {
    let config: TimerBarEntityConfig = { ...this.config };
    delete config.name // so card name does not override entity name
    // Merge in per-entity configuration
    if (typeof entity === 'string') config.entity = entity;
    else config = { ...config, ...entity };
    return config
  }

  private _renderContent(): TemplateResult[] {
    return this._filteredEntities().map(entity => {
      const style = this.config.compressed ? { height: '36px' } : {};
      // Create a entity-row component for every entity
      return html`<timer-bar-entity-row
                    .config=${this._configFor(entity)}
                    .hass=${this.hass}
                    style=${styleMap(style)}
                  ></timer-bar-entity-row>`;
    });
  }

  private _renderTitle(): TemplateResult {
    const style = this._filteredEntities().length > 0 ? { 'margin-bottom': '12px' } : {};
    const config = { entity: this.config.header_entity, secondary_info: this.config.header_secondary};
    return html`<hui-generic-entity-row
                  style=${styleMap(style)}
                  .config=${config}
                  .hass=${this.hass}
                ></generic-entity-header>`;
  }

  private _patchFontSize() {
    const element = this.renderRoot.querySelector('hui-generic-entity-row');
    const info = element?.shadowRoot?.querySelector('.info');
    if (!info) return;

    const nodes = [...info.childNodes];
    const textNodes = nodes.filter(c => {
      return c.nodeType === Node.TEXT_NODE && c.textContent!.trim();
    });
    if (textNodes[0]) {
      const span = document.createElement("span");
      span.style.fontSize = '1.1em';
      info.insertBefore(span, textNodes[0]);
      info.removeChild(textNodes[0]);
      span.appendChild(textNodes[0]);
    }
  }

  private _entitiesOfMode(entities: (string|TimerBarEntityConfig)[], mode: Mode) {
    // FIXME: The browser-home assistant time offset is assumed to be zero.
    // Should I watch the states of every entity in the entities list?
    // Or should there be a single offset for the whole card?
    return entities.filter(e => findMode(this.hass!, this._configFor(e), 0) === mode)
  }

  private _filteredEntities() {
    if (!this.config.filter || !this.hass) return this.config.entities!;

    return this._entitiesOfMode(this.config.entities!, 'active')
      .concat(this._entitiesOfMode(this.config.entities!, 'pause'))
      .concat(this._entitiesOfMode(this.config.entities!, 'waiting'))
    ;
  }

  public async getCardSize(): Promise<number> {
    if (this.config.entity) return 1;

    let size = 0;
    if (this.config.header_entity)
      size += 1;
    else if (this.config.name)
      size += 2;

    return size + this._filteredEntities().length;
  }

  /** Taken from lovelace-mushroom */
  public getGridOptions() {
    if (!('mushroom' in this.config)) {
      return {};
    }
    if (!this.config.mushroom) {
      return {
        columns: 6,
        rows: 1,
      };
    }

    const options = {
      min_rows: 1,
      min_columns: 4,
      columns: 6,
      rows: 0, // initial value
    };

    const appearance = this.config.mushroom;

    const collapsible_controls =
      "collapsible_controls" in this.config &&
      Boolean(this.config.collapsible_controls);

    const hasInfo =
      appearance.primary_info !== "none" ||
      appearance.secondary_info !== "none";
    const hasIcon = appearance.icon_type !== "none";

    if (appearance.layout === "vertical") {
      if (hasIcon) {
        options.rows += 1;
      }
      if (hasInfo) {
        options.rows += 1;
      }
      options.min_columns = 2;
    }

    if (appearance.layout === "horizontal") {
      options.rows = 1;
      options.columns = 12;
    }

    if (appearance.layout === "default") {
      if (hasInfo || hasIcon) {
        options.rows += 1;
      }
    }

    // If icon only, set 3x1 for size
    if (!hasInfo) {
      options.columns = 3;
      options.rows = 1;
      options.min_columns = 2;
    }

    // Ensure card has at least 1 row
    options.rows = Math.max(options.rows, 1);
    options.min_rows = options.rows;

    return options;
  }

  static styles = css`
    .card-header {
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
  `;
}
