/**
 * ProvenanceView — 4-quadrant display:
 *   Top-left:  Parameters (proxymodel, LLM config, live override, server)
 *   Top-right: Preview (diff from upstream after replay)
 *   Bottom-left:  Upstream (raw) — the origin, optionally editable
 *   Bottom-right: Client (diff from upstream)
 */

import { Box, Text } from 'ink';
import { Spinner, TextInput } from '@inkjs/ui';
import type { TimelineEvent, ReplayConfig, ReplayResult, ProxyModelDetails } from '../unified-types.js';
import { computeDiffLines, formatDiffStats, DiffPanel } from './diff-renderer.js';

interface ProvenanceViewProps {
  clientEvent: TimelineEvent;
  upstreamEvent: TimelineEvent | null;
  height: number;
  scrollOffset: number;
  horizontalOffset: number;
  focusedPanel: 'client' | 'upstream' | 'parameters' | 'preview';
  parameterIdx: number; // 0=ProxyModel, 1=Provider, 2=Model, 3=Live, 4=Server
  replayConfig: ReplayConfig;
  replayResult: ReplayResult | null;
  replayRunning: boolean;
  editingUpstream: boolean;
  editedContent: string;
  onEditContent: (text: string) => void;
  proxyModelDetails: ProxyModelDetails | null;
  liveOverride: boolean;
  serverList: string[];
  serverOverrides: Record<string, string>;
  selectedServerIdx: number;
  serverPickerOpen: boolean;
  modelPickerOpen: boolean;
  modelPickerIdx: number;
  availableModels: string[];
  searchMode: boolean;
  searchQuery: string;
  searchMatches: number[];
  searchMatchIdx: number;
}

export function getContentText(event: TimelineEvent): string {
  const body = event.body as Record<string, unknown> | null;
  if (!body) return '(no body)';

  const result = body['result'] as Record<string, unknown> | undefined;
  if (!result) return JSON.stringify(body, null, 2);

  const content = (result['content'] ?? result['contents'] ?? []) as Array<{ text?: string }>;
  if (content.length > 0) {
    return content.map((c) => c.text ?? '').join('\n');
  }

  return JSON.stringify(result, null, 2);
}

