/**
 * Spare parts & consumables section on the object detail page.
 *
 * Shows the object's parts list (stock badge, storage location, identifiers,
 * shopping link), lets admins add/edit/delete parts, and adjust stock
 * (inventory correction / manual restock). Follows the documents-section
 * conventions: standalone LitElement fed by the panel, native <input>s in the
 * inline form (the ha-textfield-in-custom-panel trap), t() i18n.
 *
 * After a create/delete (entity set changes → backend reloads the entry) it
 * fires "parts-changed" so the panel re-fetches objects; stock adjustments
 * update the local copy from the WS response for instant feedback.
 */

import { LitElement, html, css, nothing } from "lit";
import { isSafeHttpUrl } from "../helpers/url";
import { property, state } from "lit/decorators.js";
import { t, ensureLocale, langOf } from "../styles";
import { describeWsError } from "../ws-errors";
import type { HomeAssistant, MaintenancePart } from "../types";
// Per-part document links (v2.26) — the task-documents component in part mode.
import "./task-documents";

interface PartForm {
  id?: string;
  name: string;
  vendor: string;
  mpn: string;
  gtin: string;
  storage_location: string;
  product_url: string;
  unit: string;
  cost: string;
  stock: string;
  reorder_threshold: string;
  restock_quantity: string;
  auto_buy_task: boolean;
  notes: string;
}

const EMPTY_FORM: PartForm = {
  name: "",
  vendor: "",
  mpn: "",
  gtin: "",
  storage_location: "",
  product_url: "",
  unit: "",
  cost: "",
  stock: "",
  reorder_threshold: "",
  restock_quantity: "",
  auto_buy_task: true,
  notes: "",
};

export class MaintenancePartsSection extends LitElement {
  @property({ attribute: false }) public hass!: HomeAssistant;
  @property({ attribute: false }) public entryId!: string;
  @property({ attribute: false }) public parts: MaintenancePart[] = [];
  @property({ type: Boolean }) public canWrite = false;
  @property({ attribute: false }) public currencySymbol = "€";

  @state() private _editing: PartForm | null = null;
  @state() private _busy = false;
  @state() private _error = "";
  @state() private _restockFor: string | null = null;
  @state() private _restockQty = "";
  @state() private _restockInvalid = false;
  @state() private _docsFor: string | null = null;

  private get _lang(): string {
    return langOf(this.hass);
  }

  public connectedCallback(): void {
    super.connectedCallback();
    // Lazy-load the locale bundle for t() (same pattern as documents-section).
    void ensureLocale(this._lang).then(() => this.requestUpdate());
  }

  private _notifyChanged(): void {
    this.dispatchEvent(new CustomEvent("parts-changed", { bubbles: true, composed: true }));
  }

  private async _send<T>(msg: Record<string, unknown>): Promise<T | null> {
    this._busy = true;
    this._error = "";
    try {
      return await this.hass.connection.sendMessagePromise<T>(msg);
    } catch (err) {
      this._error = describeWsError(err, this._lang);
      return null;
    } finally {
      this._busy = false;
    }
  }

  private _openAdd(): void {
    this._editing = { ...EMPTY_FORM };
  }

  private _openEdit(part: MaintenancePart): void {
    this._editing = {
      id: part.id,
      name: part.name,
      vendor: part.vendor || "",
      mpn: part.mpn || "",
      gtin: part.gtin || "",
      storage_location: part.storage_location || "",
      product_url: part.product_url || "",
      unit: part.unit || "",
      cost: part.cost != null ? String(part.cost) : "",
      stock: part.stock != null ? String(part.stock) : "",
      reorder_threshold: part.reorder_threshold != null ? String(part.reorder_threshold) : "",
      restock_quantity: part.restock_quantity != null ? String(part.restock_quantity) : "",
      auto_buy_task: !!part.auto_buy_task,
      notes: part.notes || "",
    };
  }

