'use client';

import { useState, useEffect, useCallback, useMemo, type MouseEvent, type ReactNode } from 'react';
import { Stack, Text, Title, Group, Box, Badge, ActionIcon, Tooltip, Button, Tabs } from '@tidbcloud/uikit';
import { isPreparedSource, useWorkspace, Artifact } from '@/lib/workspace';
import { useTranslation, type TranslationKey } from '@/lib/i18n';
import ArtifactCard from './ArtifactCard';
// import StudioActionCards from './StudioActionCards'; // hidden — all cards are coming soon
import NoteModal from './NoteModal';
import InsightReport from '@/components/InsightReport';
import AnomaliesPanel from '@/components/AnomaliesPanel';
import DiagnosisReport from '@/components/DiagnosisReport';
import DiagnosisEvidencePanel, { EvidencePreviewPane, FocusedMetricChart, FocusedMetricChartData, JsonValue } from './DiagnosisEvidencePanel';
import { dirname, evidenceRunDir, evidenceTypeLabel, isClinicEvidenceJson, workspaceEvidenceInfo } from './evidencePaths';
import { InsightData } from '@/lib/types';
import { fetchWithAuth } from '@/lib/auth';
import { WorkspaceFile } from '@/lib/workspace';

const API = process.env.NEXT_PUBLIC_API_URL ?? '';

