import type { AggregateUsage, AIProviderRegistryStatusEntry } from '@genesislcap/foundation-ai';
import { html, ref, repeat, slotted, when, type ViewTemplate } from '@genesislcap/web-core';
import type { FoundationAiAssistant } from '../../main/main';
import { ANIMATION_DEFS, type AiAssistantAnimation } from '../../main/main.types';
import type { AssistantAppSettingsToggle } from '../../provider/assistant-app-settings';
import {
  formatCostSessionDate,
  type CostSessionModelEntry,
  type CostSessionRecord,
} from '../../utils/cost-session-history';
import { formatCompactTokenCountSafe } from '../../utils/sum-tokens';

const SESSION_COST_DECIMALS = 2;

type AnimationCategoryGroup = {
  category: string;
  items: Array<{ value: AiAssistantAnimation; label: string; description: string }>;
};

type AnimationPickerRow =
  | { kind: 'category'; category: string }
  | {
      kind: 'option';
      value: AiAssistantAnimation;
      label: string;
      description: string;
    };

/** Animation options grouped by {@link ANIMATION_DEFS} category for the settings list. */
const animationCategoryGroups: AnimationCategoryGroup[] = (() => {
  const groups = new Map<string, AnimationCategoryGroup['items']>();
  for (const [value, def] of Object.entries(ANIMATION_DEFS) as Array<
    [AiAssistantAnimation, (typeof ANIMATION_DEFS)[AiAssistantAnimation]]
  >) {
    const items = groups.get(def.category) ?? [];
    items.push({ value, label: def.label, description: def.description });
    groups.set(def.category, items);
  }
  return [...groups.entries()].map(([category, items]) => ({ category, items }));
})();

const animationPickerRows: AnimationPickerRow[] = animationCategoryGroups.flatMap((group) => [
  { kind: 'category', category: group.category },
  ...group.items.map(
    (item): AnimationPickerRow => ({
      kind: 'option',
      value: item.value,
      label: item.label,
      description: item.description,
    }),
  ),
]);

const toggleAnimationEnabled = (
  host: FoundationAiAssistant,
  value: AiAssistantAnimation,
  enabled: boolean,
): void => {
  const next = enabled
    ? [...host.enabledAnimations, value]
    : host.enabledAnimations?.filter((animation) => animation !== value);
  host.setEnabledAnimations(next);
};

const readSwitchChecked = (event: Event): boolean => (event.target as HTMLInputElement).checked;

/** Inline animation picker — categories and options always visible (no multiselect popup). */
const animationsPickerTemplate = (
  switchTag: string,
): ViewTemplate<FoundationAiAssistant> => html<FoundationAiAssistant>`
  <div class="settings-animation-list" part="toggle-animations">
    ${repeat(
      (x) => {
        void x.enabledAnimations;
        return animationPickerRows;
      },
      html<AnimationPickerRow, FoundationAiAssistant>`
        ${when(
          (row) => row.kind === 'category',
          html<AnimationPickerRow>`
            <div class="settings-animation-category-label" part="animation-category">
              ${(row) => (row.kind === 'category' ? row.category : '')}
            </div>
          `,
        )}
        ${when(
          (row) => row.kind === 'option',
          html<AnimationPickerRow, FoundationAiAssistant>`
            <div class="settings-toggle-row settings-animation-option" part="animation-option">
              <div class="settings-toggle-text">
                <div class="settings-toggle-title">
                  ${(row) => (row.kind === 'option' ? row.label : '')}
                </div>
                <div class="settings-toggle-desc">
                  ${(row) => (row.kind === 'option' ? row.description : '')}
                </div>
              </div>
              <div class="settings-toggle-control">
                <${switchTag}
                  part="animation-toggle"
                  :checked=${(row, c) =>
                    row.kind === 'option' && c.parent.enabledAnimations.includes(row.value)}
                  @change=${(row, c) => {
                    if (row.kind !== 'option') return;
                    toggleAnimationEnabled(c.parent, row.value, readSwitchChecked(c.event));
                  }}
                ></${switchTag}>
              </div>
            </div>
          `,
        )}
      `,
    )}
  </div>
`;

