<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { storeToRefs } from 'pinia'
import yaml from 'js-yaml'
import { useEditorStore } from '@/stores/editor'
import { useAuthStore } from '@/stores/auth'
import { usePostHog } from '@/composables/usePostHog'
import { useKeyboardShortcuts } from '@/composables/useKeyboardShortcuts'
import { useResizablePane } from '@/composables/useResizablePane'
import { saveSpec, saveSpecAsPR, parseSpec, previewSpec } from '@/api/editor'
import { ApiError } from '@/api/client'
import type { SpecFrontmatter, SpecSection } from '@/api/types'
import AiSpecGenerator from './AiSpecGenerator.vue'
import EditorToolbar from './EditorToolbar.vue'
import FrontmatterForm from './FrontmatterForm.vue'
import MonacoEditor from './MonacoEditor.vue'
import StructuredEditor from './StructuredEditor.vue'
import ConflictModal from './ConflictModal.vue'
import PreviewPane from './PreviewPane.vue'
import MarkdownPreview from './MarkdownPreview.vue'
import SourceModeAi from './SourceModeAi.vue'
import AiDiffReview from './AiDiffReview.vue'
import SaveDialog from './SaveDialog.vue'
import type { SaveDialogResult } from './SaveDialog.vue'

const props = defineProps<{
  owner: string
  repo: string
  isNew: boolean
  configMode?: boolean
}>()

const editorStore = useEditorStore()
const auth = useAuthStore()
const { track } = usePostHog()
const { mode, frontmatter, sourceContent, sections, conflict, saving, dirty, sha, filePath } = storeToRefs(editorStore)

const previewHtml = ref('')
const showPreview = ref(false)
const statusMessage = ref('')
const statusType = ref<'success' | 'error'>('success')
const showGitHubPrompt = ref(false)
const monacoRef = ref<InstanceType<typeof MonacoEditor> | null>(null)
const sourceModeAiRef = ref<InstanceType<typeof SourceModeAi> | null>(null)
const splitContainerRef = ref<HTMLElement | null>(null)
const { splitPercent, layout, isDragging, startDrag, setLayout } = useResizablePane(50)

const showSaveDialog = ref(false)
const saveDialogMode = ref<'commit' | 'pr'>('commit')

const showDiffReview = computed(() => sourceModeAiRef.value?.showPanel ?? false)
const diffOriginal = computed(() => sourceModeAiRef.value?.capturedOriginal ?? '')
const diffModified = computed(() => {
  const ai = sourceModeAiRef.value
  if (!ai) return ''
  // For generate_acs without selection, show original doc vs doc + appended ACs
  if (ai.currentAction === 'generate_acs' && !ai.hadSelection) {
    if (!ai.suggestionContent) return ai.capturedOriginal
    return ai.capturedOriginal.trimEnd() + '\n\n' + ai.suggestionContent.trim() + '\n'
  }
  return ai.suggestionContent
})

// Parse frontmatter from source content
function parseFrontmatterFromSource() {
  const content = sourceContent.value
  const match = content.match(/^---\n([\s\S]*?)\n---/)
  if (match) {
    try {
      const parsed = yaml.load(match[1]) as Record<string, unknown>
      frontmatter.value = {
        title: String(parsed.title || ''),
        status: String(parsed.status || 'draft'),
        owner: String(parsed.owner || ''),
        team: String(parsed.team || ''),
        tags: Array.isArray(parsed.tags) ? parsed.tags.map(String) : [],
        review_status: parsed.review_status ? String(parsed.review_status) as SpecFrontmatter['review_status'] : undefined,
        created: parsed.created ? String(parsed.created) : undefined,
        updated: parsed.updated ? String(parsed.updated) : undefined,
        doc_type: parsed.doc_type ? String(parsed.doc_type) as SpecFrontmatter['doc_type'] : undefined,
        ticket_project: parsed.ticket_project ? String(parsed.ticket_project) : undefined,
      }
    } catch {
      // Invalid YAML, keep current frontmatter
    }
  }
}

