<template>
  <aside class="git-changes-panel" :style="{ width: width + 'px' }">
    <header class="git-changes-panel__header">
      <div class="git-changes-panel__title">
        <span class="git-changes-panel__branch">{{ branchLabel }}</span>
        <span class="git-changes-panel__arrow">→</span>
        <span class="git-changes-panel__upstream">{{ upstreamLabel }}</span>
      </div>
      <div class="git-changes-panel__summary">
        <span class="git-changes-panel__add">+{{ additions }}</span>
        <span class="git-changes-panel__del">-{{ deletions }}</span>
        <button type="button" class="git-changes-panel__close" aria-label="关闭 Git 变更面板" @click="$emit('close')">
          ×
        </button>
      </div>
    </header>

    <div class="git-changes-panel__body">
      <div v-if="loading && !diff" class="git-changes-panel__empty">正在加载 Git 变更…</div>
      <div v-else-if="error && !files.length" class="git-changes-panel__empty git-changes-panel__empty--error">{{ error }}</div>
      <div v-else-if="!files.length" class="git-changes-panel__empty">当前没有未提交变更</div>
      <section v-for="file in files" v-else :key="file.path" class="git-change-file">
        <div class="git-change-file__header">
          <span class="git-change-file__title">
            <span class="git-change-file__open-wrap">
              <button
                type="button"
                class="git-change-file__open-btn"
                :class="{ open: openMenuPath === file.path || appsLoadingPath === file.path }"
                :disabled="!hostMode"
                :title="hostMode ? '用应用打开文件' : 'Docker 模式不可用'"
                @click.stop.prevent="toggleOpenMenu(file.path)"
              >
                <img class="git-change-file__open-icon" :src="filePenIcon" alt="" aria-hidden="true" />
                <span class="git-change-file__open-caret" aria-hidden="true"></span>
              </button>
              <div
                v-if="openMenuPath === file.path"
                class="git-change-file__open-menu"
                @click.stop
              >
                <div v-if="openAppsError" class="git-change-file__open-empty">{{ openAppsError }}</div>
                <button
                  v-for="app in openApps"
                  v-else
                  :key="app.id"
                  type="button"
                  class="git-change-file__open-option"
                  @click.stop.prevent="openFileWithApp(file.path, app.id)"
                >
                  <img
                    class="git-change-file__open-option-icon"
                    :src="app.icon_url || filePenIcon"
                    alt=""
                    aria-hidden="true"
                  />
                  <span>{{ app.label }}</span>
                </button>
                <div v-if="!appsLoadingPath && !openAppsError && !openApps.length" class="git-change-file__open-empty">
                  未检测到可用应用
                </div>
              </div>
            </span>
            <span class="git-change-file__path">{{ file.path }}</span>
            <button
              v-if="canResetContext(file.path)"
              type="button"
              class="git-change-file__reset"
              @click.stop.prevent="handleResetContext(file.path)"
            >
              恢复
            </button>
          </span>
          <span class="git-change-file__stats">
            <span class="git-changes-panel__add">+{{ file.additions || 0 }}</span>
            <span class="git-changes-panel__del">-{{ file.deletions || 0 }}</span>
          </span>
        </div>

        <div class="git-change-file__diff">
          <div class="git-change-file__diff-inner">
            <template v-for="(hunk, hunkIndex) in file.hunks" :key="`${file.path}-${hunkIndex}`">
              <button
                v-if="hunk.before_hidden > 0"
                type="button"
                class="git-change-file__fold"
                @click.stop.prevent="handleExpandContext(file.path, hunk.before_fold_key)"
              >
                <span class="git-change-file__fold-content">
                  <span class="git-change-file__fold-icon git-change-file__fold-icon--up" aria-hidden="true"></span>
                  <span>{{ hunk.before_hidden }} 行未编辑的内容</span>
                </span>
              </button>
              <div
                v-for="(line, lineIndex) in hunk.lines"
                :key="`${file.path}-${hunkIndex}-${lineIndex}`"
                class="git-change-line"
                :class="{
                  'git-change-line--add': line.kind === 'add',
                  'git-change-line--delete': line.kind === 'delete'
                }"
              >
                <span class="git-change-line__num">{{ formatLineNumber(line) }}</span>
                <span class="git-change-line__marker">{{ lineMarker(line.kind) }}</span>
                <span class="git-change-line__text">{{ line.text }}</span>
              </div>
            </template>
            <button
              v-if="file.after_hidden > 0"
              type="button"
              class="git-change-file__fold"
              @click.stop.prevent="handleExpandContext(file.path, file.after_fold_key || 'after')"
            >
              <span class="git-change-file__fold-content">
                <span class="git-change-file__fold-icon git-change-file__fold-icon--down" aria-hidden="true"></span>
                <span>{{ file.after_hidden }} 行未编辑的内容</span>
              </span>
            </button>
          </div>
        </div>
      </section>
    </div>
  </aside>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { ref } from 'vue';