  private _formValue(f: PartForm): Record<string, unknown> {
    const num = (s: string): number | null => (s.trim() === "" ? null : Number(s));
    return {
      entry_id: this.entryId,
      name: f.name.trim(),
      vendor: f.vendor.trim() || null,
      mpn: f.mpn.trim() || null,
      gtin: f.gtin.trim() || null,
      storage_location: f.storage_location.trim() || null,
      product_url: f.product_url.trim() || null,
      unit: f.unit.trim() || null,
      cost: num(f.cost),
      stock: num(f.stock),
      reorder_threshold: num(f.reorder_threshold),
      restock_quantity: num(f.restock_quantity),
      auto_buy_task: f.auto_buy_task,
      notes: f.notes.trim() || null,
    };
  }

  private async _save(): Promise<void> {
    const f = this._editing;
    if (!f || !f.name.trim()) return;
    const payload = this._formValue(f);
    const type = f.id ? "maintenance_supporter/part/update" : "maintenance_supporter/part/create";
    const result = await this._send<{ part_id?: string }>(f.id ? { type, part_id: f.id, ...payload } : { type, ...payload });
    if (result !== null) {
      this._editing = null;
      this._notifyChanged();
    }
  }

  private async _delete(part: MaintenancePart): Promise<void> {
    // Deleting a part drops its stock tracking, task links and buy reminder —
    // destructive enough to warrant an explicit confirmation (user request).
    if (!window.confirm(t("part_delete_confirm", this._lang).replace("{name}", part.name))) return;
    const result = await this._send<{ success: boolean }>({
      type: "maintenance_supporter/part/delete",
      entry_id: this.entryId,
      part_id: part.id,
    });
    if (result !== null) this._notifyChanged();
  }

  private async _restock(part: MaintenancePart): Promise<void> {
    const qty = parseFloat(this._restockQty);
    if (!Number.isFinite(qty) || qty === 0) {
      // Don't silently swallow a no-op amount — keep the input open and mark
      // it so the user sees WHY nothing happened (0 / empty / not a number).
      this._restockInvalid = true;
      return;
    }
    this._restockInvalid = false;
    const result = await this._send<{ stock: number }>({
      type: "maintenance_supporter/part/restock",
      entry_id: this.entryId,
      part_id: part.id,
      delta: qty,
    });
    this._restockFor = null;
    if (result !== null) {
      // Instant local feedback; the panel refresh follows via parts-changed.
      part.stock = result.stock;
      this.requestUpdate();
      this._notifyChanged();
    }
  }

  private _identLine(part: MaintenancePart): string {
    return [part.vendor, part.mpn ? `MPN: ${part.mpn}` : "", part.gtin ? `GTIN: ${part.gtin}` : ""]
      .filter(Boolean)
      .join(" · ");
  }

  private _renderRow(part: MaintenancePart) {
    const L = this._lang;
    const tracked = part.stock !== null && part.stock !== undefined;
    const ident = this._identLine(part);
    const docsOpen = this._docsFor === part.id;
    return html`
      <div class="part-row ${part.is_low ? "low" : ""}">
        <ha-icon class="part-icon" icon=${part.is_low ? "mdi:cart-arrow-down" : "mdi:package-variant-closed"}></ha-icon>
        <div class="part-main">
          <div class="part-name">
            ${isSafeHttpUrl(part.shopping_url)
              ? html`<a href=${part.shopping_url} target="_blank" rel="noopener noreferrer">${part.name}</a>`
              : part.name}
            ${tracked
              ? html`<span class="stock-badge ${part.is_low ? "low" : ""}"
                  >${part.stock}${part.unit ? ` ${part.unit}` : ""}${part.reorder_threshold != null
                    ? html`<span class="threshold">/${part.reorder_threshold}</span>`
                    : nothing}</span
                >`
              : nothing}
          </div>
          <div class="part-meta">
            ${ident ? html`<span>${ident}</span>` : nothing}
            ${part.storage_location
              ? html`<span class="loc"><ha-icon icon="mdi:map-marker-outline"></ha-icon>${part.storage_location}</span>`
              : nothing}
          </div>
        </div>
        <ha-icon-button
          title=${t("documents", L)}
          class=${docsOpen ? "docs-open" : ""}
          @click=${() => (this._docsFor = docsOpen ? null : part.id)}
          ><ha-icon icon="mdi:paperclip"></ha-icon
        ></ha-icon-button>
        ${this.canWrite
          ? html`
              ${this._restockFor === part.id
                ? html`
                    <input
                      class="restock-input${this._restockInvalid ? " invalid" : ""}"
                      type="number"
                      .value=${this._restockQty}
                      placeholder="+1"
                      @input=${(e: Event) => (this._restockQty = (e.target as HTMLInputElement).value)}
                      @keydown=${(e: KeyboardEvent) => {
                        if (e.key === "Enter") this._restock(part);
                        if (e.key === "Escape") this._restockFor = null;
                      }}
                    />
                    <ha-icon-button title=${t("save", L)} @click=${() => this._restock(part)}
                      ><ha-icon icon="mdi:check"></ha-icon
                    ></ha-icon-button>
                  `
                : html`
                    <ha-icon-button
                      title=${t("part_restock", L)}
                      .disabled=${this._busy}
                      @click=${() => {
                        this._restockFor = part.id;
                        this._restockInvalid = false;
                        this._restockQty = String(part.restock_quantity || 1);
                      }}
                      ><ha-icon icon="mdi:plus-minus-variant"></ha-icon
                    ></ha-icon-button>
                  `}
              <ha-icon-button title=${t("edit", L)} .disabled=${this._busy} @click=${() => this._openEdit(part)}
                ><ha-icon icon="mdi:pencil"></ha-icon
              ></ha-icon-button>
              <ha-icon-button title=${t("delete", L)} .disabled=${this._busy} @click=${() => this._delete(part)}
                ><ha-icon icon="mdi:delete-outline"></ha-icon
              ></ha-icon-button>
            `
          : nothing}
      </div>
      ${docsOpen
        ? html`<div class="part-docs">
            <maintenance-task-documents
              .hass=${this.hass}
              .entryId=${this.entryId}
              .partId=${part.id}
              .canWrite=${this.canWrite}
            ></maintenance-task-documents>
          </div>`
        : nothing}
    `;
  }

