import { LitElement, html, nothing } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { event, type EventRef } from '@dh-software/lit-extensions';
import type { Software } from 'bibliothek-types';

/**
 * Eine kompakte Katalog-Zeile für einen Software-Eintrag. Feuert Aktions-Events an die App.
 * Die Aktionen liegen in festen Spalten, damit sie über alle Zeilen hinweg untereinander stehen;
 * fehlt eine Aktion, bleibt ihre Spalte leer.
 *
 * Zusatzinhalte (Plug-ins, Sprachpakete, Vorlagen …) stehen eingerückt direkt unter ihrem Tool
 * und bilden mit ihm eine Gruppe.
 */
@customElement('bibliothek-zeile')
export class BibliothekZeile extends LitElement {
  @property({ attribute: false }) software!: Software;
  /** Weitere Einträge, die zu dieser Software gehören. */
  @property({ attribute: false }) zusatzinhalte: Software[] = [];
  @property({ type: Boolean }) lokal = false;
  /** Zeile eines Zusatzinhalts (eingerückt unter seinem Tool). */
  @property({ type: Boolean }) eingerueckt = false;
  /** Ist dieser Eintrag für den Sammel-Download angehakt? */
  @property({ type: Boolean }) gewaehlt = false;
  /** Ids aller angehakten Einträge — nötig, damit auch eingerückte Zeilen ihren Zustand kennen. */
  @property({ attribute: false }) auswahl: ReadonlySet<number> = new Set();

  @event({ bubbles: true, composed: true }, 'auswahlGeaendert')
  private auswahlGeaendert!: EventRef<{ software: Software; gewaehlt: boolean }>;

  @event({ bubbles: true, composed: true }, 'herunterladen')
  private herunterladen!: EventRef<Software>;

  @event({ bubbles: true, composed: true }, 'pruefen')
  private pruefen!: EventRef<Software>;

  @event({ bubbles: true, composed: true }, 'bearbeiten')
  private bearbeiten!: EventRef<Software>;

  @event({ bubbles: true, composed: true }, 'loeschen')
  private loeschen!: EventRef<Software>;

  // Light DOM, damit das globale dh-components-Theme greift.
  createRenderRoot() {
    return this;
  }

  render() {
    const software = this.software;
    return html`
      <div class="zeile ${this.eingerueckt ? 'zeile-eingerueckt' : ''}">
        <span class="zeile-auswahl">
          <dh-checkbox
            ?checked=${this.gewaehlt}
            ?disabled=${!software.dateiname}
            title=${software.dateiname ? 'Für Sammel-Download auswählen' : 'Keine Datei vorhanden'}
            @isChange=${this.beiAuswahl}
          ></dh-checkbox>
        </span>
        <div class="zeile-info">
          <span class="zeile-name">
            ${software.name}
            ${software.einstufung === 'lizenzpflichtig'
              ? html`<span class="lizenz-marke" title="Lizenzschlüssel über den Support beziehen">lizenzpflichtig</span>`
              : nothing}
            ${software.einstufung === 'intern' ? html`<span class="intern-marke">intern</span>` : nothing}
          </span>
          <span class="zeile-meta">
            ${this.rendereStand()}
            <span class="${this.statusKlasse()}">${this.statusText()}</span>
            ${this.rendereSignatur()}
          </span>
          ${this.rendereHinweise()}
        </div>
        <div class="zeile-aktionen">
          <span class="aktion">
            ${software.anleitungUrl
              ? html`<a class="anleitung" href=${software.anleitungUrl} target="_blank" rel="noopener noreferrer">Anleitung</a>`
              : nothing}
          </span>
          <span class="aktion">
            ${software.dateiname
              ? html`<dh-button
                  icon-button
                  variant="filled"
                  title=${`${software.name} herunterladen`}
                  @click=${this.beiHerunterladen}
                >
                  <span slot="img" class="fv-icon-download"></span>
                </dh-button>`
              : nothing}
          </span>
          ${this.lokal
            ? html`
                <span class="aktion">
                  ${software.quelleTyp && software.quelleTyp !== 'manuell'
                    ? html`<dh-button buttonText="Prüfen" variant="standard" @click=${this.beiPruefen}></dh-button>`
                    : nothing}
                </span>
                <span class="aktion">
                  <dh-button buttonText="Bearbeiten" variant="standard" @click=${this.beiBearbeiten}></dh-button>
                </span>
                <span class="aktion">
                  <dh-button buttonText="Löschen" variant="standard" @click=${this.beiLoeschen}></dh-button>
                </span>
              `
            : nothing}
        </div>
      </div>
      ${this.zusatzinhalte.map(
        (zusatzinhalt) => html`<bibliothek-zeile
          .software=${zusatzinhalt}
          .auswahl=${this.auswahl}
          ?gewaehlt=${this.auswahl.has(zusatzinhalt.id)}
          ?lokal=${this.lokal}
          eingerueckt
        ></bibliothek-zeile>`
      )}
    `;
  }