// Build complete markdown from frontmatter + body
function buildMarkdown(): string {
  if (mode.value === 'source') {
    return sourceContent.value
  }

  // Structured mode: rebuild from frontmatter + sections
  const fm: Record<string, unknown> = {
    title: frontmatter.value.title,
    status: frontmatter.value.status,
  }
  if (frontmatter.value.owner) fm.owner = frontmatter.value.owner
  if (frontmatter.value.team) fm.team = frontmatter.value.team
  if (frontmatter.value.tags?.length) fm.tags = frontmatter.value.tags
  if (frontmatter.value.review_status) fm.review_status = frontmatter.value.review_status
  if (frontmatter.value.created) fm.created = frontmatter.value.created
  if (frontmatter.value.updated) fm.updated = frontmatter.value.updated

  let md = `---\n${yaml.dump(fm, { lineWidth: -1 }).trim()}\n---\n\n`

  for (const section of sections.value) {
    md += `## ${section.title}\n\n`
    const prose = section.prose_content || section.content
    if (prose.trim()) {
      md += `${prose.trim()}\n\n`
    }
    if (section.acceptance_criteria.length > 0) {
      md += `### Acceptance Criteria\n\n`
      for (const ac of section.acceptance_criteria) {
        md += `- [${ac.checked ? 'x' : ' '}] ${ac.text}\n`
      }
      md += '\n'
    }
  }

  return md.trim() + '\n'
}

// File path for new specs
const newFilePath = ref('docs/specs/new-spec.md')

const currentPath = computed(() => props.isNew ? newFilePath.value : filePath.value)
const defaultCommitMessage = computed(() => `docs: update ${currentPath.value}`)

function openSaveDialog() {
  if (showDiffReview.value) return
  if (!auth.hasGitHub) { showGitHubPrompt.value = true; return }
  saveDialogMode.value = 'commit'
  showSaveDialog.value = true
}

function openPRDialog() {
  if (!auth.hasGitHub) { showGitHubPrompt.value = true; return }
  saveDialogMode.value = 'pr'
  showSaveDialog.value = true
}

async function handleSaveConfirm(result: SaveDialogResult) {
  showSaveDialog.value = false
  saving.value = true
  statusMessage.value = ''
  const content = buildMarkdown()
  const path = currentPath.value

  if (saveDialogMode.value === 'commit') {
    try {
      const res = await saveSpec(auth.org, props.owner, props.repo, path, {
        content,
        sha: sha.value,
        message: result.message,
      })
      if (res.ok && res.sha) {
        editorStore.markSaved(res.sha)
        statusMessage.value = 'Saved!'
        statusType.value = 'success'
        track('spec_saved', { method: 'direct', repo: `${props.owner}/${props.repo}`, file_path: path })
      }
    } catch (err) {
      if (err instanceof ApiError && err.status === 409) {
        const body = err.body as { server_content?: string; server_sha?: string }
        if (body?.server_content && body?.server_sha) {
          editorStore.setConflict(body.server_content, body.server_sha)
        }
        statusMessage.value = 'Conflict detected'
        statusType.value = 'error'
      } else {
        statusMessage.value = err instanceof Error ? err.message : 'Save failed'
        statusType.value = 'error'
      }
    } finally {
      saving.value = false
    }
  } else {
    try {
      const res = await saveSpecAsPR(auth.org, props.owner, props.repo, path, {
        content,
        sha: sha.value,
        message: result.message,
        branch_name: result.branchName,
        pr_title: result.prTitle,
        pr_body: result.prBody,
      })
      if (res.ok && res.pr_url) {
        dirty.value = false
        statusMessage.value = `PR created: #${res.pr_number}`
        statusType.value = 'success'
        track('spec_saved', { method: 'pr', repo: `${props.owner}/${props.repo}`, file_path: path })
        window.open(res.pr_url, '_blank')
      }
    } catch (err) {
      statusMessage.value = err instanceof Error ? err.message : 'Failed to create PR'
      statusType.value = 'error'
    } finally {
      saving.value = false
    }
  }
}

async function handlePreview() {
  const content = buildMarkdown()
  try {
    const result = await previewSpec(auth.org, props.owner, props.repo, { content })
    previewHtml.value = result.html
    showPreview.value = true
  } catch {
    previewHtml.value = '<p class="text-red-500">Failed to render preview.</p>'
    showPreview.value = true
  }
}

