/**
 * TaskMate Incentive Card (shared base for Bonuses + Penalties) — QUAL-2.
 * One parameterised component; the two cards differ only by name/colour/icon/
 * service-prefix/i18n-prefix, supplied via the P object.
 */

const LitElement = customElements.get("hui-masonry-view")
  ? Object.getPrototypeOf(customElements.get("hui-masonry-view"))
  : Object.getPrototypeOf(customElements.get("hui-view"));

const html = LitElement.prototype.html;
const css = LitElement.prototype.css;

const _safeColor = (c, d) => (typeof c === "string" && /^#[0-9a-fA-F]{3,8}$/.test(c) ? c : d);

export function createIncentiveCard(P) {
  class IncentiveCard extends LitElement {
    static get properties() {
      return {
        hass: { type: Object },
        config: { type: Object },
        _selectedChildId: { type: String },
        _editMode: { type: Boolean },
        _loading: { type: Object },
        _editingBonus: { type: Object },   // bonus being edited (null = none)
        _showNewForm: { type: Boolean },
        _toast: { type: String },
        _newForm: { type: Object },
      };
    }

    shouldUpdate(changedProps) {
      if (changedProps.has("hass")) {
        return window.__taskmate_hasChanged
          ? window.__taskmate_hasChanged(changedProps.get("hass"), this.hass, this.config?.entity)
          : true;
      }
      return true;
    }

    _t(key, params) {
      const fn = window.__taskmate_localize;
      return fn ? fn(this.hass, key, params) : key;
    }

    _tp(key, params) {
      return this._t(P.i18n + "." + key, params);
    }

    constructor() {
      super();
      this._selectedChildId = null;
      this._editMode = false;
      this._loading = {};
      this._editingBonus = null;
      this._saving = false;
      this._showNewForm = false;
      this._toast = null;
      this._newForm = { name: "", points: "", description: "", icon: P.icon };
    }

    setConfig(config) {
      this.config = config;
    }

    static getConfigElement() {
      return document.createElement(P.tag + "-editor");
    }

    static getStubConfig() {
      return { entity: "sensor.taskmate_overview" };
    }

    static get styles() {
      const base = css`
        :host {
          display: block;
          --text-primary: var(--primary-text-color, #212121);
          --text-secondary: var(--secondary-text-color, #757575);
          --card-bg: var(--card-background-color, #fff);
          --divider: var(--divider-color, #e0e0e0);
        }

        ha-card { overflow: hidden; }

        /* ── Header ── */
        .card-header {
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 14px 18px;
          background: var(--taskmate-header-bg, var(--bonus-green));
          color: white;
        }
        .header-left { display: flex; align-items: center; gap: 12px; }
        .header-icon { --mdc-icon-size: 32px; opacity: 0.95; }
        .header-title { font-size: 1.3rem; font-weight: 600; }
        .bonus-count {
          background: rgba(255,255,255,0.2);
          padding: 4px 12px;
          border-radius: 16px;
          font-size: 0.9rem;
          font-weight: 500;
        }
        .header-actions { display: flex; gap: 8px; }
        .icon-btn {
          background: rgba(255,255,255,0.18);
          border: none;
          color: white;
          border-radius: 50%;
          width: 36px;
          height: 36px;
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          transition: background 0.2s;
          --mdc-icon-size: 20px;
        }
        .icon-btn:hover { background: rgba(255,255,255,0.32); }
        .icon-btn.active { background: rgba(255,255,255,0.35); }

        /* ── Child tabs ── */
        .child-tabs {
          display: flex;
          gap: 6px;
          padding: 10px 16px 0;
          overflow-x: auto;
          scrollbar-width: none;
        }
        .child-tabs::-webkit-scrollbar { display: none; }
        .child-tab {
          display: flex;
          align-items: center;
          gap: 6px;
          padding: 6px 14px;
          border-radius: 20px;
          background: var(--divider);
          border: 2px solid transparent;
          cursor: pointer;
          font-size: 0.9rem;
          font-weight: 500;
          white-space: nowrap;
          color: var(--text-secondary);
          transition: all 0.15s;
        }
        .child-tab ha-icon { --mdc-icon-size: 18px; }
        .child-tab.selected {
          background: var(--bonus-green-light);
          border-color: var(--bonus-green);
          color: var(--bonus-green);
        }

        /* ── Card body ── */
        .card-content {
          padding: 16px;
          display: flex;
          flex-direction: column;
          gap: 10px;
        }

        /* ── Bonus tile ── */
        .bonus-row {
          display: flex;
          align-items: center;
          gap: 14px;
          padding: 14px 16px;
          background: var(--card-bg);
          border: 1px solid var(--divider);
          border-radius: 12px;
          transition: box-shadow 0.2s, transform 0.15s;
        }
        .bonus-row:hover { box-shadow: 0 3px 10px rgba(0,0,0,0.09); transform: translateY(-1px); }

        /* Flash animation when bonus is applied */
        @keyframes flash-green {
          0%   { background: var(--bonus-green-light); }
          40%  { background: var(--bonus-green-flash); }
          100% { background: var(--card-bg); }
        }
        .bonus-row.flashing { animation: flash-green 0.6s ease forwards; }

        /* Points badge */
        .points-badge {
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          min-width: 64px;
          padding: 10px 8px;
          background: linear-gradient(135deg, var(--bonus-green) 0%, var(--bonus-green-dark) 100%);
          border-radius: 10px;
          flex-shrink: 0;
          box-shadow: 0 2px 6px var(--bonus-green-shadow);
        }
        .points-badge ha-icon { --mdc-icon-size: 20px; color: white; margin-bottom: 2px; }
        .points-value { font-size: 1.3rem; font-weight: 700; color: white; line-height: 1; }
        .points-label { font-size: 0.62rem; font-weight: 600; color: rgba(255,255,255,0.88); text-transform: uppercase; letter-spacing: 0.4px; margin-top: 2px; }

        /* Bonus info */
        .bonus-info { flex: 1; min-width: 0; }
        .bonus-name { font-size: 1.05rem; font-weight: 600; color: var(--text-primary); }
        .bonus-description { font-size: 0.85rem; color: var(--text-secondary); margin-top: 2px; }

        /* Apply button */
        .apply-btn {
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 6px;
          padding: 8px 16px;
          background: var(--bonus-green);
          color: white;
          border: none;
          border-radius: 8px;
          font-size: 0.9rem;
          font-weight: 600;
          cursor: pointer;
          transition: background 0.15s, transform 0.1s;
          white-space: nowrap;
          flex-shrink: 0;
          --mdc-icon-size: 16px;
        }
        .apply-btn:hover { background: var(--bonus-green-dark); }
        .apply-btn:active { transform: scale(0.97); }
        .apply-btn:disabled { opacity: 0.55; cursor: default; }

        /* Edit mode actions */
        .edit-actions { display: flex; gap: 6px; flex-shrink: 0; }
        .edit-btn {
          background: none;
          border: 1px solid var(--divider);
          border-radius: 8px;
          width: 34px;
          height: 34px;
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          color: var(--text-secondary);
          --mdc-icon-size: 18px;
          transition: all 0.15s;
        }
        .edit-btn:hover { background: var(--divider); color: var(--text-primary); }
        .edit-btn.delete:hover { background: var(--bonus-green-light); color: var(--bonus-green); border-color: var(--bonus-green); }

        /* Inline edit form */
        .edit-form {
          background: var(--ha-card-background, #f5f5f5);
          border: 1px solid var(--divider);
          border-radius: 12px;
          padding: 14px;
          display: flex;
          flex-direction: column;
          gap: 10px;
          margin-top: -4px;
        }
        .form-row { display: flex; gap: 10px; }
        .form-row.full { flex-direction: column; }
        .form-field {
          display: flex;
          flex-direction: column;
          gap: 4px;
          flex: 1;
          min-width: 0;
        }
        .form-field label { font-size: 0.8rem; font-weight: 600; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.4px; }
        .form-field input {
          padding: 8px 10px;
          border: 1px solid var(--divider);
          border-radius: 8px;
          font-size: 0.95rem;
          background: var(--card-bg);
          color: var(--text-primary);
          width: 100%;
          box-sizing: border-box;
        }
        .form-field input:focus { outline: 2px solid var(--bonus-green); border-color: transparent; }
        .form-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 4px; }
        .btn-save {
          padding: 8px 18px;
          background: var(--bonus-green);
          color: white;
          border: none;
          border-radius: 8px;
          font-size: 0.9rem;
          font-weight: 600;
          cursor: pointer;
        }
        .btn-save:hover { background: var(--bonus-green-dark); }
        .btn-cancel {
          padding: 8px 14px;
          background: none;
          color: var(--text-secondary);
          border: 1px solid var(--divider);
          border-radius: 8px;
          font-size: 0.9rem;
          cursor: pointer;
        }
        .btn-cancel:hover { background: var(--divider); }

        /* Add new button */
        .add-bonus-btn {
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 8px;
          padding: 12px;
          border: 2px dashed var(--divider);
          border-radius: 12px;
          background: none;
          color: var(--text-secondary);
          cursor: pointer;
          font-size: 0.95rem;
          font-weight: 500;
          transition: all 0.15s;
          --mdc-icon-size: 20px;
        }
        .add-bonus-btn:hover { border-color: var(--bonus-green); color: var(--bonus-green); background: var(--bonus-green-light); }

        /* Empty state */
        .empty-state {
          text-align: center;
          padding: 32px 16px;
          color: var(--text-secondary);
        }
        .empty-state ha-icon { --mdc-icon-size: 48px; opacity: 0.35; display: block; margin: 0 auto 12px; }
        .empty-state .empty-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }
        .empty-state .empty-sub { font-size: 0.85rem; }

        /* Toast */
        .toast {
          position: fixed;
          bottom: 24px;
          left: 50%;
          transform: translateX(-50%) translateY(0);
          background: #333;
          color: white;
          padding: 10px 20px;
          border-radius: 24px;
          font-size: 0.92rem;
          font-weight: 500;
          z-index: 9999;
          pointer-events: none;
          animation: toast-in 0.25s ease, toast-out 0.3s ease 2s forwards;
          white-space: normal;
          max-width: calc(100vw - 32px);
          text-align: center;
        }
        @keyframes toast-in {
          from { opacity: 0; transform: translateX(-50%) translateY(12px); }
          to   { opacity: 1; transform: translateX(-50%) translateY(0); }
        }
        @keyframes toast-out {
          to { opacity: 0; transform: translateX(-50%) translateY(8px); }
        }

        /* ══════════════════════════════════════════════════════════════════
           DESIGNED STYLES (playroom / console / cleanpro)
           Shared .tmd kit + tokens come from taskmate-design.js styles().
           Only card-specific layout classes live here.
        ══════════════════════════════════════════════════════════════════ */

        /* Child tab strip */
        .d-tabs { display: flex; gap: 8px; margin-bottom: 13px; align-items: center;
                  overflow-x: auto; scrollbar-width: none; }
        .d-tabs::-webkit-scrollbar { display: none; }
        .d-tab { display: flex; align-items: center; gap: 6px; padding: 4px 12px 4px 4px;
                 border-radius: 999px; font-weight: 800; font-size: 13px; cursor: pointer;
                 white-space: nowrap; color: var(--tmd-text);
                 background: color-mix(in srgb, var(--ac, var(--tmd-accent)) 16%, transparent);
                 border: 1px solid transparent; }
        .d-tab .av { box-shadow: none; }
        .d-tab.off { background: transparent; border-color: transparent; opacity: .55; }
        .d-tab.off .av { opacity: .6; }
        :host([data-tm-design="console"]) .d-tab { border-radius: 8px; font-size: 12px; font-weight: 700;
                 background: var(--tmd-surface-2); border-color: color-mix(in srgb, var(--ac, var(--tmd-accent)) 60%, transparent); }
        :host([data-tm-design="cleanpro"]) .d-tab { border-radius: 9px; font-size: 12.5px; font-weight: 700;
                 background: var(--tmd-surface-2); border-color: var(--tmd-accent); }

        /* Playroom rows */
        .d-pl-list { display: flex; flex-direction: column; gap: 10px; }
        .d-pl-row { display: flex; align-items: center; gap: 10px; background: var(--tmd-surface-2);
                    border-radius: 18px; padding: 11px; }
        .d-ic { width: 40px; height: 40px; border-radius: 13px; display: grid; place-items: center;
                font-size: 20px; flex: none; }
        .d-ic ha-icon { --mdc-icon-size: 22px; color: var(--tmd-text); }
        .d-pl-row .d-ic { background: color-mix(in srgb, var(--d-accent) 18%, transparent); }
        .d-name { flex: 1; min-width: 0; font-weight: 800; font-size: 14px;
                  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .d-pts { font-family: var(--tmd-font-display); font-weight: 800; font-size: 19px;
                 color: var(--d-accent); flex: none; }

        /* Console rows */
        .d-cn-row { display: flex; align-items: center; gap: 10px; background: var(--tmd-surface-2);
                    border-radius: 9px; padding: 10px; margin-top: 9px;
                    border: 1px solid color-mix(in srgb, var(--d-accent) 38%, var(--tmd-border));
                    box-shadow: 0 0 14px color-mix(in srgb, var(--d-accent) 22%, transparent); }
        .d-cn-row:first-of-type { margin-top: 0; }
        .d-cn-ic { font-size: 18px; flex: none; }
        .d-cn-ic ha-icon { --mdc-icon-size: 20px; color: var(--tmd-text); }
        .d-cn-mid { flex: 1; min-width: 0; }
        .d-cn-name { font-weight: 700; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .d-cn-sub { font-size: 10px; }
        .d-cn-pts { font-size: 18px; color: var(--d-accent);
                    text-shadow: 0 0 10px color-mix(in srgb, var(--d-accent) 60%, transparent); flex: none; }

        /* Clean Pro rows */
        .d-cp-row { display: flex; align-items: center; gap: 11px; padding: 10px 0; }
        .d-cp-ic { width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center;
                   font-size: 17px; background: var(--tmd-surface-2); flex: none; }
        .d-cp-ic ha-icon { --mdc-icon-size: 18px; color: var(--tmd-text); }
        .d-cp-name { flex: 1; min-width: 0; font-weight: 600; font-size: 13.5px;
                     overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .d-cp-pts { font-size: 14px; color: var(--d-accent); flex: none; }

        /* Designed inline edit / new form (reuse classic .edit-form look on token surfaces) */
        .d-form { background: var(--tmd-surface-2); border: 1px solid var(--tmd-border);
                  border-radius: var(--tmd-radius-sm); padding: 12px; display: flex;
                  flex-direction: column; gap: 10px; margin: 10px 0 4px; }
        .d-form .form-field input { background: var(--tmd-surface); color: var(--tmd-text);
                  border: 1px solid var(--tmd-border); }
        .d-form .form-field label { color: var(--tmd-dim); }
        .d-form-actions { display: flex; gap: 8px; justify-content: flex-end; }
        .d-add { width: 100%; justify-content: center; }
        .d-edit-actions { display: flex; gap: 6px; flex: none; }
        .d-foot { text-align: center; font-size: 12px; color: var(--tmd-dim); padding-top: 8px; }

        /* Scrollable list (parity with a long bonus list) */
        .d-scroll { max-height: 360px; overflow-y: auto; }
      `;
      const tokens = window.__taskmate_design && window.__taskmate_design.styles
        ? window.__taskmate_design.styles() : null;
      return tokens ? [tokens, base] : base;
    }

    _getState() {
      const entityId = this.config?.entity || "sensor.taskmate_overview";
      return this.hass?.states[entityId];
    }

    _getAttrs() {
      const entityId = this.config?.entity || "sensor.taskmate_overview";
      if (window.__taskmate_attrs) {
        return window.__taskmate_attrs(this.hass, entityId);
      }
      return this._getState()?.attributes || {};
    }

    _getChildren() {
      return this._getAttrs().children || [];
    }

    _getBonuses() {
      return this._getAttrs()[P.attr] || [];
    }

    _getSelectedChild() {
      const children = this._getChildren();
      if (!children.length) return null;
      if (this._selectedChildId) return children.find(c => c.id === this._selectedChildId) || children[0];
      return children[0];
    }

    _getPointsName() {
      return this._getAttrs().points_name || this._t("common.stars");
    }

    /* ── Roles (#749) ──────────────────────────────────────────────────────
       Applying an existing bonus/penalty is a day-to-day parent action, so
       admins *and* TaskMate parents get the Apply buttons. Defining one is
       structural config, so the manage controls (pencil, add/edit/delete) are
       admin-only — matching the `_parent` / `_admin` service gates. A child on
       a shared tablet sees the list with no buttons. */

    _canApply() {
      return window.__taskmate_is_parent(this.hass);
    }

    _canManage() {
      return !!this.hass?.user?.is_admin;
    }

    _toggleEditMode() {
      if (!this._canManage()) return;
      this._editMode = !this._editMode;
      this._editingBonus = null;
      this._showNewForm = false;
    }

    _getVisibleBonuses() {
      const child = this._getSelectedChild();
      if (!child) return this._getBonuses();
      return this._getBonuses().filter(b =>
        !b.assigned_to?.length || b.assigned_to.includes(child.id)
      );
    }

    _selectChild(id) {
      this._selectedChildId = id;
      this._editingBonus = null;
      this._showNewForm = false;
    }

    async _applyBonus(bonus) {
      const child = this._getSelectedChild();
      if (!child || !this._canApply()) return;
      const key = bonus.id;
      if (this._loading[key]) return;
      this._loading = { ...this._loading, [key]: true };

      try {
        await this.hass.callService("taskmate", `apply_${P.kind}`, {
          [P.idKey]: bonus.id,
          child_id: child.id,
        });
        this._showToast(this._tp('toast_applied', { points: bonus.points, pointsName: this._getPointsName(), childName: child.name }));
        // Flash the row
        const row = this.shadowRoot.querySelector(`[data-bonus-id="${bonus.id}"]`);
        if (row) {
          row.classList.add("flashing");
          setTimeout(() => row.classList.remove("flashing"), 700);
        }
      } catch (e) {
        this._showToast(this._tp('toast_apply_failed'));
      } finally {
        this._loading = { ...this._loading, [key]: false };
      }
    }

    _showToast(msg) {
      this._toast = null;
      clearTimeout(this._toastTimer);
      // Force re-render with new toast
      this._toastTimer = setTimeout(() => {
        this._toast = msg;
        this._toastTimer = setTimeout(() => { this._toast = null; }, 2700);
      }, 10);
    }

    disconnectedCallback() {
      super.disconnectedCallback();
      clearTimeout(this._toastTimer);
    }

    _startEdit(bonus) {
      this._editingBonus = { ...bonus };
      this._showNewForm = false;
    }

    _cancelEdit() {
      this._editingBonus = null;
    }

    async _saveEdit() {
      if (!this._editingBonus?.name || !this._editingBonus?.points) return;
      if (this._saving) return;
      this._saving = true;
      try {
        await this.hass.callService("taskmate", `update_${P.kind}`, {
          [P.idKey]: this._editingBonus.id,
          name: this._editingBonus.name,
          points: parseInt(this._editingBonus.points, 10),
          description: this._editingBonus.description || "",
          icon: this._editingBonus.icon || P.icon,
        });
        this._editingBonus = null;
      } catch (e) {
        this._showToast(this._tp('toast_save_failed'));
      } finally {
        this._saving = false;
      }
    }

    async _deleteBonus(id) {
      try {
        await this.hass.callService("taskmate", `remove_${P.kind}`, { [P.idKey]: id });
      } catch (e) {
        this._showToast(this._tp('toast_delete_failed'));
      }
    }

    _openNewForm() {
      this._showNewForm = true;
      this._editingBonus = null;
      this._newForm = { name: "", points: "", description: "", icon: P.icon };
    }

    async _saveNew() {
      if (!this._newForm.name || !this._newForm.points) return;
      if (this._saving) return;
      this._saving = true;
      try {
        await this.hass.callService("taskmate", `add_${P.kind}`, {
          name: this._newForm.name,
          points: parseInt(this._newForm.points, 10),
          description: this._newForm.description || "",
          icon: this._newForm.icon || P.icon,
        });
        this._showNewForm = false;
        this._newForm = { name: "", points: "", description: "", icon: P.icon };
        this._showToast(this._tp('toast_saved'));
      } catch (e) {
        this._showToast(this._tp('toast_add_failed'));
      } finally {
        this._saving = false;
      }
    }

    _renderChildTabs() {
      const children = this._getChildren();
      if (children.length <= 1) return html``;
      const selected = this._getSelectedChild();
      return html`
        <div class="child-tabs">
          ${children.map(c => html`
            <div class="child-tab ${selected?.id === c.id ? "selected" : ""}"
                 @click=${() => this._selectChild(c.id)}>
              <ha-icon icon="${c.avatar || "mdi:account-circle"}"></ha-icon>
              ${c.name}
            </div>
          `)}
        </div>
      `;
    }

    _renderBonusRow(b) {
      const isEditing = this._editingBonus?.id === b.id;
      const isLoading = this._loading[b.id];
      const child = this._getSelectedChild();
      const pointsName = this._getPointsName();

      return html`
        <div class="bonus-row" data-bonus-id="${b.id}">
          <div class="points-badge">
            <ha-icon icon="${b.icon || P.icon}"></ha-icon>
            <div class="points-value">${P.sign}${b.points}</div>
            <div class="points-label">${pointsName}</div>
          </div>

          <div class="bonus-info">
            <div class="bonus-name">${b.name}</div>
            ${b.description ? html`<div class="bonus-description">${b.description}</div>` : ""}
          </div>

          ${this._editMode ? html`
            <div class="edit-actions">
              <button class="edit-btn" title="${this._tp('btn_edit_title')}" aria-label="${this._tp('btn_edit_title')}" @click=${() => this._startEdit(b)}>
                <ha-icon icon="mdi:pencil"></ha-icon>
              </button>
              <button class="edit-btn delete" title="${this._tp('btn_delete_title')}" aria-label="${this._tp('btn_delete_title')}" @click=${() => this._deleteBonus(b.id)}>
                <ha-icon icon="mdi:trash-can-outline"></ha-icon>
              </button>
            </div>
          ` : this._canApply() ? html`
            <button class="apply-btn"
                    ?disabled=${isLoading || !child}
                    @click=${() => this._applyBonus(b)}>
              ${isLoading
                ? html`<ha-icon icon="mdi:loading" class="spin"></ha-icon>`
                : html`<ha-icon icon="${P.applyIcon}"></ha-icon> ${this._t('common.apply')}`
              }
            </button>
          ` : ""}
        </div>
        ${isEditing ? this._renderEditForm() : ""}
      `;
    }

    _renderEditForm() {
      const b = this._editingBonus;
      return html`
        <div class="edit-form">
          <div class="form-row">
            <div class="form-field" style="flex:2">
              <label>${this._tp('form_name_label')}</label>
              <input type="text" .value=${b.name}
                @input=${e => this._editingBonus = { ...b, name: e.target.value }} />
            </div>
            <div class="form-field" style="flex:1">
              <label>${this._tp('form_points_label')}</label>
              <input type="number" min="1" .value=${b.points}
                @input=${e => this._editingBonus = { ...b, points: e.target.value }} />
            </div>
          </div>
          <div class="form-row">
            <div class="form-field">
              <label>${this._tp('form_icon_label')}</label>
              <input type="text" .value=${b.icon || P.icon}
                @input=${e => this._editingBonus = { ...b, icon: e.target.value }} />
            </div>
          </div>
          <div class="form-row full">
            <div class="form-field">
              <label>${this._tp('form_description_label')}</label>
              <input type="text" .value=${b.description || ""}
                @input=${e => this._editingBonus = { ...b, description: e.target.value }} />
            </div>
          </div>
          <div class="form-actions">
            <button class="btn-cancel" @click=${this._cancelEdit}>${this._t('common.cancel')}</button>
            <button class="btn-save" @click=${this._saveEdit}>${this._t('common.save')}</button>
          </div>
        </div>
      `;
    }

    _renderNewForm() {
      const f = this._newForm;
      return html`
        <div class="edit-form">
          <div class="form-row">
            <div class="form-field" style="flex:2">
              <label>${this._tp('form_name_label')}</label>
              <input type="text" placeholder="${this._tp('form_name_placeholder')}" .value=${f.name}
                @input=${e => this._newForm = { ...f, name: e.target.value }} />
            </div>
            <div class="form-field" style="flex:1">
              <label>${this._tp('form_points_label')}</label>
              <input type="number" min="1" placeholder="10" .value=${f.points}
                @input=${e => this._newForm = { ...f, points: e.target.value }} />
            </div>
          </div>
          <div class="form-row">
            <div class="form-field">
              <label>${this._tp('form_icon_label')}</label>
              <input type="text" .value=${f.icon}
                @input=${e => this._newForm = { ...f, icon: e.target.value }} />
            </div>
          </div>
          <div class="form-row full">
            <div class="form-field">
              <label>${this._tp('form_description_label')}</label>
              <input type="text" placeholder="${this._tp('form_description_placeholder')}" .value=${f.description}
                @input=${e => this._newForm = { ...f, description: e.target.value }} />
            </div>
          </div>
          <div class="form-actions">
            <button class="btn-cancel" @click=${() => this._showNewForm = false}>${this._t('common.cancel')}</button>
            <button class="btn-save" @click=${this._saveNew}>${this._tp('add_bonus')}</button>
          </div>
        </div>
      `;
    }

    render() {
      if (!this.hass || !this.config) return html``;

      const design = window.__taskmate_design
        ? window.__taskmate_design.apply(this, this.hass, this.config, this.config.entity || "sensor.taskmate_overview")
        : "classic";
      if (design !== "classic") return this._renderDesigned(design);

      const bonuses = this._getBonuses();
      const visible = this._getVisibleBonuses();
      const child = this._getSelectedChild();

      return html`
        <ha-card>
          <style>:host { --taskmate-header-bg: ${_safeColor(this.config.header_color, P.accent)}; --bonus-green: ${P.accent}; --bonus-green-dark: ${P.accentDark}; --bonus-green-light: ${P.accentLight}; --bonus-green-flash: ${P.accentFlash}; --bonus-green-shadow: ${P.accentShadow}; }</style>
          <div class="card-header">
            <div class="header-left">
              <ha-icon class="header-icon" icon="${P.headerIcon}"></ha-icon>
              <span class="header-title">${this.config.title || this._tp('default_title')}</span>
            </div>
            <div class="header-actions">
              ${bonuses.length ? html`
                <span class="bonus-count">${bonuses.length}</span>
              ` : ""}
              ${this._canManage() ? html`
                <button class="icon-btn ${this._editMode ? "active" : ""}" title="${this._tp('manage_title')}"
                        @click=${this._toggleEditMode}>
                  <ha-icon icon="mdi:pencil"></ha-icon>
                </button>
              ` : ""}
            </div>
          </div>

          ${this._renderChildTabs()}

          <div class="card-content">
            ${visible.length === 0 && !this._showNewForm ? html`
              <div class="empty-state">
                <ha-icon icon="${P.icon}"></ha-icon>
                <div class="empty-title">${this._tp('empty_title')}</div>
                <div class="empty-sub">${this._tp('empty_sub')}</div>
              </div>
            ` : ""}

            ${visible.map(b => this._renderBonusRow(b))}

            ${this._editMode ? html`
              ${this._showNewForm
                ? this._renderNewForm()
                : html`
                  <button class="add-bonus-btn" @click=${this._openNewForm}>
                    <ha-icon icon="mdi:plus"></ha-icon>
                    ${this._tp('new_bonus')}
                  </button>
                `}
            ` : ""}

            ${child && !this._editMode && this._canApply() ? html`
              <div style="text-align:center; font-size:0.8rem; color:var(--text-secondary); padding-top:4px;">
                ${this._tp('applying_to', { childName: child.name, points: child.points, pointsName: this._getPointsName() })}
              </div>
            ` : ""}
          </div>
        </ha-card>

        ${this._toast ? html`<div class="toast">${this._toast}</div>` : ""}
      `;
    }

    /* ══════════════════════════════════════════════════════════════════════
       DESIGNED STYLES (playroom / console / cleanpro) — see taskmate-design.js
       Ported from docs/design/redesigns/frag/14-bonuses.html.
    ══════════════════════════════════════════════════════════════════════ */

    _designTone(i) { return `var(--tmd-c${(i % 6) + 1})`; }

    _av(child, tone, size) {
      const a = child.avatar || "";
      const inner = a.startsWith("mdi:")
        ? html`<ha-icon icon="${a}"></ha-icon>`
        : a
          ? html`<img src="${a}" alt="${child.name}">`
          : (child.name || "?").split(" ").map(w => w[0]).slice(0, 2).join("").toUpperCase();
      return html`<div class="av" style="--av:${size}px;--ac:${tone}">${inner}</div>`;
    }

    _renderDesignTabs(design) {
      const children = this._getChildren();
      if (children.length <= 1) return "";
      const selected = this._getSelectedChild();
      const avSize = design === "playroom" ? 28 : 24;
      return html`
        <div class="d-tabs">
          ${children.map((c, i) => {
            const tone = this._designTone(i);
            const isSel = selected?.id === c.id;
            // Always show the child's name on every tab (selected and not).
            return html`<div class="d-tab ${isSel ? "" : "off"}" style="--ac:${tone}"
                @click=${() => this._selectChild(c.id)}>
              ${this._av(c, tone, avSize)}${c.name}</div>`;
          })}
        </div>`;
    }

    _designIcon(b) {
      const icon = b.icon || P.icon;
      return icon.startsWith("mdi:")
        ? html`<ha-icon icon="${icon}"></ha-icon>`
        : html`${icon}`;
    }

    _designApplyBtn(b, klass) {
      if (!this._canApply()) return "";
      const child = this._getSelectedChild();
      const isLoading = this._loading[b.id];
      return html`
        <button class="btn ${klass}" ?disabled=${isLoading || !child}
                @click=${() => this._applyBonus(b)}>
          ${isLoading
            ? html`<ha-icon icon="mdi:loading" class="spin"></ha-icon>`
            : this._t('common.apply')}
        </button>`;
    }

    _designEditActions(b) {
      return html`
        <div class="d-edit-actions">
          <button class="btn ghost sm" title="${this._tp('btn_edit_title')}"
                  aria-label="${this._tp('btn_edit_title')}" @click=${() => this._startEdit(b)}>
            <ha-icon icon="mdi:pencil"></ha-icon>
          </button>
          <button class="btn ghost sm" title="${this._tp('btn_delete_title')}"
                  aria-label="${this._tp('btn_delete_title')}" @click=${() => this._deleteBonus(b.id)}>
            <ha-icon icon="mdi:trash-can-outline"></ha-icon>
          </button>
        </div>`;
    }

    _renderDesignForm(model, onField, onCancel, onSave, saveLabel) {
      return html`
        <div class="d-form">
          <div class="form-row">
            <div class="form-field" style="flex:2">
              <label>${this._tp('form_name_label')}</label>
              <input type="text" placeholder="${this._tp('form_name_placeholder')}" .value=${model.name || ""}
                @input=${e => onField('name', e.target.value)} />
            </div>
            <div class="form-field" style="flex:1">
              <label>${this._tp('form_points_label')}</label>
              <input type="number" min="1" placeholder="10" .value=${model.points || ""}
                @input=${e => onField('points', e.target.value)} />
            </div>
          </div>
          <div class="form-row">
            <div class="form-field">
              <label>${this._tp('form_icon_label')}</label>
              <input type="text" .value=${model.icon || P.icon}
                @input=${e => onField('icon', e.target.value)} />
            </div>
          </div>
          <div class="form-row full">
            <div class="form-field">
              <label>${this._tp('form_description_label')}</label>
              <input type="text" placeholder="${this._tp('form_description_placeholder')}" .value=${model.description || ""}
                @input=${e => onField('description', e.target.value)} />
            </div>
          </div>
          <div class="d-form-actions">
            <button class="btn ghost sm" @click=${onCancel}>${this._t('common.cancel')}</button>
            <button class="btn sm" @click=${onSave}>${saveLabel}</button>
          </div>
        </div>`;
    }

    _renderDesignedRow(b, design) {
      const isEditing = this._editingBonus?.id === b.id;
      const accent = P.designAccent;
      const pv = P.dpts(b.points);

      let row;
      if (design === "playroom") {
        row = html`
          <div class="d-pl-row" style="--d-accent:${accent}" data-bonus-id="${b.id}">
            <div class="d-ic">${this._designIcon(b)}</div>
            <div class="d-name">${b.name}</div>
            <div class="d-pts">${pv}</div>
            ${this._editMode ? this._designEditActions(b) : this._designApplyBtn(b, "good")}
          </div>`;
      } else if (design === "console") {
        const pointsName = this._getPointsName();
        row = html`
          <div class="d-cn-row" style="--d-accent:${accent}" data-bonus-id="${b.id}">
            <div class="d-cn-ic">${this._designIcon(b)}</div>
            <div class="d-cn-mid">
              <div class="d-cn-name">${b.name}</div>
              <div class="num muted d-cn-sub">${pv} ${pointsName}${b.description ? html` · ${b.description}` : ""}</div>
            </div>
            <div class="num d-cn-pts">${pv}</div>
            ${this._editMode ? this._designEditActions(b) : this._designApplyBtn(b, "good sm")}
          </div>`;
      } else {
        row = html`
          <div class="d-cp-row" style="--d-accent:${accent}" data-bonus-id="${b.id}">
            <div class="d-cp-ic">${this._designIcon(b)}</div>
            <div class="d-cp-name">${b.name}</div>
            <div class="num d-cp-pts">${pv}</div>
            ${this._editMode ? this._designEditActions(b) : this._designApplyBtn(b, "sm")}
          </div>`;
      }

      const divider = design === "cleanpro" ? html`<div class="divide" style="margin:0"></div>` : "";
      const editForm = isEditing
        ? this._renderDesignForm(
            this._editingBonus,
            (k, v) => { this._editingBonus = { ...this._editingBonus, [k]: v }; },
            () => this._cancelEdit(),
            () => this._saveEdit(),
            this._t('common.save'))
        : "";
      return html`${row}${divider}${editForm}`;
    }

    _renderDesigned(design) {
      const hd = _safeColor(this.config.header_color, P.designHd);
      const bonuses = this._getBonuses();
      const visible = this._getVisibleBonuses();
      const child = this._getSelectedChild();
      const tone = this._designTone(0);

      const icon = P.designIcons[design] || P.designIcons.playroom;

      const header = html`
        <div class="tmd-hd" style="--ac:${tone}">
          <span class="ic">${icon}</span>
          <span class="tt">${this.config.title || this._tp('default_title')}${
            bonuses.length ? html`<small>${bonuses.length}</small>` : ""}</span>
          ${this._canManage() ? html`
            <button class="btn ghost sm" style="margin-left:auto"
                    title="${this._tp('manage_title')}"
                    @click=${this._toggleEditMode}>
              <ha-icon icon="mdi:pencil"></ha-icon>
            </button>
          ` : ""}
        </div>`;

      const rows = design === "playroom"
        ? html`<div class="d-scroll d-pl-list">${visible.map(b => this._renderDesignedRow(b, design))}</div>`
        : html`<div class="d-scroll">${visible.map(b => this._renderDesignedRow(b, design))}</div>`;

      const body = html`
        ${this._renderDesignTabs(design)}
        ${visible.length === 0 && !this._showNewForm
          ? html`<div class="tmd-empty">${this._tp('empty_title')}</div>`
          : rows}
        ${this._editMode
          ? (this._showNewForm
              ? this._renderDesignForm(
                  this._newForm,
                  (k, v) => { this._newForm = { ...this._newForm, [k]: v }; },
                  () => { this._showNewForm = false; },
                  () => this._saveNew(),
                  this._tp('add_bonus'))
              : html`<button class="btn ghost sm d-add" style="margin-top:11px"
                            @click=${this._openNewForm}>
                  <ha-icon icon="mdi:plus"></ha-icon> ${this._tp('new_bonus')}
                </button>`)
          : ""}
        ${child && !this._editMode && this._canApply()
          ? html`<div class="d-foot">${this._tp('applying_to', { childName: child.name, points: child.points, pointsName: this._getPointsName() })}</div>`
          : ""}`;

      return html`
        <ha-card class="tmd" style="--hd:${hd}">
          ${header}
          <div class="tmd-bd">${body}</div>
        </ha-card>
        ${this._toast ? html`<div class="toast">${this._toast}</div>` : ""}`;
    }
  }

  class IncentiveCardEditor extends LitElement {
    static get properties() {
      return { hass: { type: Object }, config: { type: Object } };
    }

    _t(key, params) {
      const fn = window.__taskmate_localize;
      return fn ? fn(this.hass, key, params) : key;
    }

    _tp(key, params) {
      return this._t(P.i18n + "." + key, params);
    }

    static get styles() {
      return css`
        :host { display: block; }
        ha-form { display: block; margin-bottom: 16px; }
        .info-note { font-size: 0.85rem; color: var(--secondary-text-color); background: var(--secondary-background-color, #f5f5f5); border-radius: 8px; padding: 10px 14px; line-height: 1.4; margin-bottom: 16px; display: flex; gap: 10px; align-items: flex-start; }
        .info-note ha-icon { flex-shrink: 0; color: var(--primary-color); --mdc-icon-size: 20px; margin-top: 1px; }
        .colour-field { display: flex; flex-direction: column; gap: 8px; padding: 12px 16px; border: 1px solid var(--outline-color, var(--divider-color, #e0e0e0)); border-radius: 4px; background: var(--mdc-text-field-fill-color, var(--card-background-color)); }
        .colour-field-label { font-size: 0.82rem; color: var(--primary-color); font-weight: 500; }
        .colour-field-body { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
        .colour-swatch-wrapper { position: relative; width: 36px; height: 36px; border-radius: 50%; overflow: hidden; cursor: pointer; border: 2px solid var(--divider-color, #e0e0e0); flex-shrink: 0; }
        .colour-swatch-wrapper input[type="color"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; border: 0; padding: 0; }
        .colour-swatch-preview { position: absolute; inset: 0; pointer-events: none; }
        .colour-hex { font-family: var(--code-font-family, monospace); font-size: 0.85rem; color: var(--secondary-text-color); min-width: 70px; }
        .colour-presets { display: flex; gap: 6px; flex-wrap: wrap; }
        .preset-swatch { width: 22px; height: 22px; border-radius: 50%; cursor: pointer; border: 2px solid var(--divider-color, #e0e0e0); transition: transform 0.1s; padding: 0; }
        .preset-swatch:hover { transform: scale(1.15); }
        .preset-swatch.active { border-color: var(--primary-text-color); box-shadow: 0 0 0 2px var(--primary-color); }
        .colour-reset { font-size: 0.78rem; color: var(--secondary-text-color); background: none; border: 1px solid var(--divider-color, #e0e0e0); border-radius: 4px; padding: 4px 10px; cursor: pointer; margin-left: auto; }
        .colour-helper { color: var(--secondary-text-color); font-size: 0.82rem; line-height: 1.3; }
      `;
    }

    setConfig(config) { this.config = config; }

    _buildSchema() {
      return [
        { name: 'entity', selector: { entity: { domain: 'sensor' } } },
        { name: 'title', selector: { text: {} } },
        {
          name: 'card_design',
          selector: {
            select: {
              options: window.__taskmate_design
                ? window.__taskmate_design.editorOptions(this._t.bind(this))
                : [{ value: 'global', label: 'Use global default' }],
              mode: 'dropdown',
            },
          },
        },
      ];
    }

    _computeLabel = (entry) => {
      const labels = {
        entity: this._t('common.editor.overview_entity'),
        title: this._t('common.editor.card_title'),
        card_design: this._t('common.design.field_label'),
      };
      return labels[entry.name] ?? entry.name;
    };

    _computeHelper = (entry) => {
      const helpers = {
        entity: this._t('common.editor.overview_entity_helper'),
      };
      return helpers[entry.name] ?? '';
    };

    render() {
      if (!this.hass || !this.config) return html``;
      const data = {
        entity: this.config.entity || '',
        title: this.config.title || '',
        card_design: this.config.card_design || 'global',
      };
      return html`
        <div class="info-note">
          <ha-icon icon="mdi:information-outline"></ha-icon>
          <span>${this._tp('editor.manage_note')}</span>
        </div>
        <ha-form
          .hass=${this.hass}
          .data=${data}
          .schema=${this._buildSchema()}
          .computeLabel=${this._computeLabel}
          .computeHelper=${this._computeHelper}
          @value-changed=${this._formChanged}
        ></ha-form>
        ${this._renderColourPicker('header_color', P.accent)}
      `;
    }

    _renderColourPicker(key, defaultValue) {
      const d = window.__taskmate_design;
      const current = this.config[key] || defaultValue;
      if (!d || !d.colourPicker) return html``;
      return d.colourPicker({
        defaultValue, current,
        label: this._t('common.editor.header_colour'),
        helper: this._t('common.editor.header_colour_helper'),
        resetLabel: this._t('common.reset'),
        onInput: (v) => this._update(key, v),
        onPreset: (v) => this._update(key, v),
        onReset: () => this._update(key, defaultValue),
      });
    }

    _formChanged(e) {
      const newValues = e.detail.value || {};
      const newConfig = { ...this.config };
      for (const [key, value] of Object.entries(newValues)) {
        if (value === '' || value === null || value === undefined) delete newConfig[key];
        else if (key === 'card_design' && value === 'global') delete newConfig[key];
        else newConfig[key] = value;
      }
      this._fire(newConfig);
    }

    _update(key, value) {
      this._fire({ ...this.config, [key]: value });
    }

    _fire(config) {
      this.dispatchEvent(new CustomEvent('config-changed', { detail: { config }, bubbles: true, composed: true }));
    }
  }

  customElements.define(P.tag, IncentiveCard);
  customElements.define(P.tag + "-editor", IncentiveCardEditor);
  window.customCards = window.customCards || [];
  window.customCards.push({
    type: P.tag, name: P.cardName, description: P.cardDesc, preview: true,
    getEntitySuggestion: (hass, entityId) =>
      window.__taskmate_suggest(hass, entityId, P.tag, "overview"),
  });
  console.info("%c TASKMATE " + P.bannerName + " CARD ",
    "background:" + P.bannerColor + ";color:white;font-weight:bold;padding:2px 4px;border-radius:4px;");
}