/**
 * Tab identifiers for the modal tab bar.
 *
 * The values are deliberately NOT renamed to match the visible labels ("Configuration" and
 * "Usage"): they are the public API that hosts read and write via `settingsModalTab`, and are
 * matched by the `settings-modal-tab-*` shadow parts. Renaming a label is cosmetic; renaming
 * these would break consumers.
 */
export type SettingsModalTab = 'settings' | 'cost';

/** Shared label + control row used for switches, buttons, and multiselects. */
const settingsToggleRow = (
  title: string,
  description: string,
  part: string,
  control: ViewTemplate<FoundationAiAssistant>,
  layout: 'row' | 'column' = 'row',
) => html<FoundationAiAssistant>`
  <div class="settings-toggle-row ${layout === 'column' ? 'is-stacked' : ''}" part="${part}-row">
    <div class="settings-toggle-text">
      <div class="settings-toggle-title">${title}</div>
      <div class="settings-toggle-desc">${description}</div>
    </div>
    <div class="settings-toggle-control">${control}</div>
  </div>
`;

const chatBotToggleRow = (
  title: string,
  description: string,
  switchTag: string,
  part: string,
  checked: (x: FoundationAiAssistant) => boolean,
  onChange: (x: FoundationAiAssistant, c: { event: Event }) => void,
) =>
  settingsToggleRow(
    title,
    description,
    part,
    html<FoundationAiAssistant>`
      <${switchTag}
        part="${part}"
        :checked=${checked}
        @change=${onChange}
      ></${switchTag}>
    `,
  );

const platformContextUsageTemplate = (
  progressTag: string,
): ViewTemplate<FoundationAiAssistant> => html<FoundationAiAssistant>`
  ${when(
    (x) =>
      x.chatConfig.ui?.showContextUsage !== false &&
      x.contextTokens != null &&
      x.contextLimit != null,
    html<FoundationAiAssistant>`
      <div class="settings-context-usage" part="context-usage">
        <div class="settings-context-header">
          <span class="settings-context-label">Context window</span>
          <span class="settings-context-tokens">
            ${(x) => x.contextTokens!.toLocaleString()} /
            ${(x) => x.contextLimit!.toLocaleString()} tokens
            (${(x) => Math.round((x.contextTokens! / x.contextLimit!) * 100)}%)
          </span>
        </div>
        <${progressTag}
          part="context-progress"
          value="${(x) => Math.min(100, Math.round((x.contextTokens! / x.contextLimit!) * 100))}"
        ></${progressTag}>
      </div>
    `,
  )}
`;

const chatBotSettingsTemplate = (
  switchTag: string,
  buttonTag: string,
): ViewTemplate<FoundationAiAssistant> => html<FoundationAiAssistant>`
  ${when(
    (x) => x.chatConfig.ui?.showToolCalls != null,
    chatBotToggleRow(
      'Show tool calls',
      "Display each tool the chatbot invokes — file reads, searches, edits and commands — inline as it works, so you can follow exactly what it's doing behind the scenes.",
      switchTag,
      'toggle-tool-calls',
      (x) => x.showToolCalls,
      (x, c) => {
        x.showToolCalls = readSwitchChecked(c.event);
      },
    ),
  )}
  ${when(
    (x) => x.chatConfig.ui?.showThinkingSteps != null,
    chatBotToggleRow(
      'Show thinking',
      "Reveal the assistant's reasoning as it works through your request, surfacing its intermediate thought process before it commits to a response.",
      switchTag,
      'toggle-thinking',
      (x) => x.showThinkingSteps,
      (x, c) => {
        x.showThinkingSteps = readSwitchChecked(c.event);
      },
    ),
  )}
  ${when(
    (x) => x.chatConfig.ui?.showNarration != null,
    chatBotToggleRow(
      'Show working',
      "Show the assistant's running commentary — the short notes it writes alongside its actions as it works, separate from its reasoning and its final answer.",
      switchTag,
      'toggle-narration',
      (x) => x.showNarration,
      (x, c) => {
        x.showNarration = readSwitchChecked(c.event);
      },
    ),
  )}
  ${when(
    (x) => x.chatConfig.ui?.showAgentSwitchIndicator != null,
    chatBotToggleRow(
      'Show delegation',
      'Show in the chat window when tasks are delegated to different agents.',
      switchTag,
      'toggle-agent-switch',
      (x) => x.showAgentSwitchIndicator,
      (x, c) => {
        x.showAgentSwitchIndicator = readSwitchChecked(c.event);
      },
    ),
  )}
  ${when(
    (x) => x.persistenceToggleable,
    chatBotToggleRow(
      'Remember chat',
      'Save this conversation so it can be restored after a page refresh. Turning this off deletes the saved copy.',
      switchTag,
      'toggle-remember-chat',
      (x) => x.persistenceActive,
      (x, c) => {
        x.setPersistenceEnabled(readSwitchChecked(c.event));
      },
    ),
  )}
  ${when(
    (x) => x.chatConfig.ui?.animations?.userConfigurable === true,
    settingsToggleRow(
      'Animations',
      'Choose which loading and activity indicators appear while the assistant is working.',
      'toggle-animations',
      animationsPickerTemplate(switchTag),
      'column',
    ),
  )}
`;