async function fetchJsonFile(workspaceId: string, path: string): Promise<any> {
  const res = await fetchWithAuth(
    `${API}/api/workspace/${encodeURIComponent(workspaceId)}/file?path=${encodeURIComponent(path)}`
  );
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

async function fetchWorkspaceFiles(workspaceId: string): Promise<WorkspaceFile[]> {
  const res = await fetchWithAuth(`${API}/api/workspace/${encodeURIComponent(workspaceId)}/files`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json();
  return data.files ?? [];
}

async function fetchFocusedMetricChart(
  workspaceId: string,
  path: string,
  metricRef: NonNullable<AuthoredEvidenceItem['metric_ref']>
): Promise<FocusedMetricChartData> {
  const params = new URLSearchParams({
    path,
    metric_group_id: metricRef.metric_group_id || '',
  });
  const res = await fetchWithAuth(
    `${API}/api/workspace/${encodeURIComponent(workspaceId)}/metric-chart?${params.toString()}`
  );
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

const SECTION_STYLES: Record<string, { bg: string; border: string; text: string; muted: string }> = {
  key:      { bg: '#eff6ff', border: '#bfdbfe', text: 'blue.7',  muted: 'blue.5' },
  overview: { bg: '#ecfeff', border: '#a5f3fc', text: 'cyan.8',  muted: 'cyan.6' },
  default:  { bg: '#f1f5f9', border: '#e2e8f0', text: 'carbon.6', muted: 'carbon.5' },
};

function SectionHeader({ label, count, open, onClick, variant = 'default' }: { label: string; count: number; open: boolean; onClick: () => void; variant?: 'key' | 'overview' | 'default' }) {
  const s = SECTION_STYLES[variant] || SECTION_STYLES.default;
  return (
    <Group
      justify="space-between"
      align="center"
      onClick={onClick}
      style={{
        cursor: 'pointer',
        userSelect: 'none',
        padding: '5px 8px',
        borderRadius: 6,
        backgroundColor: s.bg,
        border: `1px solid ${s.border}`,
      }}
    >
      <Group gap={6} align="center">
        <Text size="xs" fw={700} c={s.text} style={{ letterSpacing: '0.04em' }}>{label}</Text>
        <Text size="xs" c={s.muted} fw={500}>({count})</Text>
      </Group>
      <Text size="xs" c={s.muted} style={{ lineHeight: 1 }}>{open ? '▾' : '▸'}</Text>
    </Group>
  );
}

function OverviewSection({ items, renderItem }: { items: Artifact[]; renderItem: (a: Artifact) => JSX.Element }) {
  const { t } = useTranslation();
  const [open, setOpen] = useState(true);
  return (
    <Stack gap={4}>
      <SectionHeader label={t('studio.sectionOverview')} count={items.length} open={open} onClick={() => setOpen(o => !o)} variant="overview" />
      {open && items.map(renderItem)}
    </Stack>
  );
}

function flattenFiles(files: WorkspaceFile[]): WorkspaceFile[] {
  const out: WorkspaceFile[] = [];
  for (const file of files) {
    if (file.type === 'file') out.push(file);
    if (file.children?.length) out.push(...flattenFiles(file.children));
  }
  return out;
}

function buildVirtualEvidenceArtifacts(files: WorkspaceFile[]): Artifact[] {
  const allFiles = flattenFiles(files);
  const evidenceFiles = allFiles.filter(file => isClinicEvidenceJson(file.path));

  return evidenceFiles
    .sort((a, b) => a.path.localeCompare(b.path))
    .reverse()
    .flatMap(file => {
      const runDir = evidenceRunDir(file.path);
      const workspaceInfo = workspaceEvidenceInfo(file.path);
      const queryKinds = file.path.endsWith('/queries.json')
        ? [
            { kind: 'topsql', label: 'TopSQL' },
            { kind: 'slow_query', label: 'Slow Query' },
          ]
        : null;
      const isWorkspaceEvidence = Boolean(workspaceInfo);
      const reportType = file.path.endsWith('/workspace_evidence_report.json')
        ? 'workspace_evidence'
        : 'diagnosis_evidence';
      const titlePrefix = isWorkspaceEvidence ? ['Workspace Evidence'] : [];

      return (queryKinds || [{ kind: '', label: evidenceTypeLabel(file.path) }]).map(({ kind, label }) => ({
        id: `virtual-evidence:${file.path}${kind ? `:${kind}` : ''}`,
        type: 'report',
        title: [...titlePrefix, label].filter(Boolean).join(' / ') || label,
        description: file.path,
        createdAt: new Date(),
        data: {
          reportType,
          filePath: file.path,
          evidenceLabel: label,
          queryKind: kind || undefined,
          runDir,
          workspaceEvidence: isWorkspaceEvidence,
          virtualOnly: true,
        },
      } as Artifact));
    });
}

const EVIDENCE_LABEL_ORDER: Record<string, number> = {
  Report: 0,
  'Workspace Report': 0,
  'Agent Summary': 1,
  Metrics: 2,
  TopSQL: 3,
  'Slow Query': 4,
  'Log Stats': 5,
  Links: 6,
};

function compareEvidenceArtifacts(a: Artifact, b: Artifact): number {
  const aLabel = shortEvidenceTitle(a);
  const bLabel = shortEvidenceTitle(b);
  const aRank = EVIDENCE_LABEL_ORDER[aLabel] ?? 100;
  const bRank = EVIDENCE_LABEL_ORDER[bLabel] ?? 100;
  if (aRank !== bRank) return aRank - bRank;
  return a.title.localeCompare(b.title);
}

function groupArtifactsByRun(artifacts: Artifact[]): Array<{ key: string; label: string; items: Artifact[] }> {
  const groups = new Map<string, { key: string; label: string; items: Artifact[] }>();

  for (const artifact of artifacts) {
    const runDir = String(artifact.data?.runDir || '');
    if (!runDir) {
      const key = '__ungrouped__';
      const existing = groups.get(key) || { key, label: 'Other', items: [] };
      existing.items.push(artifact);
      groups.set(key, existing);
      continue;
    }

    if (artifact.data?.workspaceEvidence) {
      const key = runDir || 'evidences';
      const existing = groups.get(key) || { key, label: 'Workspace Evidence', items: [] };
      existing.items.push(artifact);
      groups.set(key, existing);
      continue;
    }

    const runName = dirname(runDir).split('/').filter(Boolean).at(-1) || 'run';
    const key = runDir;
    const label = runName;
    const existing = groups.get(key) || { key, label, items: [] };
    existing.items.push(artifact);
    groups.set(key, existing);
  }

  return Array.from(groups.values())
    .map(group => ({
      ...group,
      items: group.items.sort(compareEvidenceArtifacts),
    }))
    .sort((a, b) => {
      if (a.label === 'Workspace Evidence') return -1;
      if (b.label === 'Workspace Evidence') return 1;
      return b.label.localeCompare(a.label);
    });
}

function shortEvidenceTitle(artifact: Artifact): string {
  const evidenceLabel = artifact.data?.evidenceLabel;
  if (typeof evidenceLabel === 'string' && evidenceLabel) return evidenceLabel;
  const filePath = String(artifact.data?.filePath || '');
  return evidenceTypeLabel(filePath);
}

function isQueriesArtifact(artifact: Artifact): boolean {
  const filePath = String(artifact.data?.filePath || '');
  return filePath.endsWith('/queries.json');
}

interface QueryLinkSource {
  source?: unknown;
  digest?: unknown;
  sql_preview?: unknown;
  link?: unknown;
}

interface QueryLinkSourceWithLink extends QueryLinkSource {
  link: string;
}

interface AuthoredEvidenceItem {
  type?: string;
  source?: string;
  name?: string;
  digest?: string;
  instance?: string;
  metric_ref?: {
    metric_group_id?: string;
    instances?: string[];
  };
  reason?: string;
  link?: unknown;
}

interface DiagnosisRunAnalysis {
  key_evidence?: AuthoredEvidenceItem[];
  supporting_evidence?: AuthoredEvidenceItem[];
}

function queryName(item: QueryLinkSource, fallback: string): string {
  const source = item.source === 'topsql' ? 'TopSQL' : item.source === 'slow_query' ? 'Slow Query' : 'Query';
  const digest = typeof item.digest === 'string' && item.digest ? item.digest.slice(0, 12) : '';
  const sql = typeof item.sql_preview === 'string' ? item.sql_preview.trim() : '';
  return [source, digest || sql.slice(0, 48) || fallback].filter(Boolean).join(' · ');
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value && typeof value === 'object' && !Array.isArray(value));
}

function authoredEvidenceLinkUrl(value: unknown): string | undefined {
  if (typeof value === 'string') {
    const trimmed = value.trim();
    return trimmed || undefined;
  }
  if (!isRecord(value)) return undefined;
  for (const key of ['url', 'href', 'link']) {
    const nested = value[key];
    if (typeof nested === 'string' && nested.trim()) return nested.trim();
  }
  return undefined;
}

function queryLinkItems(data: unknown, queryKind?: unknown): Array<{ name: string; link: string }> {
  const dataRecord = isRecord(data) ? data : {};
  const source = isRecord(dataRecord.queries) ? dataRecord.queries : dataRecord;
  const kind = queryKind === 'topsql' || queryKind === 'slow_query' ? queryKind : undefined;
  const slow = !kind || kind === 'slow_query'
    ? (Array.isArray(source.slow_queries) ? source.slow_queries : [])
    : [];
  const topsql = !kind || kind === 'topsql'
    ? (Array.isArray(source.topsql) ? source.topsql : [])
    : [];
  return [...slow, ...topsql]
    .filter((item): item is QueryLinkSourceWithLink => isRecord(item) && typeof item.link === 'string' && item.link.length > 0)
    .map((item, index) => ({ name: queryName(item, `Query ${index + 1}`), link: item.link }));
}

function metricChartRequestKey(artifact: Artifact, item: AuthoredEvidenceItem): string {
  const ref = item.metric_ref;
  return [
    artifact.id,
    ref?.metric_group_id || '',
  ].join('|');
}

function isDiagnosisArtifact(artifact: Artifact): boolean {
  return artifact.data?.reportType === 'diagnosis';
}

function authoredEvidenceTitle(item: AuthoredEvidenceItem): string {
  return item.name || item.digest || item.type || 'Evidence';
}

type AuthoredEvidenceKind = 'topsql' | 'slow_query' | 'query' | null;

function authoredEvidenceTypeKind(item: AuthoredEvidenceItem): AuthoredEvidenceKind {
  const rawType = String(item.type || '').toLowerCase();
  if (rawType === 'topsql') return 'topsql';
  if (rawType === 'slow_query' || rawType === 'slow-query') return 'slow_query';
  if (rawType !== 'query') return null;

  const rawSource = String(item.source || '').toLowerCase();
  if (rawSource === 'topsql') return 'topsql';
  if (rawSource === 'slow_query' || rawSource === 'slow-query') return 'slow_query';

  const hint = [
    authoredEvidenceLinkUrl(item.link),
    item.name,
    item.reason,
  ].filter(Boolean).join(' ').toLowerCase();

  if (hint.includes('topsql') || hint.includes('top sql')) return 'topsql';
  if (hint.includes('slow_query') || hint.includes('slow query') || hint.includes('slow-query')) return 'slow_query';
  return 'query';
}

function authoredEvidenceTypeLabel(item: AuthoredEvidenceItem, t: (key: TranslationKey) => string): string {
  const kind = authoredEvidenceTypeKind(item);
  if (kind === 'topsql') return 'TOPSQL';
  if (kind === 'slow_query') return t('diagnosis.typeSlowQuery');
  if (kind === 'query') return t('diagnosis.typeQuery');
  return item.type || 'Evidence';
}

function authoredEvidenceTypeColor(kind: AuthoredEvidenceKind): string {
  if (kind === 'topsql') return 'grape';
  if (kind === 'slow_query') return 'violet';
  return 'blue';
}

function AuthoredEvidenceCard({
  item,
  metricChart,
  onMetricChartRequest,
}: {
  item: AuthoredEvidenceItem;
  metricChart?: ReactNode;
  onMetricChartRequest?: (item: AuthoredEvidenceItem) => void;
}) {
  const { t } = useTranslation();
  const kind = authoredEvidenceTypeKind(item);
  const typeLabel = authoredEvidenceTypeLabel(item, t);
  const showLink = item.type !== 'metric';
  const isMetric = item.type === 'metric';
  const metricGroupId = item.metric_ref?.metric_group_id;
  const linkUrl = authoredEvidenceLinkUrl(item.link);

  useEffect(() => {
    if (isMetric) onMetricChartRequest?.(item);
    // eslint-disable-next-line react-hooks/exhaustive-deps -- use stable metricGroupId instead of item object reference
  }, [isMetric, metricGroupId]);

  return (
    <Box
      style={{
        borderRadius: 8,
        border: '1px solid #e5e7eb',
        backgroundColor: '#fff',
        padding: '8px 10px',
      }}
    >
      <Group justify="space-between" gap="sm" wrap="nowrap" align="flex-start">
        <Box style={{ minWidth: 0, flex: 1 }}>
          <Group gap="xs" wrap="nowrap">
            {typeLabel && (
              <Badge size="sm" variant="light" color={authoredEvidenceTypeColor(kind) as any}>
                {typeLabel}
              </Badge>
            )}
            <Text size="xs" fw={600} c="carbon.8" style={{ wordBreak: 'break-word' }}>
              {authoredEvidenceTitle(item)}
            </Text>
            {item.instance && <Text size="xs" c="carbon.6" truncate>[{item.instance}]</Text>}
          </Group>
          {item.reason && (
            <Text size="xs" c="carbon.7" mt={4} style={{ lineHeight: 1.5 }}>
              {item.reason}
            </Text>
          )}
          {isMetric && metricChart && (
            <Box mt={8}>
              {metricChart}
            </Box>
          )}
        </Box>
        {showLink && linkUrl && (
          <Text
            size="xs"
            c="blue.7"
            component="a"
            href={linkUrl}
            target="_blank"
            rel="noopener noreferrer"
            onClick={(event: MouseEvent) => event.stopPropagation()}
            style={{ textDecoration: 'underline', whiteSpace: 'nowrap', flexShrink: 0 }}
          >
            {t('studio.openLink')}
          </Text>
        )}
      </Group>
    </Box>
  );
}

const overviewSuffixes = ['/links.json', '/logs.json'];

function isOverviewArtifact(a: Artifact): boolean {
  const fp = String(a.data?.filePath || '');
  return overviewSuffixes.some(s => fp.endsWith(s));
}

function RunGroupEvidenceContent({
  analysis,
  loading,
  error,
  evidenceItems,
  renderEvidenceFile,
  renderMetricChart,
  onMetricChartRequest,
}: {
  analysis?: DiagnosisRunAnalysis;
  loading: boolean;
  error?: string;
  evidenceItems: Artifact[];
  renderEvidenceFile: (artifact: Artifact) => JSX.Element;
  renderMetricChart?: (item: AuthoredEvidenceItem) => ReactNode;
  onMetricChartRequest?: (item: AuthoredEvidenceItem) => void;
}) {
  const { t } = useTranslation();
  const [keyOpen, setKeyOpen] = useState(true);
  const [supportingOpen, setSupportingOpen] = useState(true);
  const [filesOpen, setFilesOpen] = useState(false);
  const keyItems = analysis?.key_evidence || [];
  const supportingItems = analysis?.supporting_evidence || [];
  const overviewItems = evidenceItems.filter(isOverviewArtifact);
  const otherItems = evidenceItems.filter(a => !isOverviewArtifact(a));

  return (
    <Stack gap={6} mt="sm">
      {loading && <Text size="xs" c="carbon.6">{t('studio.loadingKeyEvidence')}</Text>}
      {error && <Text size="xs" c="red">{error}</Text>}

      {overviewItems.length > 0 && (
        <OverviewSection items={overviewItems} renderItem={renderEvidenceFile} />
      )}

      <Stack gap={4}>
        <SectionHeader label={t('studio.sectionKeyEvidence')} count={keyItems.length} open={keyOpen} onClick={() => setKeyOpen(open => !open)} variant="key" />
        {keyOpen && (
          keyItems.length > 0 ? (
            keyItems.map((item, index) => (
              <AuthoredEvidenceCard
                key={`${item.type || 'evidence'}-${item.name || item.digest || index}-${index}`}
                item={item}
                metricChart={renderMetricChart?.(item)}
                onMetricChartRequest={onMetricChartRequest}
              />
            ))
          ) : (
            <Text size="xs" c="carbon.6" px={8}>{t('studio.noKeyEvidence')}</Text>
          )
        )}
      </Stack>

      <Stack gap={4}>
        <SectionHeader label={t('studio.sectionSupportingEvidence')} count={supportingItems.length} open={supportingOpen} onClick={() => setSupportingOpen(open => !open)} />
        {supportingOpen && (
          supportingItems.length > 0 ? (
            supportingItems.map((item, index) => (
              <AuthoredEvidenceCard
                key={`${item.type || 'supporting'}-${item.name || item.digest || index}-${index}`}
                item={item}
                metricChart={renderMetricChart?.(item)}
                onMetricChartRequest={onMetricChartRequest}
              />
            ))
          ) : (
            <Text size="xs" c="carbon.6" px={8}>{t('studio.noSupportingEvidence')}</Text>
          )
        )}
      </Stack>

      <Stack gap={4}>
        <SectionHeader label={t('studio.sectionEvidenceFiles')} count={otherItems.length} open={filesOpen} onClick={() => setFilesOpen(open => !open)} />
        {filesOpen && otherItems.map(renderEvidenceFile)}
      </Stack>
    </Stack>
  );
}

export default function StudioPanel({
  collapsed = false,
  onToggleCollapsed,
}: {
  collapsed?: boolean;
  onToggleCollapsed?: () => void;
} = {}) {
  const { t } = useTranslation();
  const {
    artifacts,
    selectedArtifactId,
    setSelectedArtifactId,
    studioView,
    setStudioView,
    sources,
    selectedSourceId,
    setSelectedSourceId,
    addArtifact,
    updateArtifact,
    workspaceId,
    filesRefreshTrigger,
  } = useWorkspace();

  // Note modal state
  const [noteModalOpen, setNoteModalOpen] = useState(false);
  const [editingNote, setEditingNote] = useState<Artifact | null>(null);

  // Dynamically-loaded report data, keyed by artifact ID to support multiple alerts
  const [insightDataMap, setInsightDataMap] = useState<Record<string, InsightData>>({});
  const [diagnosticDataMap, setDiagnosticDataMap] = useState<Record<string, any>>({});
  const [resourceWorkerDataMap, setResourceWorkerDataMap] = useState<Record<string, any>>({});
  const [diagnosisDataMap, setDiagnosisDataMap] = useState<Record<string, any>>({});
  const [reportLoading, setReportLoading] = useState(false);
  const [reportError, setReportError] = useState<string | null>(null);
  const [diagnosisDetailTab, setDiagnosisDetailTab] = useState<'report' | 'evidence'>('report');
  const [virtualArtifacts, setVirtualArtifacts] = useState<Artifact[]>([]);
  const [expandedRunGroups, setExpandedRunGroups] = useState<Set<string>>(new Set());
  const [expandedEvidenceArtifacts, setExpandedEvidenceArtifacts] = useState<Set<string>>(new Set());
  const [evidenceDataByArtifact, setEvidenceDataByArtifact] = useState<Record<string, JsonValue>>({});
  const [evidenceLoading, setEvidenceLoading] = useState<Set<string>>(new Set());
  const [evidenceError, setEvidenceError] = useState<Record<string, string>>({});
  const [metricChartDataByKey, setMetricChartDataByKey] = useState<Record<string, FocusedMetricChartData>>({});
  const [metricChartLoading, setMetricChartLoading] = useState<Set<string>>(new Set());
  const [metricChartError, setMetricChartError] = useState<Record<string, string>>({});
  const [runAnalysisByArtifact, setRunAnalysisByArtifact] = useState<Record<string, DiagnosisRunAnalysis>>({});
  const [runAnalysisLoading, setRunAnalysisLoading] = useState<Set<string>>(new Set());
  const [runAnalysisError, setRunAnalysisError] = useState<Record<string, string>>({});
  const persistedVisibleArtifacts = useMemo(
    () => artifacts.filter(artifact => !isDiagnosisArtifact(artifact)),
    [artifacts]
  );
  const visibleArtifacts = useMemo(
    () => [...persistedVisibleArtifacts, ...virtualArtifacts],
    [persistedVisibleArtifacts, virtualArtifacts]
  );
  const groupedVisibleArtifacts = useMemo(
    () => groupArtifactsByRun(virtualArtifacts),
    [virtualArtifacts]
  );
  const groupedVisibleArtifactKeys = useMemo(
    () => groupedVisibleArtifacts.map(group => group.key).join('|'),
    [groupedVisibleArtifacts]
  );

  const refreshVirtualArtifacts = useCallback(() => {
    if (!workspaceId) return Promise.resolve();
    return fetchWorkspaceFiles(workspaceId)
      .then(files => {
        setVirtualArtifacts(buildVirtualEvidenceArtifacts(files));
      })
      .catch(() => {
        setVirtualArtifacts([]);
      });
  }, [workspaceId]);

  const loadEvidenceArtifact = useCallback((artifact: Artifact) => {
    const filePath = String(artifact.data?.filePath || '');
    if (!workspaceId || !filePath) return;
    if (evidenceDataByArtifact[artifact.id] || evidenceLoading.has(artifact.id)) return;

    setEvidenceError(prev => ({ ...prev, [artifact.id]: '' }));
    setEvidenceLoading(prev => new Set(prev).add(artifact.id));
    fetchJsonFile(workspaceId, filePath)
      .then(data => setEvidenceDataByArtifact(prev => ({ ...prev, [artifact.id]: data as JsonValue })))
      .catch(err => setEvidenceError(prev => ({ ...prev, [artifact.id]: String(err) })))
      .finally(() => setEvidenceLoading(prev => {
        const next = new Set(prev);
        next.delete(artifact.id);
        return next;
      }));
  }, [evidenceDataByArtifact, evidenceLoading, workspaceId]);

  const toggleEvidenceArtifact = useCallback((artifact: Artifact) => {
    const filePath = String(artifact.data?.filePath || '');
    if (!workspaceId || !filePath) return;

    setExpandedEvidenceArtifacts(prev => {
      const next = new Set(prev);
      if (next.has(artifact.id)) next.delete(artifact.id);
      else next.add(artifact.id);
      return next;
    });

    loadEvidenceArtifact(artifact);
  }, [loadEvidenceArtifact, workspaceId]);

  const loadRunAnalysis = useCallback((reportArtifact?: Artifact) => {
    const filePath = String(reportArtifact?.data?.filePath || '');
    if (!workspaceId || !reportArtifact || !filePath) return;
    if (runAnalysisByArtifact[reportArtifact.id] || runAnalysisLoading.has(reportArtifact.id)) return;

    setRunAnalysisError(prev => ({ ...prev, [reportArtifact.id]: '' }));
    setRunAnalysisLoading(prev => new Set(prev).add(reportArtifact.id));
    fetchJsonFile(workspaceId, filePath)
      .then(data => {
        const analysis = isRecord(data) && isRecord(data.analysis) ? data.analysis : {};
        setRunAnalysisByArtifact(prev => ({
          ...prev,
          [reportArtifact.id]: {
            key_evidence: Array.isArray(analysis.key_evidence) ? analysis.key_evidence as AuthoredEvidenceItem[] : [],
            supporting_evidence: Array.isArray(analysis.supporting_evidence) ? analysis.supporting_evidence as AuthoredEvidenceItem[] : [],
          },
        }));
      })
      .catch(err => setRunAnalysisError(prev => ({ ...prev, [reportArtifact.id]: String(err) })))
      .finally(() => setRunAnalysisLoading(prev => {
        const next = new Set(prev);
        next.delete(reportArtifact.id);
        return next;
      }));
  }, [runAnalysisByArtifact, runAnalysisLoading, workspaceId]);

  const toggleRunGroup = useCallback((groupKey: string, reportArtifact?: Artifact) => {
    setExpandedRunGroups(prev => {
      const next = new Set(prev);
      if (next.has(groupKey)) next.delete(groupKey);
      else next.add(groupKey);
      return next;
    });
    loadRunAnalysis(reportArtifact);
  }, [loadRunAnalysis]);

  // When a prepared source is re-preparing, evict that source's cached report data
  // so the new files are loaded once preparation completes.
  useEffect(() => {
    const loadingIssueIds = new Set(
      sources
        .filter(s => isPreparedSource(s) && s.status === 'loading' && s.issueId)
        .map(s => s.issueId as string)
    );
    if (loadingIssueIds.size === 0) return;

    const staleIds = visibleArtifacts
      .filter(a => a.issueId && loadingIssueIds.has(a.issueId))
      .map(a => a.id);
    if (staleIds.length === 0) return;

    const idSet = new Set(staleIds);
    setInsightDataMap(prev => {
      const next = { ...prev };
      idSet.forEach(id => delete next[id]);
      return next;
    });
    setDiagnosticDataMap(prev => {
      const next = { ...prev };
      idSet.forEach(id => delete next[id]);
      return next;
    });
    setResourceWorkerDataMap(prev => {
      const next = { ...prev };
      idSet.forEach(id => delete next[id]);
      return next;
    });
    setDiagnosisDataMap(prev => {
      const next = { ...prev };
      idSet.forEach(id => delete next[id]);
      return next;
    });
  }, [sources, visibleArtifacts]);

  // Initial load + event-driven refresh (chat complete, source ready, etc.)
  useEffect(() => {
    if (!workspaceId) return;
    refreshVirtualArtifacts();
    // Clear run analysis and evidence caches so expanded groups re-fetch
    // updated data (e.g. after update_report_analysis.py merges new analysis).
    setRunAnalysisByArtifact({});
    setEvidenceDataByArtifact({});
  }, [workspaceId, filesRefreshTrigger, refreshVirtualArtifacts]);

  useEffect(() => {
    if (groupedVisibleArtifacts.length === 0) {
      setExpandedRunGroups(prev => (prev.size === 0 ? prev : new Set()));
      return;
    }
    setExpandedRunGroups(prev => {
      const next = new Set<string>();
      const validKeys = new Set(groupedVisibleArtifacts.map(group => group.key));
      for (const key of prev) {
        if (validKeys.has(key)) next.add(key);
      }
      if (next.size === 0 && groupedVisibleArtifacts[0]) {
        next.add(groupedVisibleArtifacts[0].key);
      }

      const prevKey = Array.from(prev).sort().join('|');
      const nextKey = Array.from(next).sort().join('|');
      return prevKey === nextKey ? prev : next;
    });
  }, [groupedVisibleArtifactKeys, groupedVisibleArtifacts]);

  useEffect(() => {
    for (const group of groupedVisibleArtifacts) {
      if (!expandedRunGroups.has(group.key)) continue;
      const reportArtifact = group.items.find(item =>
        item.data?.reportType === 'diagnosis' ||
        item.data?.reportType === 'workspace_evidence' ||
        String(item.data?.filePath || '').endsWith('/workspace_evidence_report.json')
      );
      loadRunAnalysis(reportArtifact);
    }
  }, [expandedRunGroups, groupedVisibleArtifacts, loadRunAnalysis]);

  // Determine what to show
  const selectedArtifact = visibleArtifacts.find(a => a.id === selectedArtifactId);
  const selectedSource = sources.find(s => s.id === selectedSourceId);

  // Determine detail view type
  const isDetail = studioView === 'detail';
  const isAnomalyReport = isDetail && selectedArtifact?.data?.reportType === 'anomaly';
  const isDiagnosisReport = isDetail && selectedArtifact?.data?.reportType === 'diagnosis';
  const isWorkspaceEvidenceReport = isDetail && selectedArtifact?.data?.reportType === 'workspace_evidence';
  const isDiagnosisEvidence = isDetail && selectedArtifact?.data?.reportType === 'diagnosis_evidence';
  const isInsightReport = isDetail && selectedArtifact?.type === 'report' && !isAnomalyReport && !isDiagnosisReport && !isWorkspaceEvidenceReport && !isDiagnosisEvidence;

  useEffect(() => {
    setDiagnosisDetailTab('report');
  }, [selectedArtifact?.id]);

  // Current artifact's loaded data
  const insightData = selectedArtifact ? insightDataMap[selectedArtifact.id] ?? null : null;
  const diagnosticData = selectedArtifact ? diagnosticDataMap[selectedArtifact.id] ?? null : null;
  const resourceWorkerData = selectedArtifact ? resourceWorkerDataMap[selectedArtifact.id] ?? null : null;

  // Auto-select artifact when a source is clicked
  useEffect(() => {
    if (!selectedSourceId) return;

    // Find artifact(s) associated with this source
    const relatedArtifact = visibleArtifacts.find(a => a.sourceId === selectedSourceId);

    if (relatedArtifact) {
      // Switch to detail view and select the artifact
      setSelectedArtifactId(relatedArtifact.id);
      setStudioView('detail');
    }
  }, [selectedSourceId, visibleArtifacts, setSelectedArtifactId, setStudioView]);

  // Fetch report JSON when switching to a report detail view
  useEffect(() => {
    if (!workspaceId || !selectedArtifact) return;

    // Resolve the file path prefix — use per-issue dir or fall back to workspace root (legacy)
    const dirPrefix = selectedArtifact.issueDir ? `${selectedArtifact.issueDir}/` : '';

    if (isInsightReport && !insightDataMap[selectedArtifact.id]) {
      setReportLoading(true);
      setReportError(null);
      fetchJsonFile(workspaceId, `${dirPrefix}insight.json`)
        .then(data => setInsightDataMap(prev => ({ ...prev, [selectedArtifact.id]: data as InsightData })))
        .catch(err => setReportError(String(err)))
        .finally(() => setReportLoading(false));
    }

    if (isAnomalyReport && !diagnosticDataMap[selectedArtifact.id]) {
      setReportLoading(true);
      setReportError(null);
      Promise.all([
        fetchJsonFile(workspaceId, `${dirPrefix}diagnostic_package.json`),
        fetchJsonFile(workspaceId, `${dirPrefix}resource_worker_insight.json`).catch(() => null),
      ])
        .then(([diag, rw]) => {
          setDiagnosticDataMap(prev => ({ ...prev, [selectedArtifact.id]: diag }));
          setResourceWorkerDataMap(prev => ({ ...prev, [selectedArtifact.id]: rw }));
        })
        .catch(err => setReportError(String(err)))
        .finally(() => setReportLoading(false));
    }

    // Load diagnosis report JSON from the filePath in artifact data
    if ((isDiagnosisReport || isWorkspaceEvidenceReport) && !diagnosisDataMap[selectedArtifact.id]) {
      const filePath = selectedArtifact.data?.filePath as string;
      if (filePath) {
        setReportLoading(true);
        setReportError(null);
        // filePath is absolute; the backend file endpoint serves relative to workspace data dir
        // We need to extract the relative portion
        fetchJsonFile(workspaceId, filePath)
          .then(data => setDiagnosisDataMap(prev => ({ ...prev, [selectedArtifact.id]: data })))
          .catch(err => {
            if (!String(err).includes('HTTP 404')) {
              setReportError(String(err));
            }
          })
          .finally(() => setReportLoading(false));
      }
    }
  }, [workspaceId, selectedArtifact?.id, isInsightReport, isAnomalyReport, isDiagnosisReport, isWorkspaceEvidenceReport]);

  return (
    <Stack
      gap={0}
      style={{
        height: '100%',
        backgroundColor: '#f8f9fa',
        borderLeft: '1px solid #e5e7eb',
        width: '100%',
      }}
    >
      {/* Artifacts */}
      <div
        style={{
          flex: 1,
          display: 'flex',
          flexDirection: 'column',
          overflow: 'hidden',
        }}
      >
        {/* Header */}
        <Box
          p="md"
          style={{
            borderBottom: '1px solid #e5e7eb',
            backgroundColor: '#fff',
            flexShrink: 0,
          }}
        >
          <Group justify="space-between" align="center" style={{ gap: 8 }}>
            <Group gap="xs" align="center">
              {studioView === 'detail' && (
                <ActionIcon
                  variant="subtle"
                  color="gray"
                  size="sm"
                  onClick={() => {
                    setStudioView('list');
                    setSelectedArtifactId(null);
                    setSelectedSourceId(null);
                  }}
                  aria-label={t('studio.backToList')}
                >
                  <span style={{ fontSize: '14px' }}>←</span>
                </ActionIcon>
              )}
              <Tooltip label={t('studio.title')} disabled={!collapsed}>
                <Title order={5} style={{ fontSize: '15px' }}>
                  {collapsed ? t('studio.titleShort') : t('studio.title')}
                </Title>
              </Tooltip>
            </Group>
            <Group gap="xs" align="center" style={{ flexShrink: 0 }}>
              {!collapsed && studioView === 'list' && (
                <Badge size="sm" variant="light" color="blue">
                  {t(visibleArtifacts.length === 1 ? 'studio.artifactsCount' : 'studio.artifactsCountPlural', { count: visibleArtifacts.length })}
                </Badge>
              )}
              {onToggleCollapsed && (
                <Tooltip label={collapsed ? t('studio.expand') : t('studio.collapse')}>
                  <ActionIcon
                    variant="subtle"
                    color="gray"
                    size="sm"
                    onClick={onToggleCollapsed}
                    aria-label={collapsed ? t('studio.expand') : t('studio.collapse')}
                  >
                    <span style={{ fontSize: '14px', lineHeight: 1 }}>{collapsed ? '⟨' : '⟩'}</span>
                  </ActionIcon>
                </Tooltip>
              )}
            </Group>
          </Group>
          {!collapsed && studioView === 'list' && (
            <Text size="xs" c="carbon.6" mt={4}>
              {t('studio.subtitle')}
            </Text>
          )}
          {!collapsed && studioView === 'detail' && selectedArtifact && (
            <Group gap={6} mt={4} align="center">
              <Text size="xs" c="carbon.6">
                {selectedArtifact.title}
              </Text>
              {selectedArtifact.issueId && (
                <Badge size="xs" variant="light" color="red">
                  {selectedArtifact.issueId}
                </Badge>
              )}
            </Group>
          )}
        </Box>

        {/* Artifact content */}
        {collapsed ? null : (
          <Box style={{ flex: 1, overflowY: 'auto' }}>
          {studioView === 'list' ? (
            <Stack gap={6} p="sm">
              {/* StudioActionCards hidden — Report / Operation / Notes are all coming soon
              <StudioActionCards
                onAction={(action) => {
                  if (action === 'notes') {
                    setEditingNote(null);
                    setNoteModalOpen(true);
                  } else {
                    console.log('Studio action:', action);
                    // TODO: wire up report and operation create flows
                  }
                }}
              />
              */}

              {persistedVisibleArtifacts.map(artifact => (
                <ArtifactCard
                  key={artifact.id}
                  artifact={artifact}
                  isSelected={selectedArtifactId === artifact.id}
                  onClick={() => {
                    if (artifact.type === 'note') {
                      setEditingNote(artifact);
                      setNoteModalOpen(true);
                    } else {
                      setSelectedArtifactId(artifact.id);
                      setStudioView('detail');
                    }
                  }}
                />
              ))}

              {groupedVisibleArtifacts.map(group => (
                  <Box
                    key={group.key}
                    p="sm"
                    style={{
                      border: '1px solid #dbe3ea',
                      borderRadius: 12,
                      backgroundColor: '#fff',
                      boxShadow: '0 1px 2px rgba(16, 24, 40, 0.04)',
                    }}
                  >
                    {(() => {
                      const reportArtifact = group.items.find(item =>
                        item.data?.reportType === 'diagnosis' ||
                        item.data?.reportType === 'workspace_evidence' ||
                        String(item.data?.filePath || '').endsWith('/workspace_evidence_report.json')
                      );
                      const evidenceItems = group.items.filter(item => item !== reportArtifact);

                      return (
                        <>
                          <Group
                            justify="space-between"
                            align="center"
                            px={4}
                            py={2}
                            onClick={() => {
                              toggleRunGroup(group.key, reportArtifact);
                            }}
                            style={{ cursor: 'pointer' }}
                          >
                            <Box>
                              <Text size="sm" fw={600} c="carbon.8">
                                {group.label === 'Workspace Evidence' ? t('studio.workspaceEvidence') : group.label}
                              </Text>
                              <Text size="xs" c="carbon.5">
                                {group.label === 'Workspace Evidence' ? t('studio.workspaceEvidenceSubtitle') : t('studio.diagnosisRun')}
                              </Text>
                            </Box>
                            <Group gap="xs" align="center">
                              <Badge size="sm" variant="light" color="blue">
                                {t('studio.itemsCount', { count: evidenceItems.length })}
                              </Badge>
                              <ActionIcon
                                variant="subtle"
                                color="gray"
                                size="sm"
                                onClick={(event: MouseEvent) => {
                                  event.stopPropagation();
                                  toggleRunGroup(group.key, reportArtifact);
                                }}
                                aria-label={expandedRunGroups.has(group.key) ? t('studio.collapseRunGroup') : t('studio.expandRunGroup')}
                              >
                                <span style={{ fontSize: '12px', lineHeight: 1 }}>
                                  {expandedRunGroups.has(group.key) ? '▾' : '▸'}
                                </span>
                              </ActionIcon>
                            </Group>
                          </Group>
                    {expandedRunGroups.has(group.key) && (() => {
                      const analysis = reportArtifact ? runAnalysisByArtifact[reportArtifact.id] : undefined;
                      const isRunAnalysisLoading = Boolean(reportArtifact && runAnalysisLoading.has(reportArtifact.id));
                      const runAnalysisLoadError = reportArtifact ? runAnalysisError[reportArtifact.id] : undefined;
                      const metricsArtifact = evidenceItems.find(item =>
                        String(item.data?.filePath || '').endsWith('/metrics.json')
                      );
                      const requestMetricChart = (item: AuthoredEvidenceItem) => {
                        const metricRef = item.metric_ref;
                        const filePath = String(metricsArtifact?.data?.filePath || '');
                        if (!workspaceId || !metricsArtifact || !filePath || !metricRef?.metric_group_id) return;
                        const requestKey = metricChartRequestKey(metricsArtifact, item);
                        if (metricChartDataByKey[requestKey] || metricChartLoading.has(requestKey) || metricChartError[requestKey]) return;

                        setMetricChartError(prev => ({ ...prev, [requestKey]: '' }));
                        setMetricChartLoading(prev => new Set(prev).add(requestKey));
                        fetchFocusedMetricChart(workspaceId, filePath, metricRef)
                          .then(data => setMetricChartDataByKey(prev => ({ ...prev, [requestKey]: data })))
                          .catch(err => setMetricChartError(prev => ({ ...prev, [requestKey]: String(err) })))
                          .finally(() => setMetricChartLoading(prev => {
                            const next = new Set(prev);
                            next.delete(requestKey);
                            return next;
                          }));
                      };
                      const renderMetricChart = (item: AuthoredEvidenceItem): ReactNode => {
                        if (item.type !== 'metric') return null;
                        if (!metricsArtifact) return <Text size="xs" c="carbon.6">{t('studio.noMetricsFile')}</Text>;
                        if (!item.metric_ref?.metric_group_id) return <Text size="xs" c="carbon.6">{t('studio.noMetricRef')}</Text>;
                        const requestKey = metricChartRequestKey(metricsArtifact, item);
                        const chartData = metricChartDataByKey[requestKey];
                        const isMetricLoading = metricChartLoading.has(requestKey);
                        const metricError = metricChartError[requestKey];
                        if (isMetricLoading) return <Text size="xs" c="carbon.6">{t('studio.loadingMetricChart')}</Text>;
                        if (metricError) return <Text size="xs" c="red">{metricError}</Text>;
                        if (!chartData) {
                          return <Text size="xs" c="carbon.6">{t('studio.noMetricChartData')}</Text>;
                        }
                        return <FocusedMetricChart chart={chartData} focus={item} />;
                      };
                      const renderItem = (artifact: Artifact) => {
                        const title = shortEvidenceTitle(artifact);
                        const isQueries = isQueriesArtifact(artifact);
                        const isExpanded = expandedEvidenceArtifacts.has(artifact.id);
                        const filePath = String(artifact.data?.filePath || '');
                        const queryKind = artifact.data?.queryKind;
                        const evidenceData = evidenceDataByArtifact[artifact.id] ?? null;
                        const queryLinks = isQueries ? queryLinkItems(evidenceData, queryKind) : [];
                        const isLoading = evidenceLoading.has(artifact.id);
                        const error = evidenceError[artifact.id];
                        return (
                          <Box
                            key={artifact.id}
                            onClick={() => {
                              toggleEvidenceArtifact(artifact);
                            }}
                            style={{
                              borderRadius: 10,
                              border: selectedArtifactId === artifact.id ? '2px solid #3b82f6' : '1px solid #e5e7eb',
                              backgroundColor: selectedArtifactId === artifact.id ? '#eff6ff' : '#f8fafc',
                              padding: '10px 12px',
                              cursor: 'pointer',
                              transition: 'all 0.12s ease',
                            }}
                          >
                            <Stack gap={isExpanded ? 8 : 0}>
                              <Group justify="space-between" align="center" wrap="nowrap">
                                <Box style={{ minWidth: 0, flex: 1 }}>
                                  <Text size="sm" fw={600} c="carbon.8">
                                    {title}
                                  </Text>
                                </Box>
                                <Group gap="xs" wrap="nowrap">
                                  <Badge
                                    size="sm"
                                    variant="light"
                                    color={
                                      title === 'Metrics' ? 'green'
                                      : title === 'Log Stats' ? 'orange'
                                      : title === 'Report' ? 'blue'
                                      : title === 'TopSQL' ? 'grape'
                                      : title === 'Slow Query' ? 'violet'
                                      : title === 'Queries' ? 'grape'
                                      : title === 'Links' ? 'cyan'
                                      : 'gray'
                                    }
                                  >
                                    {title}
                                  </Badge>
                                  <Text size="xs" c="carbon.5">{isExpanded ? '▾' : '▸'}</Text>
                                </Group>
                              </Group>
                              {isExpanded && (
                                <Stack gap={6} onClick={event => event.stopPropagation()}>
                                  {isLoading ? (
                                    <Text size="xs" c="carbon.6">{t('studio.loadingEvidence')}</Text>
                                  ) : error ? (
                                    <Text size="xs" c="red">{error}</Text>
                                  ) : isQueries && queryLinks.length ? (
                                    <>
                                      <Group justify="space-between" gap="sm" wrap="nowrap">
                                        <Text size="xs" fw={600} c="carbon.6">{t('studio.colName')}</Text>
                                        <Text size="xs" fw={600} c="carbon.6">{t('studio.colLink')}</Text>
                                      </Group>
                                      {queryLinks.map((item, index) => (
                                        <Group key={`${item.link}-${index}`} justify="space-between" gap="sm" wrap="nowrap">
                                          <Text size="xs" c="carbon.7" truncate style={{ minWidth: 0, flex: 1 }}>
                                            {item.name}
                                          </Text>
                                          <Text
                                            size="xs"
                                            c="blue.7"
                                            component="a"
                                            href={item.link}
                                            target="_blank"
                                            rel="noopener noreferrer"
                                            style={{ textDecoration: 'underline', whiteSpace: 'nowrap' }}
                                          >
                                            {t('studio.openLink')}
                                          </Text>
                                        </Group>
                                      ))}
                                    </>
                                  ) : isQueries ? (
                                    <Text size="xs" c="carbon.6">{t('studio.noQueryLinks', { title })}</Text>
                                  ) : (
                                    <Box
                                      style={{
                                        maxHeight: 520,
                                        overflow: 'auto',
                                        border: '1px solid #e5e7eb',
                                        borderRadius: 8,
                                        backgroundColor: '#fff',
                                      }}
                                    >
                                      <EvidencePreviewPane filePath={filePath} data={evidenceData} />
                                    </Box>
                                  )}
                                </Stack>
                              )}
                            </Stack>
                          </Box>
                        );
                      };

                      return (
                        <RunGroupEvidenceContent
                          analysis={analysis}
                          loading={isRunAnalysisLoading}
                          error={runAnalysisLoadError}
                          evidenceItems={evidenceItems}
                          renderEvidenceFile={renderItem}
                          renderMetricChart={renderMetricChart}
                          onMetricChartRequest={requestMetricChart}
                        />
                      );
                    })()}
                        </>
                      );
                    })()}
                  </Box>
                ))
              }

              {visibleArtifacts.length === 0 && (
                <Box p="lg" style={{ textAlign: 'center' }}>
                  <Text size="sm" c="carbon.5">{t('studio.noArtifacts')}</Text>
                  <Text size="xs" c="carbon.5" mt={4}>
                    {t('studio.noArtifactsHint')}
                  </Text>
                </Box>
              )}
            </Stack>
          ) : isInsightReport ? (
            reportLoading ? (
              <Box p="lg" style={{ textAlign: 'center' }}><Text size="sm" c="carbon.5">{t('studio.loadingReport')}</Text></Box>
            ) : reportError ? (
              <Box p="lg"><Text size="sm" c="red">{reportError}</Text></Box>
            ) : insightData ? (
              <InsightReport data={insightData} />
            ) : null
          ) : isAnomalyReport ? (
            reportLoading ? (
              <Box p="lg" style={{ textAlign: 'center' }}><Text size="sm" c="carbon.5">{t('studio.loadingReport')}</Text></Box>
            ) : reportError ? (
              <Box p="lg"><Text size="sm" c="red">{reportError}</Text></Box>
            ) : diagnosticData ? (
              <AnomaliesPanel data={diagnosticData} resourceWorkerData={resourceWorkerData} />
            ) : null
          ) : isDiagnosisEvidence ? (
            workspaceId && typeof selectedArtifact?.data?.filePath === 'string' ? (
              <DiagnosisEvidencePanel
                workspaceId={workspaceId}
                reportPath={selectedArtifact.data.filePath}
              />
            ) : (
              <Box p="md"><Text size="sm" c="carbon.6">{t('studio.noEvidenceFile')}</Text></Box>
            )
          ) : isDiagnosisReport || isWorkspaceEvidenceReport ? (
            reportLoading ? (
              <Box p="lg" style={{ textAlign: 'center' }}><Text size="sm" c="carbon.5">{t('studio.loadingEvidenceReport')}</Text></Box>
            ) : reportError ? (
              <Box p="lg"><Text size="sm" c="red">{reportError}</Text></Box>
            ) : diagnosisDataMap[selectedArtifact?.id || ''] || selectedArtifact?.data?.virtualOnly ? (
              <Tabs value={diagnosisDetailTab} onChange={(value) => setDiagnosisDetailTab((value as 'report' | 'evidence') || 'report')}>
                <Box px="md" pt="md" style={{ borderBottom: '1px solid #e5e7eb', backgroundColor: '#fff' }}>
                  <Tabs.List>
                    <Tabs.Tab value="report">{t('studio.tabReport')}</Tabs.Tab>
                    <Tabs.Tab value="evidence">{t('studio.tabEvidence')}</Tabs.Tab>
                  </Tabs.List>
                </Box>

                <Tabs.Panel value="report">
                  {diagnosisDataMap[selectedArtifact!.id] ? (
                    <DiagnosisReport data={diagnosisDataMap[selectedArtifact!.id]} />
                  ) : (
                    <Box p="md">
                      <Text size="sm" c="carbon.6">{t('studio.noEvidenceReportJson')}</Text>
                    </Box>
                  )}
                </Tabs.Panel>

                <Tabs.Panel value="evidence">
                  {workspaceId && typeof selectedArtifact?.data?.filePath === 'string' ? (
                    <DiagnosisEvidencePanel
                      workspaceId={workspaceId}
                      reportPath={selectedArtifact.data.filePath}
                    />
                  ) : (
                    <Box p="md">
                      <Text size="sm" c="carbon.6">{t('studio.noEvidenceDir')}</Text>
                    </Box>
                  )}
                </Tabs.Panel>
              </Tabs>
            ) : null
          ) : (
            <Box p="md">
              <Text size="sm" c="carbon.6">
                {selectedArtifact
                  ? t('studio.viewing', { title: selectedArtifact.title })
                  : t('studio.selectArtifact')}
              </Text>
            </Box>
          )}
          </Box>
        )}
      </div>

      {/* Note modal */}
      <NoteModal
        opened={noteModalOpen}
        onClose={() => {
          setNoteModalOpen(false);
          setEditingNote(null);
        }}
        note={editingNote}
        onSave={(title, content) => {
          if (editingNote) {
            // Update existing note
            updateArtifact(editingNote.id, {
              title,
              description: content.slice(0, 80) + (content.length > 80 ? '...' : ''),
              data: { ...editingNote.data, content },
            });
          } else {
            // Create new note
            addArtifact({
              type: 'note',
              title,
              description: content.slice(0, 80) + (content.length > 80 ? '...' : ''),
              data: { content },
            });
          }
        }}
      />
    </Stack>
  );
}
