import { LitElement, html, nothing } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { EINSTUFUNGEN, type Einstufung } from 'bibliothek-types';
import { bruecke, type EintragMitBefund, type Einstellungen } from '../bruecke';
import { gemerkterModus, setzeTheme, type ThemeModus } from '../theme';
import './client-zeile';

interface Auswahloption {
  dataId: string;
  showContent: string;
  selected?: boolean;
}

const THEME_OPTIONEN: Array<{ id: ThemeModus; text: string }> = [
  { id: 'system', text: 'System' },
  { id: 'hell', text: 'Hell' },
  { id: 'dunkel', text: 'Dunkel' },
];

/** Wurzel-Komponente des Clients. */
@customElement('client-app')
export class ClientApp extends LitElement {
  @state() private eintraege: EintragMitBefund[] = [];
  @state() private aktiveEinstufung: Einstufung = 'lizenzfrei';
  @state() private suchbegriff = '';
  @state() private ladefehler = '';
  @state() private meldung = '';
  @state() private laeuftFuer = 0;
  @state() private einstellungenOffen = false;
  @state() private einstellungen?: Einstellungen;
  @state() private serverAdresse = '';
  @state() private werkzeugOrdner = '';
  @state() private themeOptionen: Auswahloption[] = [];

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

  connectedCallback(): void {
    super.connectedCallback();
    const modus = gemerkterModus();
    this.themeOptionen = THEME_OPTIONEN.map((eintrag) => ({
      dataId: eintrag.id,
      showContent: eintrag.text,
      selected: eintrag.id === modus,
    }));
    void this.alesLaden();
  }

  render() {
    return html`
      <div class="rahmen">
        <header class="kopf">
          <h1>Software-Bibliothek</h1>
          <div class="kopf-aktionen">
            <dh-select
              class="theme-select"
              withoutEmptyDefault
              notSearchable
              .selectOptions=${this.themeOptionen}
              @changeSelectOption=${this.beiTheme}
            ></dh-select>
            <dh-button icon-button variant="standard" title="Werkzeug-Ordner öffnen" @click=${this.beiOrdnerOeffnen}>
              <span slot="img" class="fv-icon-folder"></span>
            </dh-button>
            <dh-button icon-button variant="standard" title="Einstellungen" @click=${this.beiEinstellungenOeffnen}>
              <span slot="img" class="fv-icon-settings"></span>
            </dh-button>
          </div>
        </header>

        ${this.ladefehler ? html`<p class="warnung">${this.ladefehler}</p>` : nothing}
        ${this.meldung ? html`<p class="hinweis">${this.meldung}</p>` : nothing}
        ${this.rendereUpdateHinweis()}
        ${this.einstellungenOffen ? this.rendereEinstellungen() : this.rendereKatalog()}
      </div>
    `;
  }

  // --- Teilansichten ---

  /** Der Grund, warum es diesen Client gibt: ungefragt sagen, was veraltet ist. */
  private rendereUpdateHinweis() {
    const veraltet = this.eintraege.filter(
      (eintrag) => eintrag.ueberwacht && (eintrag.lokal?.zustand === 'veraltet' || eintrag.lokal?.zustand === 'fremd')
    );
    if (veraltet.length === 0) {
      return nothing;
    }
    return html`<p class="update-hinweis">
      ${veraltet.length === 1 ? 'Ein Werkzeug ist nicht auf dem neuesten Stand' : `${veraltet.length} Werkzeuge sind nicht auf dem neuesten Stand`}:
      ${veraltet.map((eintrag) => eintrag.name).join(', ')}.
    </p>`;
  }

  private rendereKatalog() {
    if (this.eintraege.length === 0) {
      return html`<p class="leer">${this.ladefehler ? '' : 'Katalog wird geladen …'}</p>`;
    }
    return html`
      <dh-input class="suche" type="text" placeholder="Suchen" .value=${this.suchbegriff} @input=${this.beiSuche}></dh-input>
      <dh-tab stretch @clickTab=${this.beiReiterWechsel}>
        ${EINSTUFUNGEN.map(
          (eintrag) => html`<div
            slot="tab-head"
            content=${eintrag.id}
            class=${eintrag.id === this.aktiveEinstufung ? 'selected' : ''}
          >
            ${eintrag.text} (${this.mitEinstufung(eintrag.id).length})
          </div>`
        )}
        ${EINSTUFUNGEN.map(
          (eintrag) => html`<div
            slot="tab-content"
            tab=${eintrag.id}
            class=${eintrag.id === this.aktiveEinstufung ? '' : 'hidden'}
          >
            ${eintrag.id === this.aktiveEinstufung ? this.rendereListe() : nothing}
          </div>`
        )}
      </dh-tab>
    `;
  }