async function handleToggleMode() {
  if (mode.value === 'source') {
    // Switch to structured: parse content
    parseFrontmatterFromSource()
    // If content is empty/whitespace, show empty structured editor without API call
    if (!sourceContent.value.trim()) {
      sections.value = []
      mode.value = 'structured'
      return
    }
    try {
      const result = await parseSpec(auth.org, props.owner, props.repo, { content: sourceContent.value })
      if (result.ok && result.sections) {
        sections.value = result.sections
        mode.value = 'structured'
      }
    } catch {
      statusMessage.value = 'Failed to parse spec'
      statusType.value = 'error'
    }
  } else {
    // Switch to source: rebuild markdown
    sourceContent.value = buildMarkdown()
    mode.value = 'source'
  }
}

function handleConflictUseServer() {
  if (conflict.value) {
    sourceContent.value = conflict.value.serverContent
    sha.value = conflict.value.serverSha
    editorStore.resolveConflict()
    parseFrontmatterFromSource()
  }
}

async function handleConflictOverwrite() {
  if (conflict.value) {
    sha.value = conflict.value.serverSha
    editorStore.resolveConflict()
    // Bypass dialog — user already confirmed via conflict modal
    await handleSaveConfirm({ message: defaultCommitMessage.value })
  }
}

// Parse frontmatter when source content is first loaded
const frontmatterParsed = ref(false)
const initialized = ref(false)
watch(sourceContent, (val) => {
  // Skip marking dirty on the initial load trigger
  if (initialized.value) {
    editorStore.markDirty()
  } else {
    initialized.value = true
  }
  if (!frontmatterParsed.value && val.trim()) {
    frontmatterParsed.value = true
    parseFrontmatterFromSource()
  }
}, { immediate: true })
watch(sections, () => { if (initialized.value) editorStore.markDirty() }, { deep: true })
watch(frontmatter, () => { if (initialized.value) editorStore.markDirty() }, { deep: true })

// AI generation handlers
function handleAiStart() {
  editorStore.reset()
  sourceContent.value = ''
  frontmatterParsed.value = false
  initialized.value = true
}

function handleAiChunk(text: string) {
  sourceContent.value += text
}

function handleAiDone() {
  parseFrontmatterFromSource()
}

// Keyboard shortcuts
useKeyboardShortcuts([
  { key: 's', meta: true, handler: () => openSaveDialog() },
  { key: 's', meta: true, shift: true, handler: () => openPRDialog() },
])
</script>

