import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { ICommandRecord } from '@/api/terminal'
import { terminalApi } from '@/api/terminal'

export interface IConfirmationRequest {
  confirmation_id: string
  command: string
  reason: string
}

export interface IApprovalRequest {
  approval_id: string
  command: string
  reason: string
  expires_in: number
}

export type TerminalMode = 'local' | 'server'

export const useTerminalStore = defineStore('terminal', () => {
  // --- State ---
  const terminalMode = ref<TerminalMode>('local')
  const sessionId = ref<string | null>(null)
  const cwd = ref<string>('~')
  const output = ref<string[]>([])
  const history = ref<ICommandRecord[]>([])
  const isExecuting = ref(false)
  const isWsConnected = ref(false)
  const ws = ref<WebSocket | null>(null)
  const error = ref<string | null>(null)
  const pendingConfirmation = ref<IConfirmationRequest | null>(null)
  const pendingApproval = ref<IApprovalRequest | null>(null)
  let wsIntentionallyClosed = false
  let wsReconnectTimer: ReturnType<typeof setTimeout> | null = null
  let _pingTimer: ReturnType<typeof setInterval> | null = null

  // --- Getters ---
  const recentCommands = computed(() => {
    return history.value.slice(-50)
  })

  /** True when waiting for admin approval (server mode only). */
  const isWaitingForApproval = computed(() => pendingApproval.value !== null)

  // --- Actions ---
  function setSessionId(id: string): void {
    sessionId.value = id
  }

  function setCwd(dir: string): void {
    cwd.value = dir
  }

  function appendOutput(text: string): void {
    output.value.push(text)
    // Keep output buffer manageable
    if (output.value.length > 5000) {
      output.value = output.value.slice(-3000)
    }
  }

  function clearOutput(): void {
    output.value = []
  }

  function addHistory(record: ICommandRecord): void {
    history.value.push(record)
    if (history.value.length > 1000) {
      history.value = history.value.slice(-500)
    }
  }

  function clearHistory(): void {
    history.value = []
  }

  /**
   * Switch terminal mode (local ↔ server).
   *
   * Disconnects the current WebSocket and reconnects to the appropriate
   * endpoint. In server mode, the JWT is included as a query parameter.
   */
  function switchMode(mode: TerminalMode): void {
    if (terminalMode.value === mode) return
    terminalMode.value = mode
    pendingConfirmation.value = null
    pendingApproval.value = null
    disconnectWebSocket()
    // Clear output when switching modes for a clean slate
    output.value = []
    sessionId.value = null
    cwd.value = mode === 'server' ? '~' : '~'
    connectWebSocket()
  }

  function connectWebSocket(): void {
    if (ws.value && ws.value.readyState === WebSocket.OPEN) {
      return
    }

    wsIntentionallyClosed = false
    const url =
      terminalMode.value === 'server'
        ? terminalApi.createServerTerminalWsUrl(sessionId.value || undefined)
        : terminalApi.createTerminalWsUrl(sessionId.value || undefined)
    const socket = new WebSocket(url)

    socket.onopen = () => {
      isWsConnected.value = true
      const modeLabel = terminalMode.value === 'server' ? '服务端终端' : '本地终端'
      appendOutput(`\x1b[32m已连接到${modeLabel}\x1b[0m`)
    }

    socket.onmessage = (event: MessageEvent) => {
      try {
        const data = JSON.parse(event.data as string)
        handleWsMessage(data)
      } catch {
        // Plain text output
        appendOutput(event.data as string)
      }
    }

    socket.onclose = () => {
      isWsConnected.value = false
      appendOutput('\x1b[33m终端连接已断开\x1b[0m')
      if (_pingTimer) {
        clearInterval(_pingTimer)
        _pingTimer = null
      }
      // Auto reconnect after 3 seconds only if not intentionally closed
      if (!wsIntentionallyClosed) {
        wsReconnectTimer = setTimeout(() => {
          if (!ws.value || ws.value.readyState === WebSocket.CLOSED) {
            connectWebSocket()
          }
        }, 3000)
      }
    }

    socket.onerror = () => {
      isWsConnected.value = false
      appendOutput('\x1b[31m连接错误\x1b[0m')
    }

    ws.value = socket
  }

  function disconnectWebSocket(): void {
    wsIntentionallyClosed = true
    if (_pingTimer) {
      clearInterval(_pingTimer)
      _pingTimer = null
    }
    if (wsReconnectTimer) {
      clearTimeout(wsReconnectTimer)
      wsReconnectTimer = null
    }
    if (ws.value) {
      ws.value.onclose = null // prevent reconnect on intentional close
      ws.value.close()
      ws.value = null
      isWsConnected.value = false
    }
  }

  function sendCommand(command: string): void {
    if (ws.value && ws.value.readyState === WebSocket.OPEN) {
      ws.value.send(JSON.stringify({ type: 'execute', command }))
      appendOutput(`\x1b[36m${cwd.value}$\x1b[0m ${command}`)
      isExecuting.value = true
    }
  }

  function confirmCommand(confirmationId: string, approved: boolean, addToWhitelist: boolean = false): void {
    if (ws.value && ws.value.readyState === WebSocket.OPEN) {
      ws.value.send(JSON.stringify({
        type: 'confirm',
        confirmation_id: confirmationId,
        approved,
        add_to_whitelist: addToWhitelist,
        command: pendingConfirmation.value?.command || '',
      }))
      pendingConfirmation.value = null
    }
  }

  /**
   * Cancel a pending approval (server mode only).
   *
   * Notifies the server that the user no longer wants to wait for
   * admin approval.
   */
  function cancelApproval(): void {
    if (pendingApproval.value && ws.value && ws.value.readyState === WebSocket.OPEN) {
      ws.value.send(JSON.stringify({
        type: 'cancel_approval',
        approval_id: pendingApproval.value.approval_id,
      }))
      pendingApproval.value = null
    }
  }

  function sendInput(data: string): void {
    if (ws.value && ws.value.readyState === WebSocket.OPEN) {
      ws.value.send(JSON.stringify({ type: 'input', data }))
    }
  }

  function handleWsMessage(data: Record<string, unknown>): void {
    const msgType = data.type as string

    switch (msgType) {
      case 'connected':
        if (data.session_id) {
          sessionId.value = data.session_id as string
        }
        break
      case 'output':
        if (data.data) {
          appendOutput(data.data as string)
        }
        break
      case 'result':
        if (data.output) {
          appendOutput(data.output as string)
        }
        if (data.cwd) {
          cwd.value = data.cwd as string
        }
        if (data.exit_code !== undefined) {
          addHistory({
            command: (data.command as string) || '',
            exit_code: data.exit_code as number,
            output: (data.output as string) || '',
            cwd: cwd.value,
            timestamp: Date.now() / 1000,
            duration_ms: (data.duration_ms as number) || 0,
          })
        }
        isExecuting.value = false
        break
      // ── Local terminal: user confirmation ──────────────────────
      case 'confirmation_required':
        pendingConfirmation.value = {
          confirmation_id: data.confirmation_id as string,
          command: data.command as string,
          reason: (data.reason as string) || '需要用户确认',
        }
        appendOutput(`\x1b[33m[WARN] 需要确认: ${data.command}\x1b[0m`)
        appendOutput(`\x1b[33m  原因: ${data.reason}\x1b[0m`)
        break
      // ── Server terminal: admin approval ────────────────────────
      case 'approval_required':
        pendingApproval.value = {
          approval_id: data.approval_id as string,
          command: data.command as string,
          reason: (data.reason as string) || '需要管理员审批',
          expires_in: (data.expires_in as number) || 300,
        }
        appendOutput(`\x1b[33m[PENDING] 等待管理员审批: ${data.command}\x1b[0m`)
        appendOutput(`\x1b[33m  原因: ${data.reason}\x1b[0m`)
        break
      case 'approval_approved':
        appendOutput(`\x1b[32m[OK] 审批已通过，正在执行...\x1b[0m`)
        pendingApproval.value = null
        break
      case 'approval_rejected':
        appendOutput(`\x1b[31m[REJECTED] 审批被拒绝: ${data.reason || ''}\x1b[0m`)
        pendingApproval.value = null
        isExecuting.value = false
        break
      case 'approval_expired':
        appendOutput(`\x1b[31m[TIMEOUT] 审批超时未响应\x1b[0m`)
        pendingApproval.value = null
        isExecuting.value = false
        break
      case 'error':
        appendOutput(`\x1b[31m错误: ${data.message}\x1b[0m`)
        isExecuting.value = false
        break
    }
  }

  return {
    // State
    terminalMode,
    sessionId,
    cwd,
    output,
    history,
    isExecuting,
    isWsConnected,
    ws,
    error,
    pendingConfirmation,
    pendingApproval,
    // Getters
    recentCommands,
    isWaitingForApproval,
    // Actions
    setSessionId,
    setCwd,
    appendOutput,
    clearOutput,
    addHistory,
    clearHistory,
    switchMode,
    connectWebSocket,
    disconnectWebSocket,
    sendCommand,
    confirmCommand,
    cancelApproval,
    sendInput,
  }
})