  private _field(label: string, key: keyof PartForm, opts: { type?: string; placeholder?: string } = {}) {
    const f = this._editing!;
    return html`
      <label class="form-field">
        <span>${label}</span>
        <input
          type=${opts.type || "text"}
          .value=${String(f[key] ?? "")}
          placeholder=${opts.placeholder || ""}
          @input=${(e: Event) => {
            (this._editing as unknown as Record<string, unknown>)[key] = (e.target as HTMLInputElement).value;
            this.requestUpdate();
          }}
        />
      </label>
    `;
  }

  private _renderForm() {
    const L = this._lang;
    const f = this._editing!;
    return html`
      <div class="part-form">
        <div class="form-grid">
          ${this._field(t("part_name", L), "name")}
          ${this._field(t("part_vendor", L), "vendor")}
          ${this._field("MPN", "mpn")}
          ${this._field("GTIN / EAN", "gtin", { placeholder: "4006381333931" })}
          ${this._field(t("part_storage_location", L), "storage_location")}
          ${this._field(t("part_product_url", L), "product_url", { placeholder: "https://…" })}
          ${this._field(t("part_unit", L), "unit")}
          ${this._field(t("part_cost", L), "cost", { type: "number" })}
          ${this._field(t("part_stock", L), "stock", { type: "number" })}
          ${this._field(t("part_reorder_threshold", L), "reorder_threshold", { type: "number" })}
          ${this._field(t("part_restock_quantity", L), "restock_quantity", { type: "number" })}
          <label class="form-field checkbox">
            <input
              type="checkbox"
              .checked=${f.auto_buy_task}
              @change=${(e: Event) => {
                this._editing = { ...f, auto_buy_task: (e.target as HTMLInputElement).checked };
              }}
            />
            <span>${t("part_auto_buy", L)}</span>
          </label>
        </div>
        <div class="form-actions">
          <ha-button appearance="plain" @click=${() => (this._editing = null)}>${t("cancel", L)}</ha-button>
          <ha-button .disabled=${this._busy || !f.name.trim()} @click=${() => this._save()}
            >${t("save", L)}</ha-button
          >
        </div>
      </div>
    `;
  }

  /** Inventory value = Σ unit cost × tracked stock (#104). Parts without a
   *  price or without tracked stock contribute nothing; null = no part has
   *  both, so the chip stays hidden rather than showing a misleading 0. */
  private _inventoryValue(): number | null {
    let sum = 0, any = false;
    for (const p of this.parts) {
      const cost = typeof p.cost === "number" ? p.cost : null;
      const stock = typeof p.stock === "number" ? p.stock : null;
      if (cost !== null && stock !== null) { sum += cost * stock; any = true; }
    }
    return any ? sum : null;
  }