export function ProvenanceView({
  clientEvent,
  upstreamEvent,
  height,
  scrollOffset,
  horizontalOffset,
  focusedPanel,
  parameterIdx,
  replayConfig,
  replayResult,
  replayRunning,
  editingUpstream,
  editedContent,
  onEditContent,
  proxyModelDetails,
  liveOverride,
  serverList,
  serverOverrides,
  selectedServerIdx,
  serverPickerOpen,
  modelPickerOpen,
  modelPickerIdx,
  availableModels,
  searchMode,
  searchQuery,
  searchMatches,
  searchMatchIdx,
}: ProvenanceViewProps) {
  // Split height: top half for params+preview, bottom half for upstream+client
  const topHeight = Math.max(4, Math.floor((height - 2) * 0.35));
  const bottomHeight = Math.max(4, height - topHeight - 2);

  const upstreamText = editedContent || (upstreamEvent ? getContentText(upstreamEvent) : '(no upstream event found)');
  const clientText = getContentText(clientEvent);
  const upstreamChars = upstreamText.length;

  // Upstream raw lines (for the origin panel)
  const upstreamLines = upstreamText.split('\n');
  const bottomBodyHeight = Math.max(1, bottomHeight - 3);
  // Route scrollOffset and horizontalOffset to only the focused panel
  const upstreamScroll = focusedPanel === 'upstream' ? scrollOffset : 0;
  const clientScroll = focusedPanel === 'client' ? scrollOffset : 0;
  const previewScroll = focusedPanel === 'preview' ? scrollOffset : 0;
  const upstreamHScroll = focusedPanel === 'upstream' ? horizontalOffset : 0;
  const clientHScroll = focusedPanel === 'client' ? horizontalOffset : 0;
  const previewHScroll = focusedPanel === 'preview' ? horizontalOffset : 0;
  const upstreamVisible = upstreamLines.slice(upstreamScroll, upstreamScroll + bottomBodyHeight);

  // Client diff (from upstream)
  const clientDiff = computeDiffLines(upstreamText, clientText);

  // Preview diff (from upstream, when replay result available)
  let previewDiff = { lines: [] as ReturnType<typeof computeDiffLines>['lines'], stats: { added: 0, removed: 0, pctChanged: 0 } };
  let previewError: string | null = null;
  let previewReady = false;

  if (replayRunning) {
    // spinner handles this
  } else if (replayResult?.error) {
    previewError = replayResult.error;
  } else if (replayResult) {
    previewDiff = computeDiffLines(upstreamText, replayResult.content);
    previewReady = true;
  }

  const previewBodyHeight = Math.max(1, topHeight - 3);

  // Server display for row 4 — show per-server override if set
  const selectedServerName = selectedServerIdx >= 0 ? serverList[selectedServerIdx] : undefined;
  const serverOverrideModel = selectedServerName ? serverOverrides[selectedServerName] : undefined;
  const serverDisplay = selectedServerIdx < 0
    ? '(project-wide)'
    : `${selectedServerName ?? '(unknown)'}${serverOverrideModel ? ` [${serverOverrideModel}]` : ''}`;

  // Build parameter rows
  const paramRows = [
    { label: 'ProxyModel', value: replayConfig.proxyModel },
    { label: 'Provider  ', value: replayConfig.provider ?? '(default)' },
    { label: 'Model     ', value: replayConfig.llmModel ?? '(default)' },
    { label: 'Live      ', value: liveOverride ? 'ON' : 'OFF', isLive: true },
    { label: 'Server    ', value: serverDisplay },
  ];

  // Build preview header
  let previewHeader = 'Preview';
  if (replayRunning) {
    previewHeader = 'Preview (running...)';
  } else if (previewError) {
    previewHeader = 'Preview (error)';
  } else if (previewReady) {
    previewHeader = `Preview (diff, ${formatDiffStats(previewDiff.stats)})`;
  }

  // Build client header
  const clientHeader = `Client (diff, ${formatDiffStats(clientDiff.stats)})`;

  // Show tooltip when ProxyModel row focused
  const showTooltip = focusedPanel === 'parameters' && parameterIdx === 0 && proxyModelDetails != null;

  return (
    <Box flexDirection="column" height={height}>
      {/* Top row: Parameters + Preview */}
      <Box flexDirection="row" height={topHeight}>
        {/* Parameters panel */}
        <Box
          flexDirection="column"
          width="50%"
          borderStyle="single"
          borderColor={focusedPanel === 'parameters' ? 'cyan' : 'gray'}
          paddingX={1}
        >
          {/* When server picker is open, show ONLY the picker (full panel height) */}
          {serverPickerOpen && focusedPanel === 'parameters' && parameterIdx === 4 ? (
            <>
              <Text bold color="cyan">Select Server</Text>
              <Text key="project-wide">
                <Text color={selectedServerIdx === -1 ? 'cyan' : undefined}>
                  {selectedServerIdx === -1 ? '\u25B6 ' : '  '}
                </Text>
                <Text bold={selectedServerIdx === -1}>(project-wide)</Text>
                {serverOverrides['*'] && <Text dimColor> [{serverOverrides['*']}]</Text>}
              </Text>
              {serverList.map((name, i) => (
                <Text key={name}>
                  <Text color={selectedServerIdx === i ? 'cyan' : undefined}>
                    {selectedServerIdx === i ? '\u25B6 ' : '  '}
                  </Text>
                  <Text bold={selectedServerIdx === i}>{name}</Text>
                  {serverOverrides[name] && <Text dimColor> [{serverOverrides[name]}]</Text>}
                </Text>
              ))}
              <Text dimColor>{'\u2191\u2193'}:navigate  Enter:select  Esc:cancel</Text>
            </>
          ) : modelPickerOpen && focusedPanel === 'parameters' && selectedServerIdx >= 0 ? (
            <>
              <Text bold color="cyan">
                ProxyModel for {serverList[selectedServerIdx] ?? '(unknown)'}
              </Text>
              {availableModels.map((name, i) => {
                const serverName = serverList[selectedServerIdx] ?? '';
                const isCurrentOverride = serverOverrides[serverName] === name;
                return (
                  <Text key={name}>
                    <Text color={modelPickerIdx === i ? 'cyan' : undefined}>
                      {modelPickerIdx === i ? '\u25B6 ' : '  '}
                    </Text>
                    <Text bold={modelPickerIdx === i}>{name}</Text>
                    {isCurrentOverride && <Text color="green"> (active)</Text>}
                  </Text>
                );
              })}
              <Text dimColor>{'\u2191\u2193'}:navigate  Enter:apply  Esc:cancel</Text>
            </>
          ) : (
            <>
              <Text bold color={focusedPanel === 'parameters' ? 'cyan' : 'magenta'}>Parameters</Text>
              {paramRows.map((row, i) => {
                const isFocused = focusedPanel === 'parameters' && parameterIdx === i;
                const isLiveRow = 'isLive' in row;
                return (
                  <Text key={i}>
                    <Text color={isFocused ? 'cyan' : undefined}>{isFocused ? '\u25C0 ' : '  '}</Text>
                    <Text dimColor={!isFocused}>{row.label}: </Text>
                    {isLiveRow ? (
                      <Text bold={isFocused} color={liveOverride ? 'green' : undefined}>
                        {row.value}
                      </Text>
                    ) : (
                      <Text bold={isFocused}>{row.value}</Text>
                    )}
                    <Text color={isFocused ? 'cyan' : undefined}>{isFocused ? ' \u25B6' : ''}</Text>
                  </Text>
                );
              })}

              {/* ProxyModel details tooltip */}
              {showTooltip && proxyModelDetails && (
                <Box
                  flexDirection="column"
                  borderStyle="round"
                  borderColor="magenta"
                  paddingX={1}
                  marginTop={0}
                >
                  <Text bold color="magenta">{proxyModelDetails.name}</Text>
                  <Text dimColor>
                    {proxyModelDetails.type === 'plugin' ? 'plugin' : proxyModelDetails.source}
                    {proxyModelDetails.cacheable ? ', cached' : ''}
                    {proxyModelDetails.appliesTo && proxyModelDetails.appliesTo.length > 0 ? ` \u00B7 ${proxyModelDetails.appliesTo.join(', ')}` : ''}
                  </Text>
                  {proxyModelDetails.hooks && proxyModelDetails.hooks.length > 0 && (
                    <Text dimColor>Hooks: {proxyModelDetails.hooks.join(', ')}</Text>
                  )}
                  {(proxyModelDetails.stages ?? []).map((stage, i) => (
                    <Text key={i}>
                      <Text color="yellow">{i + 1}. {stage.type}</Text>
                      {stage.config && Object.keys(stage.config).length > 0 && (
                        <Text dimColor>
                          {' '}{Object.entries(stage.config).map(([k, v]) => `${k}=${String(v)}`).join(' ')}
                        </Text>
                      )}
                    </Text>
                  ))}
                </Box>
              )}

              {/* Per-server overrides summary */}
              {Object.keys(serverOverrides).length > 0 && (
                <Text dimColor wrap="truncate">
                  Overrides: {Object.entries(serverOverrides).map(([s, m]) => `${s}=${m}`).join(', ')}
                </Text>
              )}
            </>
          )}
        </Box>

        {/* Preview panel — diff from upstream */}
        <Box
          flexDirection="column"
          width="50%"
          borderStyle="single"
          borderColor={focusedPanel === 'preview' ? 'cyan' : 'gray'}
          paddingX={1}
        >
          <Text bold color={focusedPanel === 'preview' ? 'cyan' : 'green'}>
            {previewHeader}
          </Text>
          {replayRunning ? (
            <Spinner label="Running replay..." />
          ) : previewError ? (
            <Text color="red" wrap="truncate">Error: {previewError}</Text>
          ) : previewReady ? (
            <DiffPanel lines={previewDiff.lines} scrollOffset={previewScroll} height={previewBodyHeight} horizontalOffset={previewHScroll} />
          ) : (
            <Text dimColor>Press Enter to run preview</Text>
          )}
        </Box>
      </Box>

      {/* Bottom row: Upstream (raw) + Client (diff) */}
      <Box flexDirection="row" height={bottomHeight}>
        {/* Upstream panel — origin, raw text */}
        <Box
          flexDirection="column"
          width="50%"
          borderStyle="single"
          borderColor={focusedPanel === 'upstream' ? 'cyan' : 'gray'}
          paddingX={1}
        >
          <Box>
            <Text bold color={focusedPanel === 'upstream' ? 'cyan' : 'yellowBright'}>
              Upstream (raw, {upstreamChars} chars)
            </Text>
            {editingUpstream && <Text color="yellow"> [EDITING]</Text>}
          </Box>
          {upstreamEvent?.upstreamName && upstreamEvent.upstreamName.includes(',') && (
            <Text dimColor wrap="truncate">{upstreamEvent.upstreamName}</Text>
          )}
          {editingUpstream ? (
            <Box flexGrow={1}>
              <TextInput defaultValue={editedContent} onChange={onEditContent} />
            </Box>
          ) : (
            <>
              {upstreamVisible.map((line, i) => (
                <Text key={i} wrap="truncate">{upstreamHScroll > 0 ? (line || ' ').slice(upstreamHScroll) : (line || ' ')}</Text>
              ))}
              {upstreamLines.length > upstreamScroll + bottomBodyHeight && (
                <Text dimColor>{'\u2026'} +{upstreamLines.length - upstreamScroll - bottomBodyHeight} more</Text>
              )}
            </>
          )}
        </Box>

        {/* Client panel — diff from upstream */}
        <Box
          flexDirection="column"
          width="50%"
          borderStyle="single"
          borderColor={focusedPanel === 'client' ? 'cyan' : 'gray'}
          paddingX={1}
        >
          <Text bold color={focusedPanel === 'client' ? 'cyan' : 'blue'}>
            {clientHeader}
          </Text>
          <DiffPanel lines={clientDiff.lines} scrollOffset={clientScroll} height={bottomBodyHeight} horizontalOffset={clientHScroll} />
        </Box>
      </Box>

      {/* Footer */}
      <Box paddingX={1}>
        {searchMode || searchQuery.length > 0 ? (
          <Text>
            <Text color="cyan">/{searchQuery}</Text>
            {searchMatches.length > 0 && (
              <Text dimColor> [{searchMatchIdx + 1}/{searchMatches.length}]  n:next N:prev Esc:clear</Text>
            )}
            {searchQuery.length > 0 && searchMatches.length === 0 && (
              <Text dimColor> (no matches)</Text>
            )}
            {searchMode && <Text color="cyan">_</Text>}
          </Text>
        ) : (
          <Text dimColor>Tab:panel  {'\u2191\u2193'}:scroll  {'\u2190\u2192'}:pan/param  /:search  Enter:run/toggle  e:edit  Esc:close</Text>
        )}
      </Box>
    </Box>
  );
}