  private rendereListe() {
    const gefiltert = this.gefiltert();
    if (gefiltert.length === 0) {
      return html`<p class="leer">
        ${this.suchbegriff ? html`Keine Treffer für „${this.suchbegriff}".` : 'In dieser Einstufung ist nichts hinterlegt.'}
      </p>`;
    }
    return html`<div class="liste">
      ${gefiltert.map(
        (eintrag) => html`<client-zeile
          .eintrag=${eintrag}
          ?laeuft=${this.laeuftFuer === eintrag.id}
          @holen=${this.beiHolen}
          @verknuepfen=${this.beiVerknuepfen}
        ></client-zeile>`
      )}
    </div>`;
  }

  private rendereEinstellungen() {
    return html`
      <dh-card variant="outlined" no-hover-effect no-click-effect>
        <span slot="header">Einstellungen</span>
        <dh-form-grid slot="content">
          <dh-form-field label="Adresse der Bibliothek">
            <dh-input type="text" .value=${this.serverAdresse} @input=${this.beiServerAdresse}></dh-input>
            <span class="feldhinweis">
              Rechnername statt IP-Adresse ist die bessere Wahl — er überlebt einen Wechsel der Adresse.
            </span>
          </dh-form-field>
          <dh-form-field label="Werkzeug-Ordner">
            <dh-input type="text" .value=${this.werkzeugOrdner} @input=${this.beiWerkzeugOrdner}></dh-input>
            <span class="feldhinweis">
              Hierhin kommen eigenständige Programme; Installationsprogramme in den Unterordner
              „Installationsprogramme".
            </span>
          </dh-form-field>
        </dh-form-grid>
        <div slot="actions">
          <dh-button buttonText="Speichern" variant="filled" @click=${this.beiEinstellungenSpeichern}></dh-button>
          <dh-button buttonText="Abbrechen" variant="standard" @click=${this.beiEinstellungenSchliessen}></dh-button>
        </div>
      </dh-card>
    `;
  }

  // --- Daten ---

  private async alesLaden(): Promise<void> {
    const api = await bruecke();
    const stand = await api.einstellungen();
    this.einstellungen = stand.einstellungen;
    this.serverAdresse = stand.einstellungen.serverAdresse;
    this.werkzeugOrdner = stand.einstellungen.werkzeugOrdner;
    await this.katalogLaden();
  }

  private async katalogLaden(): Promise<void> {
    const api = await bruecke();
    const antwort = await api.katalog();
    if (!antwort.ok) {
      this.ladefehler = `${antwort.meldung} — Adresse in den Einstellungen prüfen.`;
      this.eintraege = [];
      return;
    }
    this.ladefehler = '';
    this.eintraege = antwort.eintraege;
  }

  private mitEinstufung(einstufung: Einstufung): EintragMitBefund[] {
    return this.eintraege.filter((eintrag) => eintrag.einstufung === einstufung && !eintrag.elternId);
  }

  private gefiltert(): EintragMitBefund[] {
    const suche = this.suchbegriff.trim().toLowerCase();
    const inDerEinstufung = this.mitEinstufung(this.aktiveEinstufung);
    if (!suche) {
      return inDerEinstufung;
    }
    return inDerEinstufung.filter(
      (eintrag) =>
        eintrag.name.toLowerCase().includes(suche) || eintrag.beschreibung.toLowerCase().includes(suche)
    );
  }

  // --- Ereignisse ---

  private beiTheme(ereignis: CustomEvent<{ selectedOption: { dataId: string } }>): void {
    setzeTheme(ereignis.detail.selectedOption.dataId as ThemeModus);
  }

  private beiSuche(ereignis: CustomEvent<string>): void {
    this.suchbegriff = ereignis.detail;
  }

  /** dh-tab liefert die Kennung des Reiters unmittelbar als `detail`. */
  private beiReiterWechsel(ereignis: CustomEvent<string>): void {
    this.aktiveEinstufung = ereignis.detail as Einstufung;
  }

  private async beiHolen(ereignis: CustomEvent<EintragMitBefund>): Promise<void> {
    const eintrag = ereignis.detail;
    this.laeuftFuer = eintrag.id;
    this.meldung = `${eintrag.name} wird geholt …`;
    const api = await bruecke();
    const ergebnis = await api.herunterladen(eintrag.id, eintrag.dateiname, eintrag.version, eintrag.programmart);
    this.meldung = ergebnis.meldung;
    this.laeuftFuer = 0;
    await this.katalogLaden();
  }

  private async beiVerknuepfen(ereignis: CustomEvent<EintragMitBefund>): Promise<void> {
    const eintrag = ereignis.detail;
    if (!eintrag.lokal?.pfad) {
      return;
    }
    const api = await bruecke();
    const ergebnis = await api.verknuepfung_anlegen(eintrag.lokal.pfad, eintrag.name);
    this.meldung = ergebnis.meldung ?? '';
  }

  private async beiOrdnerOeffnen(): Promise<void> {
    const api = await bruecke();
    await api.ordner_oeffnen();
  }

  private beiEinstellungenOeffnen(): void {
    this.serverAdresse = this.einstellungen?.serverAdresse ?? '';
    this.werkzeugOrdner = this.einstellungen?.werkzeugOrdner ?? '';
    this.einstellungenOffen = true;
  }

  private beiEinstellungenSchliessen(): void {
    this.einstellungenOffen = false;
  }

  private beiServerAdresse(ereignis: CustomEvent<string>): void {
    this.serverAdresse = ereignis.detail;
  }

  private beiWerkzeugOrdner(ereignis: CustomEvent<string>): void {
    this.werkzeugOrdner = ereignis.detail;
  }

  private async beiEinstellungenSpeichern(): Promise<void> {
    const api = await bruecke();
    const ergebnis = await api.einstellungen_speichern(this.serverAdresse, this.werkzeugOrdner);
    this.einstellungen = ergebnis.einstellungen;
    this.einstellungenOffen = false;
    this.meldung = 'Einstellungen gespeichert.';
    await this.katalogLaden();
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'client-app': ClientApp;
  }
}
