<script lang="ts">
  import AgentPane from './AgentPane.svelte';
  import TerminalTabs from './TerminalTabs.svelte';
  import FileBrowser from './FileBrowser.svelte';
  import MemoryTab from './MemoryTab.svelte';
  import CommsTab from './CommsTab.svelte';
  import TaskBoardTab from './TaskBoardTab.svelte';
  import DocsTab from './DocsTab.svelte';
  import SshTab from './SshTab.svelte';
  import StatusDot from './ui/StatusDot.svelte';
  import {
    startAgent, stopAgent, sendPrompt, getSession, hasSession,
    loadLastSession,
    type AgentStatus, type AgentMessage,
  } from './agent-store.svelte.ts';
  import {
    getActiveTab, setActiveTab, isTabActivated,
    ALL_TABS, type ProjectTab,
  } from './project-tabs-store.svelte.ts';

  interface Props {
    id: string;
    name: string;
    cwd: string;
    accent: string;
    provider?: string;
    profile?: string;
    model?: string;
    contextPct?: number;
    burnRate?: number;
    // blinkVisible removed — StatusDot reads from blink-store directly
    /** Worktree branch name — set when this is a clone card. */
    worktreeBranch?: string;
    /** ID of parent project — set when this is a clone card. */
    cloneOf?: string;
    /** Max clones reached for this project. */
    clonesAtMax?: boolean;
    /** Callback when user requests cloning (receives projectId and branch name). */
    onClone?: (projectId: string, branch: string) => void;
    /** Group ID for btmsg/bttask context. */
    groupId?: string;
  }

  let {
    id,
    name,
    cwd,
    accent,
    provider = 'claude',
    profile,
    model = 'claude-opus-4-5',
    contextPct = 0,
    burnRate = 0,
    // blinkVisible removed
    worktreeBranch,
    cloneOf,
    clonesAtMax = false,
    onClone,
    groupId = 'dev',
  }: Props = $props();

  // ── Agent session (reactive from store) ──────────────────────────
  // CRITICAL: Use shared constants for fallback values. Creating new [] or {}
  // inside $derived causes infinite loops because each evaluation returns a
  // new reference → Svelte thinks the value changed → re-renders → new ref → loop.
  const EMPTY_MESSAGES: AgentMessage[] = [];
  let session = $derived(getSession(id));
  let agentStatus: AgentStatus = $derived(session?.status ?? 'idle');
  let agentMessages: AgentMessage[] = $derived(session?.messages ?? EMPTY_MESSAGES);
  let agentCost = $derived(session?.costUsd ?? 0);
  let agentTokens = $derived((session?.inputTokens ?? 0) + (session?.outputTokens ?? 0));
  let agentModel = $derived(session?.model ?? model);
  let agentInputTokens = $derived(session?.inputTokens ?? 0);
  let agentOutputTokens = $derived(session?.outputTokens ?? 0);

  // Context limit per model (approximate)
  const MODEL_LIMITS: Record<string, number> = {
    'claude-opus-4-5': 200000,
    'claude-sonnet-4-5': 200000,
    'claude-haiku-4-5': 200000,
    'gpt-5.4': 128000,
    'qwen3:8b': 32000,
  };
  let contextLimit = $derived(MODEL_LIMITS[agentModel] ?? 200000);
  let computedContextPct = $derived(
    agentInputTokens > 0 ? Math.min(100, Math.round((agentInputTokens / contextLimit) * 100)) : (contextPct ?? 0)
  );

  // File references from tool_call messages
  // NOTE: Do NOT use $derived with .filter()/.map() — creates new arrays every
  // evaluation, which Svelte interprets as "changed" → re-render → new arrays → loop.
  // Instead, compute these as plain functions called in template (no caching).
  function getFileRefs(): string[] {
    return agentMessages
      .filter((m) => m.role === 'tool-call' && m.toolPath)
      .map((m) => m.toolPath!)
      .filter((p, i, arr) => arr.indexOf(p) === i)
      .slice(0, 20);
  }

  function getTurnCount(): number {
    return agentMessages.filter((m) => m.role === 'user' || m.role === 'assistant').length;
  }

  // ── Display messages (no $derived — avoids new ref on re-render) ──
  function getDisplayMessages(): AgentMessage[] {
    return agentMessages.length > 0 ? agentMessages : EMPTY_MESSAGES;
  }

  // ── Clone dialog state ──────────────────────────────────────────
  let showCloneDialog = $state(false);
  let cloneBranchName = $state('');
  let cloneError = $state('');

  const BRANCH_RE = /^[a-zA-Z0-9/_.-]+$/;

  function openCloneDialog() {
    cloneBranchName = '';
    cloneError = '';
    showCloneDialog = true;
  }

  function submitClone() {
    if (!BRANCH_RE.test(cloneBranchName)) {
      cloneError = 'Use only letters, numbers, /, _, -, .';
      return;
    }
    onClone?.(id, cloneBranchName);
    showCloneDialog = false;
  }

  // Derived from project-tabs-store for reactive reads
  let activeTab = $derived(getActiveTab(id));

  // ── Load last session on mount (once, not reactive) ─────────────────
  import { onMount } from 'svelte';
  onMount(() => { loadLastSession(id); });

  function setTab(tab: ProjectTab) {
    setActiveTab(id, tab);
  }

  function handleSend(text: string) {
    if (hasSession(id)) {
      // Session exists — send follow-up prompt
      sendPrompt(id, text).catch((err) => {
        console.error('[agent.prompt] error:', err);
      });
    } else {
      // No session — start a new agent
      startAgent(id, provider, text, { cwd, model }).catch((err) => {
        console.error('[agent.start] error:', err);
      });
    }
  }

  function handleStop() {
    stopAgent(id).catch((err) => {
      console.error('[agent.stop] error:', err);
    });
  }
