<template>
  <div class="diff-viewer">
    <NAlert v-if="truncated" type="warning" :show-icon="true" class="mb-2">
      差异内容过大，已截断显示（上限 1 MiB）。
    </NAlert>

    <NEmpty v-if="files.length === 0" description="没有差异" />

    <div v-for="(file, fi) in files" :key="fi" class="diff-file">
      <div class="diff-file-header">
        {{ file.header }}
      </div>
      <table class="diff-table">
        <tbody>
          <tr v-for="(line, li) in file.lines" :key="li" :class="lineClass(line.kind)">
            <td class="diff-gutter">
              {{
                line.kind === 'add'
                  ? '+'
                  : line.kind === 'del'
                    ? '-'
                    : line.kind === 'hunk'
                      ? ''
                      : ' '
              }}
            </td>
            <td class="diff-code">
              <pre>{{ line.text }}</pre>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { NAlert, NEmpty } from 'naive-ui'

type LineKind = 'add' | 'del' | 'context' | 'hunk' | 'meta'
interface DiffLine {
  kind: LineKind
  text: string
}
interface DiffFile {
  header: string
  lines: DiffLine[]
}

const props = defineProps<{ diff: string; truncated?: boolean }>()

// parseUnifiedDiff splits raw `git diff` text into per-file blocks and classifies
// each line for +/- coloring. No external lib: the format is simple enough.
const files = computed<DiffFile[]>(() => {
  const out: DiffFile[] = []
  if (!props.diff) return out
  let current: DiffFile | null = null
  for (const raw of props.diff.split('\n')) {
    if (raw.startsWith('diff --git')) {
      current = { header: raw.replace('diff --git ', ''), lines: [] }
      out.push(current)
      continue
    }
    if (!current) {
      current = { header: '(diff)', lines: [] }
      out.push(current)
    }
    let kind: LineKind = 'context'
    if (raw.startsWith('@@')) kind = 'hunk'
    else if (
      raw.startsWith('+++') ||
      raw.startsWith('---') ||
      raw.startsWith('index ') ||
      raw.startsWith('new file') ||
      raw.startsWith('deleted file') ||
      raw.startsWith('similarity') ||
      raw.startsWith('rename ')
    )
      kind = 'meta'
    else if (raw.startsWith('+')) kind = 'add'
    else if (raw.startsWith('-')) kind = 'del'
    const text = kind === 'add' || kind === 'del' ? raw.slice(1) : raw
    current.lines.push({ kind, text })
  }
  return out
})

const truncated = computed(() => props.truncated === true)

function lineClass(kind: LineKind): string {
  return `diff-line diff-${kind}`
}
</script>

<style scoped>
.diff-file {
  border: 1px solid var(--n-border-color, #e0e0e6);
  border-radius: 4px;
  margin-bottom: 12px;
  overflow: hidden;
}
.diff-file-header {
  background: #f5f5f7;
  padding: 6px 10px;
  font-family: monospace;
  font-size: 12px;
  font-weight: 600;
  border-bottom: 1px solid #e0e0e6;
}
.diff-table {
  width: 100%;
  border-collapse: collapse;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-size: 12px;
}
.diff-gutter {
  width: 24px;
  text-align: center;
  color: #999;
  user-select: none;
  padding: 0 4px;
  vertical-align: top;
}
.diff-code {
  white-space: pre-wrap;
  word-break: break-all;
  padding: 0 6px;
}
.diff-code pre {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-all;
}
.diff-add {
  background: #e6ffed;
}
.diff-add .diff-gutter {
  color: #22863a;
}
.diff-del {
  background: #ffeef0;
}
.diff-del .diff-gutter {
  color: #cb2431;
}
.diff-hunk {
  background: #f1f8ff;
  color: #6f42c1;
}
.diff-meta {
  color: #6a737d;
}
</style>