<template>
  <div>
    <!-- Status toast -->
    <div
      v-if="statusMessage"
      class="fixed top-4 right-4 z-50 px-4 py-2 rounded-md text-sm text-white shadow-lg"
      :class="statusType === 'success' ? 'bg-accent-500' : 'bg-red-500'"
    >
      {{ statusMessage }}
      <button class="ml-2 text-white/80 hover:text-white" @click="statusMessage = ''">x</button>
    </div>

    <EditorToolbar
      :hide-mode-toggle="!!props.configMode"
      :hide-preview="!!props.configMode"
      :github-connected="auth.hasGitHub"
      :pane-layout="layout"
      @save="openSaveDialog"
      @save-pr="openPRDialog"
      @preview="handlePreview"
      @toggle-mode="handleToggleMode"
      @set-layout="setLayout"
    />

    <!-- GitHub connection prompt -->
    <div
      v-if="showGitHubPrompt"
      class="mt-3 flex items-center gap-3 rounded-md border border-amber-300 dark:border-amber-600 bg-amber-50 dark:bg-amber-900/20 px-4 py-2.5 text-sm text-amber-800 dark:text-amber-300"
    >
      <span class="flex-1">Connect your GitHub account to save changes.</span>
      <a
        href="/auth/github/login"
        class="font-medium underline hover:text-amber-900 dark:hover:text-amber-200"
      >Connect GitHub</a>
      <button
        class="text-amber-600 dark:text-amber-400 hover:text-amber-800 dark:hover:text-amber-200"
        @click="showGitHubPrompt = false"
      >
        x
      </button>
    </div>

    <!-- AI Spec Generator for new specs -->
    <AiSpecGenerator
      v-if="isNew && !props.configMode"
      :owner="props.owner"
      :repo="props.repo"
      class="mt-4"
      @start="handleAiStart"
      @chunk="handleAiChunk"
      @done="handleAiDone"
    />

    <!-- New spec file path -->
    <div v-if="isNew && !props.configMode" class="mt-4 mb-2">
      <label class="block text-xs font-medium text-slate-500 mb-1">File Path</label>
      <input
        v-model="newFilePath"
        class="w-full sm:w-96 border border-border-light dark:border-slate-600 bg-surface-light dark:bg-[#0a0f1a] rounded-md px-3 py-1.5 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-accent-500"
        placeholder="docs/specs/my-feature.md"
      />
    </div>

    <FrontmatterForm
      v-if="!props.configMode && mode === 'structured'"
      v-model="frontmatter"
      :is-new="isNew"
      class="mt-4"
    />

    <!-- Source mode: read-only header showing spec identity -->
    <div v-if="!props.configMode && mode === 'source' && frontmatter.title" class="mt-4 mb-2">
      <h1 class="text-xl font-display font-bold text-slate-800 dark:text-slate-100">
        {{ frontmatter.title }}
      </h1>
      <div class="mt-1 flex items-center gap-2 text-xs text-slate-400">
        <span
          class="status-badge text-[11px]"
          :class="{
            'status-draft': frontmatter.status === 'draft',
            'status-todo': frontmatter.status === 'todo',
            'status-in_progress': frontmatter.status === 'in_progress',
            'status-done': frontmatter.status === 'done',
            'status-blocked': frontmatter.status === 'blocked',
            'status-deprecated': frontmatter.status === 'deprecated',
          }"
        >{{ frontmatter.status.replace('_', ' ') }}</span>
        <template v-if="frontmatter.owner">
          <span class="text-slate-300 dark:text-slate-600">|</span>
          <span>{{ frontmatter.owner }}</span>
        </template>
        <template v-if="frontmatter.team">
          <span class="text-slate-300 dark:text-slate-600">|</span>
          <span>{{ frontmatter.team }}</span>
        </template>
        <span v-for="tag in frontmatter.tags" :key="tag" class="px-1.5 py-0 rounded text-[10px] font-medium bg-brand-100 text-brand-700 dark:bg-brand-900/30 dark:text-brand-300">{{ tag }}</span>
      </div>
    </div>

    <!-- Source editor with split-pane live preview -->
    <div v-if="mode === 'source' || props.configMode" class="mt-4">
      <!-- Normal editor (v-show preserves Monaco instance + selection during diff review) -->
      <div v-show="!showDiffReview">
        <!-- Single pane (config mode only) -->
        <div v-if="props.configMode" class="w-full">
          <MonacoEditor ref="monacoRef" v-model="sourceContent" language="yaml" />
        </div>

        <!-- Resizable split pane -->
        <div
          v-else
          ref="splitContainerRef"
          class="flex flex-col lg:flex-row lg:h-[calc(100vh-14rem)] min-h-[400px] relative"
          :class="{ 'select-none': isDragging }"
        >
          <!-- Editor pane -->
          <div
            v-show="layout !== 'preview-only'"
            class="min-w-0 min-h-0 relative"
            :class="layout === 'editor-only' ? 'w-full h-full' : 'h-[400px] lg:h-full'"
            :style="layout === 'split' ? { flex: `0 0 ${splitPercent}%` } : undefined"
          >
            <!-- Maximize/restore button -->
            <button
              class="absolute top-2 right-2 z-10 p-1 rounded bg-surface-light/80 dark:bg-surface-alt/80 border border-border-light dark:border-slate-700 text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 opacity-0 hover:opacity-100 focus:opacity-100 transition-opacity"
              :title="layout === 'editor-only' ? 'Restore split view' : 'Maximize editor'"
              @click="setLayout(layout === 'editor-only' ? 'split' : 'editor-only')"
            >
              <svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
                <path v-if="layout !== 'editor-only'" stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" />
                <path v-else stroke-linecap="round" stroke-linejoin="round" d="M9 9V4.5M9 9H4.5M9 9L3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5l5.25 5.25" />
              </svg>
            </button>
            <MonacoEditor ref="monacoRef" v-model="sourceContent" :language="props.configMode ? 'yaml' : 'markdown'" />
          </div>

          <!-- Drag handle (visible in split mode on lg screens) -->
          <div
            v-if="layout === 'split'"
            class="hidden lg:flex items-center justify-center w-2 cursor-col-resize group hover:bg-accent-100 dark:hover:bg-accent-900/20 transition-colors shrink-0"
            :class="isDragging ? 'bg-accent-200 dark:bg-accent-900/30' : ''"
            @mousedown="splitContainerRef && startDrag($event, splitContainerRef)"
          >
            <div class="w-0.5 h-8 rounded-full bg-gray-300 dark:bg-slate-600 group-hover:bg-accent-400 dark:group-hover:bg-accent-500 transition-colors"></div>
          </div>

          <!-- Preview pane -->
          <div
            v-show="layout !== 'editor-only'"
            class="min-w-0 min-h-0 relative"
            :class="layout === 'preview-only' ? 'w-full h-full' : 'flex-1 h-[400px] lg:h-full'"
          >
            <!-- Maximize/restore button -->
            <button
              class="absolute top-2 left-2 z-10 p-1 rounded bg-surface-light/80 dark:bg-surface-alt/80 border border-border-light dark:border-slate-700 text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 opacity-0 hover:opacity-100 focus:opacity-100 transition-opacity"
              :title="layout === 'preview-only' ? 'Restore split view' : 'Maximize preview'"
              @click="setLayout(layout === 'preview-only' ? 'split' : 'preview-only')"
            >
              <svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
                <path v-if="layout !== 'preview-only'" stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" />
                <path v-else stroke-linecap="round" stroke-linejoin="round" d="M9 9V4.5M9 9H4.5M9 9L3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5l5.25 5.25" />
              </svg>
            </button>
            <MarkdownPreview
              :content="sourceContent"
              mode="fast"
              :owner="props.owner"
              :repo="props.repo"
              hide-header
            />
          </div>
        </div>
      </div>
      <!-- AI diff review (replaces editor in-place) -->
      <AiDiffReview
        v-if="showDiffReview"
        mode="inline"
        :original="diffOriginal"
        :modified="diffModified"
        :loading="sourceModeAiRef?.loading ?? false"
        :error="sourceModeAiRef?.error ?? ''"
        :action="sourceModeAiRef?.currentAction ?? 'improve'"
        style="height: 600px"
        @accept="sourceModeAiRef?.accept()"
        @reject="sourceModeAiRef?.reject()"
      />
      <!-- Source mode AI assist -->
      <SourceModeAi
        v-if="!props.configMode && monacoRef"
        ref="sourceModeAiRef"
        :editor-ref="monacoRef"
        :content="sourceContent"
        :owner="props.owner"
        :repo="props.repo"
        class="mt-3"
        @update-content="sourceContent = $event"
      />
    </div>

    <!-- Structured editor -->
    <div v-if="mode === 'structured' && !props.configMode" class="mt-4">
      <StructuredEditor
        v-model="sections"
        :owner="props.owner"
        :repo="props.repo"
      />
    </div>

    <!-- Preview pane -->
    <div v-if="showPreview" class="mt-4">
      <div class="flex items-center justify-between mb-2">
        <h3 class="text-sm font-medium text-slate-600 dark:text-slate-400">Preview</h3>
        <button class="text-xs text-slate-400 hover:text-slate-600" @click="showPreview = false">Close</button>
      </div>
      <PreviewPane :html="previewHtml" />
    </div>

    <!-- Save dialog -->
    <SaveDialog
      v-if="showSaveDialog"
      :mode="saveDialogMode"
      :file-path="currentPath"
      :default-message="defaultCommitMessage"
      @confirm="handleSaveConfirm"
      @cancel="showSaveDialog = false"
    />

    <!-- Conflict modal -->
    <ConflictModal
      v-if="conflict"
      :conflict="conflict"
      :local-content="buildMarkdown()"
      @use-server="handleConflictUseServer"
      @overwrite="handleConflictOverwrite"
      @cancel="editorStore.resolveConflict()"
    />
  </div>
</template>