</script>

<article
  class="project-card"
  class:is-clone={!!cloneOf}
  style="--accent: {accent}"
  aria-label="Project: {name}{cloneOf ? ' (worktree clone)' : ''}"
>
  <!-- Header -->
  <header class="project-header">
    <div class="status-dot-wrap" aria-label="Status: {agentStatus}">
      <StatusDot status={agentStatus} />
    </div>

    <span class="project-name" title={name}>{name}</span>
    <span class="project-cwd" title={cwd}>{cwd}</span>

    {#if worktreeBranch}
      <span class="wt-badge" title="Worktree branch: {worktreeBranch}">
        WT · {worktreeBranch}
      </span>
    {/if}

    <span class="provider-badge" title="Provider: {provider}">{provider}</span>

    {#if profile}
      <span class="profile-badge" title="Profile: {profile}">{profile}</span>
    {/if}

    {#if contextPct > 50}
      <span
        class="ctx-badge"
        class:ctx-warn={contextPct >= 75}
        class:ctx-danger={contextPct >= 90}
        title="Context window {contextPct}% used"
      >{contextPct}%</span>
    {/if}

    {#if burnRate > 0}
      <span class="burn-badge" title="Burn rate">${burnRate.toFixed(2)}/hr</span>
    {/if}

    <!-- Clone button (only on non-clone cards) -->
    {#if !cloneOf && onClone}
      <button
        class="clone-btn"
        onclick={openCloneDialog}
        disabled={clonesAtMax}
        title={clonesAtMax ? 'Maximum 3 clones reached' : 'Clone into git worktree'}
        aria-label="Clone project into worktree"
      >
        <!-- Fork / branch SVG icon -->
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
          <line x1="6" y1="3" x2="6" y2="15"/>
          <circle cx="18" cy="6" r="3"/>
          <circle cx="6" cy="18" r="3"/>
          <path d="M18 9a9 9 0 0 1-9 9"/>
        </svg>
      </button>
    {/if}
  </header>

  <!-- Clone dialog (inline, shown above tab bar) -->
  {#if showCloneDialog}
    <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
    <div
      class="clone-dialog"
      role="dialog"
      aria-label="Create worktree clone"
      onkeydown={(e) => { if (e.key === 'Escape') showCloneDialog = false; }}
    >
      <span class="clone-dialog-label">Branch name</span>
      <input
        class="clone-dialog-input"
        type="text"
        placeholder="feature/my-branch"
        bind:value={cloneBranchName}
        onkeydown={(e) => { if (e.key === 'Enter') submitClone(); }}
        autofocus
        aria-label="New branch name for worktree"
      />
      {#if cloneError}
        <span class="clone-dialog-error">{cloneError}</span>
      {/if}
      <div class="clone-dialog-actions">
        <button class="clone-dialog-cancel" onclick={() => showCloneDialog = false}>Cancel</button>
        <button class="clone-dialog-submit" onclick={submitClone}>Create</button>
      </div>
    </div>
  {/if}

  <!-- Project tab bar -->
  <div class="tab-bar" role="tablist" aria-label="{name} tabs">
    {#each ALL_TABS as tab}
      <button
        class="tab-btn"
        class:active={activeTab === tab}
        role="tab"
        aria-selected={activeTab === tab}
        aria-controls="tabpanel-{id}-{tab}"
        onclick={() => setTab(tab)}
      >
        {tab.charAt(0).toUpperCase() + tab.slice(1)}
      </button>
    {/each}
  </div>

  <!-- Tab content: display:flex/none to keep agent session mounted -->
  <div class="tab-content">
    <!-- Model tab: agent pane + terminal tabs — always mounted -->
    <div
      id="tabpanel-{id}-model"
      class="tab-pane"
      style:display={activeTab === 'model' ? 'flex' : 'none'}
      role="tabpanel"
      aria-label="Model"
    >
      <AgentPane
        messages={getDisplayMessages()}
        status={agentStatus}
        costUsd={agentCost}
        tokens={agentTokens}
        model={agentModel}
        {provider}
        {profile}
        {contextPct}
        {burnRate}
        onSend={handleSend}
        onStop={handleStop}
      />
      <TerminalTabs projectId={id} {accent} {cwd} />
    </div>

    <!-- Docs tab -->
    
      <div
        id="tabpanel-{id}-docs"
        class="tab-pane"
        style:display={activeTab === 'docs' ? 'flex' : 'none'}
        role="tabpanel"
        aria-label="Docs"
      >
        <DocsTab {cwd} />
      </div>

    <!-- Context tab -->
    
      <div
        id="tabpanel-{id}-context"
        class="tab-pane"
        style:display={activeTab === 'context' ? 'flex' : 'none'}
        role="tabpanel"
        aria-label="Context"
      >
        <div class="context-pane">
          <div class="ctx-stats-row">
            <div class="ctx-stat">
              <span class="ctx-stat-label">Input tokens</span>
              <span class="ctx-stat-value">{agentInputTokens.toLocaleString()}</span>
            </div>
            <div class="ctx-stat">
              <span class="ctx-stat-label">Output tokens</span>
              <span class="ctx-stat-value">{agentOutputTokens.toLocaleString()}</span>
            </div>
            <div class="ctx-stat">
              <span class="ctx-stat-label">Context</span>
              <span class="ctx-stat-value">{computedContextPct}%</span>
            </div>
            <div class="ctx-stat">
              <span class="ctx-stat-label">Model</span>
              <span class="ctx-stat-value">{agentModel}</span>
            </div>
            <div class="ctx-stat">
              <span class="ctx-stat-label">Turns</span>
              <span class="ctx-stat-value">{getTurnCount()}</span>
            </div>
          </div>
          <div class="ctx-meter-wrap" title="{computedContextPct}% of {contextLimit.toLocaleString()} tokens">
            <div class="ctx-meter-bar" style:width="{computedContextPct}%"
              class:meter-warn={computedContextPct >= 75}
              class:meter-danger={computedContextPct >= 90}
            ></div>
          </div>
          {#if getFileRefs().length > 0}
          <div class="ctx-turn-list">
            <div class="ctx-section-label">File references ({getFileRefs().length})</div>
            {#each getFileRefs() as ref}
              <div class="ctx-turn-row">
                <span class="ctx-turn-preview" title={ref}>{ref}</span>
              </div>
            {/each}
          </div>
          {/if}
          <div class="ctx-turn-list">
            <div class="ctx-section-label">Recent turns</div>
            {#each getDisplayMessages().slice(-10) as msg}
              <div class="ctx-turn-row">
                <span class="ctx-turn-role ctx-role-{msg.role}">{msg.role}</span>
                <span class="ctx-turn-preview">{msg.content.slice(0, 60)}{msg.content.length > 60 ? '...' : ''}</span>
              </div>
            {/each}
          </div>
        </div>
      </div>

    <!-- Files tab -->
    
      <div
        id="tabpanel-{id}-files"
        class="tab-pane"
        style:display={activeTab === 'files' ? 'flex' : 'none'}
        role="tabpanel"
        aria-label="Files"
      >
        <FileBrowser {cwd} />
      </div>

    <!-- SSH tab -->
    
      <div
        id="tabpanel-{id}-ssh"
        class="tab-pane"
        style:display={activeTab === 'ssh' ? 'flex' : 'none'}
        role="tabpanel"
        aria-label="SSH"
      >
        <SshTab projectId={id} />
      </div>

    <!-- Memory tab -->
    
      <div
        id="tabpanel-{id}-memory"
        class="tab-pane"
        style:display={activeTab === 'memory' ? 'flex' : 'none'}
        role="tabpanel"
        aria-label="Memory"
      >
        <MemoryTab />
      </div>

    <!-- Comms tab (inter-agent messaging) -->
    
      <div
        id="tabpanel-{id}-comms"
        class="tab-pane"
        style:display={activeTab === 'comms' ? 'flex' : 'none'}
        role="tabpanel"
        aria-label="Comms"
      >
        <CommsTab {groupId} agentId={id} />
      </div>

    <!-- Tasks tab (kanban board) -->
    
      <div
        id="tabpanel-{id}-tasks"
        class="tab-pane"
        style:display={activeTab === 'tasks' ? 'flex' : 'none'}
        role="tabpanel"
        aria-label="Tasks"
      >
        <TaskBoardTab {groupId} agentId={id} />
      </div>
  </div>
</article>

<style>
  .project-card {
    background: var(--ctp-base);
    border: 1px solid var(--ctp-surface0);
    border-radius: 0.5rem;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-height: 0;
    position: relative;
  }

  /* Accent stripe on left edge */
  .project-card::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 3px;
    background: var(--accent, var(--ctp-mauve));
    border-radius: 0.5rem 0 0 0.5rem;
  }

  /* Worktree clone: accent top border instead of left stripe */
  .project-card.is-clone {
    border-top: 2px solid var(--accent, var(--ctp-mauve));
  }

  .project-card.is-clone::before {
    display: none;
  }

  /* Header */
  .project-header {
    height: 2.5rem;
    background: var(--ctp-mantle);
    border-bottom: 1px solid var(--ctp-surface0);
    display: flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0 0.625rem 0 0.875rem;
    flex-shrink: 0;
    overflow: hidden;
  }

  .status-dot-wrap {
    flex-shrink: 0;
    width: 0.625rem;
    height: 0.625rem;
    position: relative;
  }


  .project-name {
    font-weight: 600;
    color: var(--ctp-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex-shrink: 1;
    min-width: 0;
  }

  .project-cwd {
    font-size: 0.6875rem;
    color: var(--ctp-subtext0);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    direction: rtl;
    max-width: 8rem;
    flex-shrink: 2;
  }

  /* Badges */
  .provider-badge,
  .profile-badge,
  .ctx-badge,
  .burn-badge {
    flex-shrink: 0;
    padding: 0.1rem 0.375rem;
    border-radius: 0.25rem;
    font-size: 0.6rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    white-space: nowrap;
  }

  .provider-badge {
    background: color-mix(in srgb, var(--accent, var(--ctp-mauve)) 15%, transparent);
    color: var(--accent, var(--ctp-mauve));
  }

  .profile-badge {
    background: color-mix(in srgb, var(--ctp-blue) 12%, transparent);
    color: var(--ctp-blue);
  }

  .ctx-badge {
    background: color-mix(in srgb, var(--ctp-yellow) 15%, transparent);
    color: var(--ctp-yellow);
  }

  .ctx-badge.ctx-warn   { color: var(--ctp-peach); background: color-mix(in srgb, var(--ctp-peach) 15%, transparent); }
  .ctx-badge.ctx-danger { color: var(--ctp-red);   background: color-mix(in srgb, var(--ctp-red)   15%, transparent); }

  .burn-badge {
    background: color-mix(in srgb, var(--ctp-peach) 10%, transparent);
    color: var(--ctp-peach);
  }

  /* Worktree badge */
  .wt-badge {
    flex-shrink: 0;
    padding: 0.1rem 0.375rem;
    border-radius: 0.25rem;
    font-size: 0.6rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    white-space: nowrap;
    background: color-mix(in srgb, var(--accent, var(--ctp-mauve)) 20%, transparent);
    color: var(--accent, var(--ctp-mauve));
    border: 1px solid color-mix(in srgb, var(--accent, var(--ctp-mauve)) 40%, transparent);
  }

  /* Clone button */
  .clone-btn {
    flex-shrink: 0;
    width: 1.5rem;
    height: 1.5rem;
    background: transparent;
    border: none;
    color: var(--ctp-overlay1);
    cursor: pointer;
    border-radius: 0.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: background 0.12s, color 0.12s;
    margin-left: auto;
  }

  .clone-btn:hover:not(:disabled) {
    background: var(--ctp-surface0);
    color: var(--ctp-text);
  }

  .clone-btn:disabled {
    opacity: 0.35;
    cursor: not-allowed;
  }

  .clone-btn svg {
    width: 0.875rem;
    height: 0.875rem;
  }

  /* Inline clone dialog */
  .clone-dialog {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.625rem;
    background: var(--ctp-mantle);
    border-bottom: 1px solid var(--ctp-surface0);
    flex-shrink: 0;
    flex-wrap: wrap;
  }

  .clone-dialog-label {
    font-size: 0.75rem;
    color: var(--ctp-subtext0);
    flex-shrink: 0;
  }

  .clone-dialog-input {
    flex: 1;
    min-width: 8rem;
    height: 1.625rem;
    background: var(--ctp-surface0);
    border: 1px solid var(--ctp-surface1);
    border-radius: 0.25rem;
    color: var(--ctp-text);
    font-family: var(--term-font-family);
    font-size: 0.75rem;
    padding: 0 0.375rem;
    outline: none;
  }

  .clone-dialog-input:focus { border-color: var(--ctp-mauve); }

  .clone-dialog-error {
    width: 100%;
    font-size: 0.6875rem;
    color: var(--ctp-red);
  }

  .clone-dialog-actions {
    display: flex;
    gap: 0.25rem;
    flex-shrink: 0;
  }

  .clone-dialog-cancel,
  .clone-dialog-submit {
    padding: 0.125rem 0.5rem;
    border-radius: 0.25rem;
    font-size: 0.75rem;
    font-family: var(--ui-font-family);
    cursor: pointer;
    transition: background 0.1s, border-color 0.1s;
  }

  .clone-dialog-cancel {
    background: transparent;
    border: 1px solid var(--ctp-surface1);
    color: var(--ctp-subtext0);
  }

  .clone-dialog-cancel:hover {
    background: var(--ctp-surface0);
    color: var(--ctp-text);
  }

  .clone-dialog-submit {
    background: color-mix(in srgb, var(--ctp-mauve) 20%, transparent);
    border: 1px solid var(--ctp-mauve);
    color: var(--ctp-mauve);
  }

  .clone-dialog-submit:hover {
    background: color-mix(in srgb, var(--ctp-mauve) 35%, transparent);
  }

  /* Tab bar */
  .tab-bar {
    height: 2rem;
    background: var(--ctp-mantle);
    border-bottom: 1px solid var(--ctp-surface0);
    display: flex;
    align-items: stretch;
    flex-shrink: 0;
    padding: 0 0.25rem;
    gap: 0.125rem;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .tab-bar::-webkit-scrollbar { display: none; }

  .tab-btn {
    padding: 0 0.625rem;
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--ctp-subtext0);
    font-family: var(--ui-font-family);
    font-size: 0.8125rem;
    cursor: pointer;
    white-space: nowrap;
    transition: color 0.12s, border-color 0.12s;
    margin-bottom: -1px;
    flex-shrink: 0;
  }

  .tab-btn:hover  { color: var(--ctp-text); }
  .tab-btn.active {
    color: var(--ctp-text);
    border-bottom-color: var(--accent, var(--ctp-mauve));
  }

  /* Tab content */
  .tab-content {
    flex: 1;
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
  }

  .tab-pane {
    flex: 1;
    min-height: 0;
    flex-direction: column;
    overflow: hidden;
  }

  .placeholder-pane {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--ctp-overlay0);
    font-size: 0.8125rem;
    font-style: italic;
  }

  /* Context tab */
  .context-pane {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 0.625rem;
    overflow-y: auto;
    flex: 1;
  }

  .ctx-stats-row {
    display: flex;
    gap: 1rem;
  }

  .ctx-stat {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
  }

  .ctx-stat-label {
    font-size: 0.625rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ctp-overlay0);
    font-weight: 600;
  }

  .ctx-stat-value {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--ctp-text);
    font-family: var(--term-font-family);
  }

  .ctx-meter-wrap {
    height: 0.375rem;
    background: var(--ctp-surface0);
    border-radius: 0.25rem;
    overflow: hidden;
  }

  .ctx-meter-bar {
    height: 100%;
    background: var(--ctp-teal);
    border-radius: 0.25rem;
    transition: width 0.3s;
  }

  .ctx-meter-bar.meter-warn   { background: var(--ctp-peach); }
  .ctx-meter-bar.meter-danger { background: var(--ctp-red); }

  .ctx-section-label {
    font-size: 0.625rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ctp-overlay0);
    font-weight: 600;
    margin-bottom: 0.25rem;
  }

  .ctx-turn-list {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
  }

  .ctx-turn-row {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    font-size: 0.75rem;
  }

  .ctx-turn-role {
    flex-shrink: 0;
    font-size: 0.625rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    min-width: 4.5rem;
  }

  .ctx-role-user        { color: var(--ctp-blue); }
  .ctx-role-assistant   { color: var(--ctp-mauve); }
  .ctx-role-tool-call   { color: var(--ctp-peach); }
  .ctx-role-tool-result { color: var(--ctp-teal); }

  .ctx-turn-preview {
    color: var(--ctp-subtext0);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
  }
</style>