  protected render() {
    const L = this._lang;
    if (!this.parts.length && !this.canWrite) return nothing;
    return html`
      <div class="section-head">
        <h3>
          <ha-icon icon="mdi:package-variant"></ha-icon>
          ${t("parts_section", L)} (${this.parts.length})
          ${this._inventoryValue() !== null
            ? html`<span class="inventory-value" title=${t("parts_inventory_value", L)}
                >${t("parts_inventory_value", L)}:
                ${this._inventoryValue()!.toFixed(2)}&nbsp;${this.currencySymbol}</span>`
            : nothing}
        </h3>
        ${this.canWrite && !this._editing
          ? html`<ha-button appearance="plain" @click=${() => this._openAdd()}>
              <ha-icon icon="mdi:plus"></ha-icon> ${t("part_add", L)}
            </ha-button>`
          : nothing}
      </div>
      ${this._error ? html`<div class="error">${this._error}</div>` : nothing}
      ${this._editing ? this._renderForm() : nothing}
      ${this.parts.map((part) => this._renderRow(part))}
    `;
  }

  static styles = css`
    :host {
      display: block;
      margin: 12px 0;
    }
    .inventory-value {
      margin-left: 8px;
      font-size: 0.75em;
      font-weight: 400;
      color: var(--secondary-text-color);
      white-space: nowrap;
    }
    .section-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    h3 {
      display: flex;
      align-items: center;
      gap: 6px;
      margin: 8px 0;
    }
    .part-row {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 6px 4px;
      border-bottom: 1px solid var(--divider-color);
    }
    .part-row.low .part-icon {
      color: var(--warning-color, #ff9800);
    }
    .part-main {
      flex: 1;
      min-width: 0;
    }
    .part-name {
      font-weight: 500;
      display: flex;
      align-items: center;
      gap: 8px;
    }
    .part-name a {
      color: var(--primary-color);
      text-decoration: none;
    }
    .stock-badge {
      font-size: 12px;
      padding: 1px 8px;
      border-radius: 10px;
      background: var(--secondary-background-color);
    }
    .stock-badge.low {
      background: var(--warning-color, #ff9800);
      color: var(--text-primary-color, #fff);
    }
    .stock-badge .threshold {
      opacity: 0.7;
    }
    .part-meta {
      font-size: 12px;
      color: var(--secondary-text-color);
      display: flex;
      gap: 12px;
      flex-wrap: wrap;
    }
    .part-meta .loc ha-icon {
      --mdc-icon-size: 13px;
    }
    .restock-input {
      width: 64px;
      padding: 4px;
      border: 1px solid var(--divider-color);
      border-radius: 4px;
      background: var(--card-background-color);
      color: var(--primary-text-color);
    }
    .restock-input.invalid {
      border-color: var(--error-color, #f44336);
    }
    .docs-open {
      color: var(--primary-color);
    }
    .part-docs {
      padding: 0 4px 8px 34px;
      border-bottom: 1px solid var(--divider-color);
    }
    .part-form {
      border: 1px solid var(--divider-color);
      border-radius: 8px;
      padding: 12px;
      margin-bottom: 8px;
    }
    .form-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
      gap: 8px 12px;
    }
    .form-field {
      display: flex;
      flex-direction: column;
      gap: 2px;
      font-size: 12px;
      color: var(--secondary-text-color);
    }
    .form-field input[type="text"],
    .form-field input[type="number"] {
      padding: 6px;
      border: 1px solid var(--divider-color);
      border-radius: 4px;
      background: var(--card-background-color);
      color: var(--primary-text-color);
    }
    .form-field.checkbox {
      flex-direction: row;
      align-items: center;
      gap: 6px;
      align-self: end;
    }
    .form-actions {
      display: flex;
      justify-content: flex-end;
      gap: 8px;
      margin-top: 10px;
    }
    .error {
      color: var(--error-color);
      font-size: 13px;
      margin: 4px 0;
    }
  `;
}

customElements.define("maintenance-parts-section", MaintenancePartsSection);