const appSettingsTemplate = (
  switchTag: string,
  iconTag: string,
): ViewTemplate<FoundationAiAssistant> => html<FoundationAiAssistant>`
  <!--
    Slot FIRST, ahead of the provider toggles. The assistant renders no heading for this
    section, so whatever the host slots in has to be able to title it — and a heading below the
    rows it names would title nothing. Anything the host wants under the toggles it can render
    below them within its own slotted block.
  -->
  <slot name="settings-app" ${slotted('settingsAppSlotted')}></slot>
  <!--
    Sub-heading for the toggle group, rendered only when the host asks for one — both the text
    and the icon come from its provider. It exists so a host whose slotted content covers other
    ground can still mark these rows off from it.
  -->
  ${when(
    (x) => !!x.appSettingsTogglesHeading,
    html<FoundationAiAssistant>`
      <h4 class="ai-settings-section-title settings-app-toggles-title" part="app-toggles-title">
        ${when(
          (x) => !!x.appSettingsTogglesHeading?.icon,
          html<FoundationAiAssistant>`
            <${iconTag} name="${(x) => x.appSettingsTogglesHeading?.icon}"></${iconTag}>
          `,
        )}
        ${(x) => x.appSettingsTogglesHeading?.title}
      </h4>
    `,
  )}
  ${repeat(
    (x) => x.visibleAppSettingsToggles,
    html<AssistantAppSettingsToggle, FoundationAiAssistant>`
      <div class="settings-app-toggle-block">
        <div
          class="settings-toggle-row"
          part="${(toggle) => `${toggle.part ?? `app-toggle-${toggle.id}`}-row`}"
        >
          <div class="settings-toggle-text">
            <div class="settings-toggle-title">${(toggle) => toggle.title}</div>
            <div class="settings-toggle-desc">${(toggle) => toggle.description}</div>
          </div>
          <div class="settings-toggle-control">
            <${switchTag}
              part="${(toggle) => toggle.part ?? `app-toggle-${toggle.id}`}"
              :checked=${(toggle, c) => c.parent.getAppSettingValue(toggle.id)}
              ?disabled=${(toggle) => toggle.disabled?.() ?? false}
              @change=${(toggle, c) => {
                c.parent.setAppSettingValue(toggle.id, readSwitchChecked(c.event));
              }}
            ></${switchTag}>
          </div>
        </div>
        ${when(
          (toggle) => !!toggle.hint?.(),
          html<AssistantAppSettingsToggle>`
            <div class="settings-toggle-hint">${(toggle) => toggle.hint?.() ?? ''}</div>
          `,
        )}
      </div>
    `,
  )}
`;

/**
 * The four billing buckets for one `AggregateUsage`, wherever it comes from — the lifetime
 * total, the live session, or a history row.
 *
 * Why four numbers instead of one: each bucket bills at a different rate, so a single token
 * total can't explain a session that processed millions of tokens for pennies. Uncached input,
 * cache read and cache write partition the prompt (they sum to it, never add on top), and
 * output is what the model generated.
 *
 * `partPrefix` keeps the shadow-part names caller-specific (`session-tokens-input`,
 * `lifetime-tokens-input`, …) so a host can style one context without hitting the others.
 */