defineOptions({ name: 'GitChangesPanel' });

const props = defineProps<{
  width: number;
  loading?: boolean;
  error?: string;
  hostMode?: boolean;
  foldContexts?: Record<string, Record<string, number>>;
  diff: Record<string, any> | null;
}>();

const emit = defineEmits<{
  (event: 'close'): void;
  (event: 'expand-context', payload: { path: string; foldKey: string }): void;
  (event: 'reset-context', path: string): void;
}>();

const branchLabel = computed(() => String(props.diff?.branch || 'HEAD'));
const upstreamLabel = computed(() => String(props.diff?.upstream || '未设置云端分支'));
const additions = computed(() => Math.max(0, Number(props.diff?.additions || 0)));
const deletions = computed(() => Math.max(0, Number(props.diff?.deletions || 0)));
const files = computed(() => (Array.isArray(props.diff?.files) ? props.diff.files : []));
const canResetContext = (path: string) => {
  const fileMap = props.foldContexts?.[path];
  return !!fileMap && Object.values(fileMap).some((value) => Number(value || 0) > 0);
};
const filePenIcon = new URL('../../../icons/file-pen.svg', import.meta.url).href;
const openMenuPath = ref('');
const openApps = ref<Array<{ id: string; label: string; icon_url?: string }>>([]);
const openAppsError = ref('');
const appsLoadingPath = ref('');

const formatLineNumber = (line: Record<string, any>) => {
  const rawValue = line?.kind === 'delete' ? line.old_line : line?.new_line;
  const num = Number(rawValue);
  return Number.isFinite(num) && num > 0 ? String(num) : '';
};

const lineMarker = (kind: string) => {
  if (kind === 'add') return '+';
  if (kind === 'delete') return '-';
  return ' ';
};

const handleExpandContext = (path: string, foldKey: string) => {
  emit('expand-context', { path, foldKey });
};

const handleResetContext = (path: string) => {
  emit('reset-context', path);
};

const toggleOpenMenu = async (path: string) => {
  if (!props.hostMode) return;
  if (openMenuPath.value === path) {
    openMenuPath.value = '';
    return;
  }
  openMenuPath.value = '';
  openApps.value = [];
  openAppsError.value = '';
  appsLoadingPath.value = path;
  try {
    const resp = await fetch(`/api/project/file-open-apps?path=${encodeURIComponent(path)}`);
    const payload = await resp.json().catch(() => ({}));
    if (!resp.ok || !payload?.success) {
      throw new Error(payload?.error || '检测应用失败');
    }
    openApps.value = Array.isArray(payload?.data?.apps) ? payload.data.apps : [];
    openMenuPath.value = path;
  } catch (error: any) {
    openAppsError.value = error?.message || '检测应用失败';
    openMenuPath.value = path;
  } finally {
    appsLoadingPath.value = '';
  }
};

const openFileWithApp = async (path: string, appId: string) => {
  try {
    const resp = await fetch('/api/project/open-file-with-app', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ path, app_id: appId })
    });
    const payload = await resp.json().catch(() => ({}));
    if (!resp.ok || !payload?.success) {
      throw new Error(payload?.error || '打开文件失败');
    }
    openMenuPath.value = '';
  } catch (error) {
    openAppsError.value = error instanceof Error ? error.message : '打开文件失败';
  }
};
</script>