  private beiAuswahl(ereignis: CustomEvent<boolean>): void {
    this.auswahlGeaendert.dispatch({ software: this.software, gewaehlt: ereignis.detail });
  }

  /**
   * Hinweise zum Umgang mit der Datei: was aus der Programmart folgt, plus der
   * freie Hinweis am Eintrag. Die eigene Zeile kostet nur dort Höhe, wo wirklich
   * etwas zu sagen ist — die meisten Einträge bleiben einzeilig.
   */
  private rendereHinweise() {
    const software = this.software;
    const teile: string[] = [];
    if (software.dateiname && software.programmart === 'installer') {
      teile.push('Datei kann nach der Installation gelöscht werden');
    }
    if (software.dateiname && software.programmart === 'eigenstaendig') {
      teile.push('Läuft direkt — an einen festen Ort legen');
    }
    if (software.hinweis) {
      teile.push(software.hinweis);
    }
    return teile.length > 0 ? html`<span class="zeile-hinweis">${teile.join(' · ')}</span>` : nothing;
  }

  /**
   * Signaturbefund der bereitgestellten Datei — nur in der Verwaltung.
   * Für die Netzwerk-Ansicht ist das Betriebswissen ohne Nutzen: Wer etwas
   * herunterlädt, kann daraus keine Entscheidung ableiten.
   */
  private rendereSignatur() {
    const software = this.software;
    if (!this.lokal || !software.dateiname || !software.signaturStatus) {
      return nothing;
    }
    if (software.signaturStatus === 'Valid') {
      return html` &nbsp;·&nbsp;
        <span class="signatur-gut" title="Gültig signiert von ${software.signaturHerausgeber}">
          signiert: ${software.signaturHerausgeber}
        </span>`;
    }
    const text = software.signaturStatus === 'NotSigned' ? 'unsigniert' : `Signatur unklar (${software.signaturStatus})`;
    return html` &nbsp;·&nbsp; <span class="signatur-unklar" title="Windows-Signaturstatus: ${software.signaturStatus}">${text}</span>`;
  }

  /**
   * Zeigt bevorzugt das Veröffentlichungsdatum des Anbieters — das ist die Angabe,
   * die den Nutzer interessiert. Nur wenn die Quelle keins liefert, wird ersatzweise
   * unser Abrufzeitpunkt genannt (und als solcher benannt).
   */
  private rendereStand() {
    const software = this.software;
    if (software.veroeffentlichtAm) {
      return html`Stand ${this.alsDatum(software.veroeffentlichtAm)} &nbsp;·&nbsp; `;
    }
    if (software.dateiStandVom) {
      return html`geholt ${this.alsDatum(software.dateiStandVom)} &nbsp;·&nbsp; `;
    }
    return nothing;
  }

  private alsDatum(zeitstempel: string): string {
    const zeitpunkt = new Date(zeitstempel);
    return Number.isNaN(zeitpunkt.getTime()) ? '—' : zeitpunkt.toLocaleDateString('de-DE');
  }

  private statusText(): string {
    const software = this.software;
    if (software.dateiname) {
      return software.dateiname;
    }
    if (!software.quelleTyp) {
      return 'keine Quelle';
    }
    if (software.quelleTyp === 'manuell') {
      return 'keine Datei hinterlegt';
    }
    if (software.letzterAbgleich && !software.abgleichErfolg) {
      return software.abgleichMeldung || 'Abgleich fehlgeschlagen';
    }
    return 'Quelle hinterlegt, noch nicht geladen';
  }

  private statusKlasse(): string {
    const software = this.software;
    if (software.dateiname) {
      return 'status-ok';
    }
    if (!software.quelleTyp || software.quelleTyp === 'manuell' || (software.letzterAbgleich && !software.abgleichErfolg)) {
      return 'status-fehler';
    }
    return 'status-ausstehend';
  }

  private beiHerunterladen(): void {
    this.herunterladen.dispatch(this.software);
  }

  private beiPruefen(): void {
    this.pruefen.dispatch(this.software);
  }

  private beiBearbeiten(): void {
    this.bearbeiten.dispatch(this.software);
  }

  private beiLoeschen(): void {
    this.loeschen.dispatch(this.software);
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'bibliothek-zeile': BibliothekZeile;
  }
}