const tokenBreakdownTemplate = <T>(
  usageOf: (x: T) => AggregateUsage,
  partPrefix: string,
): ViewTemplate<T> => html<T>`
  <div class="settings-token-breakdown" part="${partPrefix}-token-breakdown">
    <!--
      Titles the box. Nothing in it otherwise said "tokens": the four bucket names are the
      token TYPES, not the unit, so the panel read as four unexplained numbers unless the
      reader found the per-bucket tooltips.
    -->
    <div class="settings-token-breakdown-label">Tokens</div>
    <div class="settings-token-buckets">
      <div class="settings-token-bucket" part="${partPrefix}-tokens-input">
        <span
          class="settings-token-bucket-label"
          title="Prompt tokens billed at the full uncached rate"
        >
          Input
        </span>
        <span
          class="settings-token-bucket-value"
          :innerHTML="${(x: T) => formatCompactTokenCountSafe(usageOf(x).uncachedInputTokens)}"
        ></span>
      </div>
      <div class="settings-token-bucket is-discounted" part="${partPrefix}-tokens-cache-read">
        <span
          class="settings-token-bucket-label"
          title="Prompt tokens served from the provider cache — billed at a fraction of the uncached rate"
        >
          Cache read
        </span>
        <span
          class="settings-token-bucket-value"
          :innerHTML="${(x: T) => formatCompactTokenCountSafe(usageOf(x).cacheReadTokens)}"
        ></span>
      </div>
      <div class="settings-token-bucket" part="${partPrefix}-tokens-cache-write">
        <span
          class="settings-token-bucket-label"
          title="Prompt tokens written to the provider cache — billed at a premium over the uncached rate"
        >
          Cache write
        </span>
        <span
          class="settings-token-bucket-value"
          :innerHTML="${(x: T) => formatCompactTokenCountSafe(usageOf(x).cacheWriteTokens)}"
        ></span>
      </div>
      <div class="settings-token-bucket" part="${partPrefix}-tokens-output">
        <span
          class="settings-token-bucket-label"
          title="Tokens the model generated, including reasoning tokens billed as output"
        >
          Output
        </span>
        <span
          class="settings-token-bucket-value"
          :innerHTML="${(x: T) => formatCompactTokenCountSafe(usageOf(x).outputTokens)}"
        ></span>
      </div>
    </div>
  </div>
`;

/**
 * Lifetime usage across every project in the history scope.
 *
 * Sits above the live session block because it is the wider figure. It used to live inside
 * that block as a lone "Total build cost", where the live badge and project name directly
 * above it implied the total belonged to the open project.
 */
const lifetimeUsageTemplate =
  (): ViewTemplate<FoundationAiAssistant> => html<FoundationAiAssistant>`
    ${when(
      (x) => x.settingsCostLifetimeVisible,
      html<FoundationAiAssistant>`
        <div class="settings-lifetime-usage" part="lifetime-usage">
          <div class="settings-lifetime-usage-top">
            <span class="settings-lifetime-label">Lifetime usage</span>
          </div>
          <div class="settings-usage-row">
            <div
              class="settings-build-stat"
              part="lifetime-cost"
              ?hidden=${(x) => !x.resolvedCostHistory.showCost}
            >
              <div class="settings-build-stat-label">Total build cost</div>
              <div class="settings-build-stat-value">
                <span class="prefix">$</span>
                <span class="value">
                  ${(x) => x.cumulativeCostUsd.toFixed(SESSION_COST_DECIMALS)}
                </span>
              </div>
            </div>
            <div
              class="settings-usage-row-tokens"
              ?hidden=${(x) => !x.resolvedCostHistory.showTokens}
            >
              ${tokenBreakdownTemplate<FoundationAiAssistant>((x) => x.cumulativeUsage, 'lifetime')}
            </div>
          </div>
        </div>
      `,
    )}
  `;

const sessionCostSummaryTemplate =
  (): ViewTemplate<FoundationAiAssistant> => html<FoundationAiAssistant>`
    ${when(
      (x) => x.settingsCostSummaryVisible,
      html<FoundationAiAssistant>`
        <div class="settings-current-build" part="session-cost-summary">
          <div class="settings-current-build-top">
            <span class="settings-live-badge">
              <span class="settings-live-dot"></span>
              ${(x) => x.resolvedCostHistory.badgeLabel}
            </span>
            ${when(
              (x) => !!x.liveCostSessionTitle,
              html<FoundationAiAssistant>`
                <span class="settings-current-build-name">${(x) => x.liveCostSessionTitle}</span>
              `,
            )}
          </div>
          <div class="settings-usage-row">
            <div
              class="settings-build-stat"
              part="session-cost"
              ?hidden=${(x) => !x.resolvedCostHistory.showCost}
            >
              <!--
                Label states the scope rather than implying one. These figures are whatever
                the open transcript proves, so they are the project's whole history exactly
                when that transcript is restored on load, and only this page-load's when it
                isn't. Same number, two meanings — so the caller can't be left guessing.
              -->
              <div class="settings-build-stat-label">
                ${(x) => (x.liveUsageIsProjectLifetime ? 'Project cost' : 'Session cost')}
              </div>
              <div class="settings-build-stat-value">
                <span class="prefix">$</span>
                <span class="value">${(x) => x.sessionCostUsd.toFixed(SESSION_COST_DECIMALS)}</span>
              </div>
            </div>
            <div
              class="settings-usage-row-tokens"
              ?hidden=${(x) => !x.resolvedCostHistory.showTokens}
            >
              ${tokenBreakdownTemplate<FoundationAiAssistant>((x) => x.sessionUsage, 'session')}
            </div>
          </div>
          ${when(
            (x) => x.chatConfig.ui?.showActiveModel !== false && x.providerStatuses.length > 0,
            html<FoundationAiAssistant>`
              <div class="settings-model-tags" part="session-model-tags">
                ${repeat(
                  (x) => x.providerStatuses,
                  html<AIProviderRegistryStatusEntry>`
                    <span
                      class="settings-model-tag vendor-${(entry) =>
                        entry.status?.provider || 'generic'}"
                    >
                      ${(entry) => entry.status?.model ?? entry.name}
                    </span>
                  `,
                )}
              </div>
            `,
          )}
        </div>
      `,
    )}
  `;

const costSessionHistoryTemplate = (
  iconTag: string,
  buttonTag: string,
): ViewTemplate<FoundationAiAssistant> => html<FoundationAiAssistant>`
  ${repeat(
    (x) => x.costSessionHistory,
    html<CostSessionRecord>`
      <div class="settings-cost-history-row" part="cost-history-row">
        <div class="settings-cost-history-row-content">
          <span class="settings-cost-history-icon" part="cost-history-icon" aria-hidden="true">
            <${iconTag} name="table-cells-large"></${iconTag}>
          </span>
          <div class="settings-cost-history-row-body">
            <div class="settings-cost-history-row-left">
              <div class="settings-cost-history-row-title">
                ${(record) => record.title}
              </div>
              <div class="settings-cost-history-row-date">
                ${(record) => formatCostSessionDate(record.updatedAt)}
              </div>
            </div>
            <!--
              Cost and the delete control live together so they centre against each other.
              The button used to sit outside the row body, held in place by a negative margin
              tuned for when this column had a second line (a merged token total); that line
              is gone, and a nudge measured against a removed sibling is exactly the kind that
              rots silently.
            -->
            <div class="settings-cost-history-row-right">
              <div class="settings-cost-history-row-metrics">
                <div class="settings-cost-history-row-cost">
                  $${(record) => record.usage.costUsd.toFixed(SESSION_COST_DECIMALS)}
                </div>
              </div>
              <${buttonTag}
                class="settings-cost-history-delete"
                part="cost-history-delete"
                appearance="stealth"
                aria-label="Remove from build history"
                @click=${(record, c) => c.parent.showConfirmationDialog('Are you sure you want to remove this build from history?', c.parent.settingsModal, () => c.parent.removeCostSessionFromHistory(record.id))}
              >
                <${iconTag} name="trash"></${iconTag}>
              </${buttonTag}>
            </div>
          </div>
        </div>
        <!--
          The same four buckets as the live block, on its own line below the row rather than
          beside the cost — the row's own width is spoken for by title, cost and delete.
          Replaces a merged total that could not explain a huge token count at a small cost
          without hovering for a tooltip.
        -->
        <div
          class="settings-cost-history-row-content settings-cost-history-row-breakdown"
          ?hidden=${(record, c) =>
            !(c.parent as FoundationAiAssistant).resolvedCostHistory.showTokens}
        >
          ${tokenBreakdownTemplate<CostSessionRecord>((record) => record.usage, 'cost-history-row')}
        </div>
        ${when(
          (record) => record.models.length > 0,
          html<CostSessionRecord>`
            <div
              class="settings-cost-history-row-content settings-model-tags settings-cost-history-model-tags"
            >
              ${repeat(
                (record) => record.models,
                html<CostSessionModelEntry>`
                  <span class="settings-model-tag vendor-${(model) => model.provider || 'generic'}">
                    ${(model) => model.model}
                  </span>
                `,
              )}
            </div>
          `,
        )}
      </div>
    `,
  )}
`;

/**
 * Modal shell for assistant settings — Configuration and Usage tabs, section slots for
 * app-specific UI, and built-in chat-bot toggles.
 */
export const SettingsModalTemplate = (
  designSystemPrefix: string,
): ViewTemplate<FoundationAiAssistant> => {
  const modalTag = `${designSystemPrefix}-modal`;
  const buttonTag = `${designSystemPrefix}-button`;
  const switchTag = `${designSystemPrefix}-switch`;
  const iconTag = `${designSystemPrefix}-icon`;
  const progressTag = `${designSystemPrefix}-progress`;
  const tabsTag = `${designSystemPrefix}-tabs`;
  const tabTag = `${designSystemPrefix}-tab`;
  const tabPanelTag = `${designSystemPrefix}-tab-panel`;

  const settingsTabPanel = html<FoundationAiAssistant>`
    <${tabPanelTag}
      slot="tabpanel"
      class="settings-modal-tab-panel"
      part="settings-tab-panel"
    >
      <div class="settings-modal-body" part="settings-modal-body">
      <section
        class="settings-modal-section"
        part="settings-section-model"
        ?hidden=${(x) => !x.settingsModelSectionVisible}
      >
        <h3 class="ai-settings-section-title">
          <${iconTag} name="microchip"></${iconTag}> AI Model Settings
        </h3>
        <div class="settings-modal-section-body">
          <slot name="settings-model" ${slotted('settingsModelSlotted')}></slot>
          ${platformContextUsageTemplate(progressTag)}
        </div>
      </section>
      <!--
        No heading here, by design. This section belongs to the host: only it knows what its
        settings are called, so the assistant supplies neither text nor icon and the host titles
        the section from the slot. See assistantSettingsSectionTitleStyles for the heading style
        it can apply to match the sections above and below.
      -->
      <section
        class="settings-modal-section"
        part="settings-section-app"
        ?hidden=${(x) => !x.settingsAppSectionVisible}
      >
        <div class="settings-modal-section-body">
          ${appSettingsTemplate(switchTag, iconTag)}
        </div>
      </section>
      ${when(
        (x) => x.chatConfig.ui?.allowDebugDownload === true,
        html<FoundationAiAssistant>`
          <section class="settings-modal-section" part="settings-section-agent-log">
            <div class="settings-modal-section-body">
              ${settingsToggleRow(
                'Download agent log',
                "Export a structured debug timeline of this session's agent activity.",
                'download-agent-log',
                html<FoundationAiAssistant>`
                  <${buttonTag}
                    part="download-button"
                    appearance="outline"
                    ?disabled=${(x) => x.downloadingDebugLog}
                    @click=${(x) => x.downloadDebugLog()}
                  >
                    ${when(
                      (x) => x.downloadingDebugLog,
                      html<FoundationAiAssistant>`
                        <span
                          class="download-spinner"
                          part="download-spinner"
                          aria-hidden="true"
                        ></span>
                      `,
                    )}
                    ${(x) => (x.downloadingDebugLog ? 'Downloading…' : 'Download')}
                  </${buttonTag}>
                `,
              )}
            </div>
          </section>
        `,
      )}
      <section class="settings-modal-section" part="settings-section-chat">
        <h3 class="ai-settings-section-title">
          <${iconTag} name="comment"></${iconTag}> AI Chat Bot Settings
        </h3>
        <p class="settings-modal-section-note">These options only control what you see in the chat window and do not control model behaviour.</p>
        <div class="settings-modal-section-body">
          ${chatBotSettingsTemplate(switchTag, buttonTag)}
        </div>
      </section>
      </div>
    </${tabPanelTag}>
  `;

  const costTabPanel = html<FoundationAiAssistant>`
    <${tabPanelTag}
      slot="tabpanel"
      class="settings-modal-tab-panel"
      part="cost-tab-panel"
    >
      <div class="settings-modal-body" part="settings-modal-body">
      ${lifetimeUsageTemplate()}
      ${sessionCostSummaryTemplate()}
      <div
        class="settings-cost-current"
        part="settings-cost-current"
        ?hidden=${(x) => !x.settingsCostCurrentSectionVisible}
      >
        <slot name="settings-cost-current" ${slotted('settingsCostCurrentSlotted')}></slot>
      </div>
      <p class="settings-cost-history-title" ?hidden=${(x) => !x.settingsCostHistorySectionVisible}>
        Build history
      </p>
      <div
        class="settings-cost-history"
        part="settings-cost-history"
        ?hidden=${(x) => !x.settingsCostHistorySectionVisible}
      >
        ${costSessionHistoryTemplate(iconTag, buttonTag)}
        <slot name="settings-cost-history" ${slotted('settingsCostHistorySlotted')}></slot>
      </div>
      </div>
    </${tabPanelTag}>
  `;

  return html<FoundationAiAssistant>`
    <${modalTag}
      class="settings-modal"
      part="settings-modal"
      exportparts="dialog: settings-dialog, content: settings-content, top: settings-top, bottom: settings-footer"
      ${ref('settingsModal')}
      initial-width="620"
      @close=${(x) => x.onSettingsModalClosed()}
    >
      <div slot="top" class="settings-modal-header" part="settings-modal-header">
        <h2 class="settings-modal-title">
          <${iconTag} name="cog"></${iconTag}> Settings
        </h2>
      </div>
      <div class="settings-modal-content" part="settings-modal-content">
        <${tabsTag}
          class="settings-modal-tabs"
          part="settings-modal-tabs"
          appearance="secondary"
          ${ref('settingsTabsEl')}
          @change=${(x, c) => x.onSettingsModalTabsChange(c.event)}
        ><${tabTag} 
          slot="tab" 
          id="settings" 
          appearance="secondary" 
          class="settings-modal-tab"
          part="settings-modal-tab-settings"
        ><${iconTag} name="cog"></${iconTag}> Configuration</${tabTag}>${when(
          (x) => x.settingsCostTabVisible,
          html<FoundationAiAssistant>`<${tabTag} 
            slot="tab" 
            id="cost" 
            appearance="secondary" 
            class="settings-modal-tab"
            part="settings-modal-tab-cost"
          ><${iconTag} name="dollar-sign"></${iconTag}> Usage</${tabTag}>`,
        )}${settingsTabPanel}${when((x) => x.settingsCostTabVisible, costTabPanel)}</${tabsTag}>
      </div>
      <div slot="bottom" class="settings-modal-footer" part="settings-modal-footer">
        <div class="settings-modal-footer-actions">
          ${when(
            (x) => x.settingsModalTab === 'cost',
            html<FoundationAiAssistant>`
              <span class="settings-disclaimer">
                ${when(
                  (x) => !!x.resolvedCostHistory.summaryHint,
                  html<FoundationAiAssistant>`
                    ${(x) => x.resolvedCostHistory.summaryHint}
                  `,
                )}Costs
                are estimated from token usage at list price.
              </span>
            `,
          )}
          <${buttonTag}
            appearance="outline"
            part="settings-done-button"
            class="settings-done-button"
            @click=${(x) => x.closeSettingsModal()}
          >Done</${buttonTag}>
        </div>
      </div>
    </${modalTag}>
  `;
};
