import * as React from 'react'
import { randomUUID } from 'crypto'
import { getTotalCost as getTotalCostUSD } from '../../cost-tracker.js'
import { getTotalTokensUsed } from '../../bootstrap/state.js'
import { Box, Text, useInput } from '../../ink.js'
import type { Goal, GoalStatus } from '../../state/AppStateStore.js'
import type { LocalJSXCommandContext, LocalJSXCommandOnDone } from '../../types/command.js'
import {
  buildObjectiveUpdatedPrompt,
  formatElapsed,
  formatGoalStatus,
  isGoalContinuationPrompt,
} from '../../utils/goal.js'
import { clearGoal, saveGoal } from '../../utils/sessionStorage.js'
import { enqueue, removeByFilter } from '../../utils/messageQueueManager.js'
import { renderToString } from '../../utils/staticRender.js'

function statusColor(status: GoalStatus): string {
  switch (status) {
    case 'pursuing':
      return 'green'
    case 'paused':
      return 'yellow'
    case 'achieved':
      return 'cyan'
    case 'blocked':
      return 'red'
    case 'usage-limited':
      return 'gray'
    case 'budget-limited':
      return 'magenta'
  }
}

function GoalDisplay({
  goal,
  now,
}: {
  goal: Goal
  now: number
}): React.ReactNode {
  const elapsed = formatElapsed(now - goal.startedAt)

  return (
    <Box flexDirection="column">
      <Text bold>Goal</Text>
      <Text>{goal.objective}</Text>
      <Box marginTop={1}>
        <Text>Status: </Text>
        <Text color={statusColor(goal.status)}>
          {formatGoalStatus(goal.status)}
        </Text>
      </Box>
      <Text dimColor>
        Elapsed: {elapsed} · Continuations:{' '}
        {goal.continuationCount}
      </Text>
      {goal.lastReason ? (
        <Text dimColor>Last update: {goal.lastReason}</Text>
      ) : null}
    </Box>
  )
}

function GoalOverwriteConfirm({
  existing,
  objective,
  context,
  onDone,
  inPlanMode,
}: {
  existing: Goal
  objective: string
  context: LocalJSXCommandContext
  onDone: LocalJSXCommandOnDone
  inPlanMode: boolean
}): React.ReactNode {
  const [choice, setChoice] = React.useState<'replace' | 'cancel' | null>(null)

  useInput((input, key) => {
    if (choice !== null) return
    if (input === 'y' || input === 'Y' || key.return) {
      setChoice('replace')
    } else if (input === 'n' || input === 'N' || key.escape) {
      setChoice('cancel')
    }
  })

  React.useEffect(() => {
    if (choice === null) return
    const { setAppState } = context

    if (choice === 'cancel') {
      onDone('Goal not changed.')
      return
    }

    const now = Date.now()
    const newGoal: Goal = {
      id: randomUUID(),
      objective,
      status: 'pursuing',
      startedAt: now,
      startCostUSD: getTotalCostUSD(),
      startTokensUsed: getTotalTokensUsed(),
      continuationCount: 0,
      lastUpdatedAt: now,
    }
    setAppState((prev: AppState) => ({ ...prev, goal: newGoal } satisfies AppState))
    clearQueuedGoalContinuations()

    const message = inPlanMode
      ? `Goal set: ${objective}\nAuto-continuation is disabled while in Plan mode. Exit plan mode (Shift+Tab) to begin pursuit.`
      : `Goal set: ${objective}\nThe agent will auto-continue toward this objective until it is achieved, blocked, or paused. Use /goal pause, /goal resume, /goal edit, or /goal clear to manage it.`
    onDone(message, {
      metaMessages: [
        `[goal] Active goal id: ${newGoal.id}. If calling update_goal for this goal, include goal_id='${newGoal.id}'.`,
      ],
    })
  }, [choice, objective, context, onDone, inPlanMode])

  return (
    <Box flexDirection="column">
      <Text bold>Replace existing goal?</Text>
      <Box marginTop={1}>
        <Text dimColor>
          Current goal ({formatGoalStatus(existing.status)}):
        </Text>
      </Box>
      <Text>{existing.objective}</Text>
      <Box marginTop={1}>
        <Text dimColor>New objective:</Text>
      </Box>
      <Text>{objective}</Text>
      <Box marginTop={1}>
        <Text dimColor>
          Press <Text bold>Enter</Text> to replace, <Text bold>Esc</Text> to
          cancel.
        </Text>
      </Box>
    </Box>
  )
}

type AppState = ReturnType<typeof context.getAppState>

function setGoal(
  setAppState: LocalJSXCommandContext['setAppState'],
  updater: (prev: Goal | undefined) => Goal | undefined,
): void {
  setAppState((prev: AppState) => {
    const newGoal = updater(prev.goal)
    if (newGoal) {
      saveGoal({ type: 'goal', ...newGoal })
    } else {
      clearGoal()
    }
    return { ...prev, goal: newGoal } satisfies AppState
  })
}

function clearQueuedGoalContinuations(): void {
  removeByFilter(cmd => isGoalContinuationPrompt(cmd.value))
}

export async function call(
  onDone: LocalJSXCommandOnDone,
  context: LocalJSXCommandContext,
  args: string,
): Promise<React.ReactNode> {
  const { getAppState, setAppState } = context
  const trimmed = args.trim()
  const firstTokenMatch = trimmed.match(/^\S+/)
  const firstToken = firstTokenMatch?.[0] ?? ''
  const sub = firstToken.toLowerCase()
  const rest = firstTokenMatch
    ? trimmed.slice(firstTokenMatch[0].length).trimStart()
    : ''

  const appState = getAppState()
  const existing = appState.goal
  const inPlanMode = appState.toolPermissionContext.mode === 'plan'

  if (sub === 'pause') {
    if (!existing) {
      onDone('No active goal.')
      return null
    }
    if (existing.status !== 'pursuing') {
      onDone(`Goal is already ${formatGoalStatus(existing.status)}.`)
      return null
    }
    setGoal(setAppState, g =>
      g ? { ...g, status: 'paused', lastUpdatedAt: Date.now() } : g,
    )
    clearQueuedGoalContinuations()
    onDone('Goal paused. Auto-continuation suspended.')
    return null
  }

  if (sub === 'resume') {
    if (!existing) {
      onDone('No active goal.')
      return null
    }
    if (existing.status === 'pursuing') {
      onDone('Goal already pursuing.')
      return null
    }
    if (existing.status === 'achieved') {
      onDone(
        `Goal already ${formatGoalStatus(existing.status)}. Use /goal <objective> to start a new one.`,
      )
      return null
    }
    setGoal(setAppState, g =>
      g
        ? {
            ...g,
            status: 'pursuing',
            continuationCount: 0,
            startedAt: Date.now(),
            startCostUSD: getTotalCostUSD(),
            startTokensUsed: getTotalTokensUsed(),
            lastUpdatedAt: Date.now(),
            lastReason: undefined,
          }
        : g,
    )
    clearQueuedGoalContinuations()
    onDone(
      'Goal resumed. Continuation count and budget window reset; auto-continuation will start on the next idle tick.',
    )
    return null
  }

  if (sub === 'clear') {
    if (!existing) {
      onDone('No active goal.')
      return null
    }
    setGoal(setAppState, () => undefined)
    clearQueuedGoalContinuations()
    onDone('Goal cleared.')
    return null
  }

  if (sub === 'edit') {
    if (!existing) {
      onDone('No active goal. Set one with: /goal <objective>')
      return null
    }
    if (!rest) {
      onDone('Usage: /goal edit <new objective>')
      return null
    }
    setGoal(setAppState, g =>
      g
        ? {
            ...g,
            objective: rest,
            lastUpdatedAt: Date.now(),
          }
        : g,
    )
    clearQueuedGoalContinuations()
    onDone(`Goal objective updated to: ${rest}`, {
      metaMessages: [
        buildObjectiveUpdatedPrompt({ ...existing, objective: rest }),
      ],
    })
    return null
  }

  if (trimmed === '') {
    if (!existing) {
      onDone(
        'No active goal. Set one with: /goal <objective>\nThen the agent will auto-continue toward it across turns.',
      )
      return null
    }
    const display = (
      <GoalDisplay
        goal={existing as Goal}
        now={Date.now()}
      />
    )
    const output = await renderToString(display)
    onDone(output)
    return null
  }

  const objective = (sub === 'set' ? rest : trimmed).trim()
  if (!objective) {
    onDone('Missing objective.\nUsage: /goal <objective>')
    return null
  }

  if (existing && (existing.status === 'pursuing' || existing.status === 'paused')) {
    return (
      <GoalOverwriteConfirm
        existing={existing}
        objective={objective}
        context={context}
        onDone={onDone}
        inPlanMode={inPlanMode}
      />
    )
  }

  const now = Date.now()
  const newGoal: Goal = {
    id: randomUUID(),
    objective,
    status: 'pursuing',
    startedAt: now,
    startCostUSD: getTotalCostUSD(),
    startTokensUsed: getTotalTokensUsed(),
    continuationCount: 0,
    lastUpdatedAt: now,
  }
  setGoal(setAppState, () => newGoal)
  clearQueuedGoalContinuations()

  const message = inPlanMode
    ? `Goal set: ${objective}\nAuto-continuation is disabled while in Plan mode. Exit plan mode (Shift+Tab) to begin pursuit.`
    : `Goal set: ${objective}\nThe agent will auto-continue toward this objective until it is achieved, blocked, or paused. Use /goal pause, /goal resume, /goal edit, or /goal clear to manage it.`
  onDone(message, {
    metaMessages: [
      `[goal] Active goal id: ${newGoal.id}. If calling update_goal for this goal, include goal_id='${newGoal.id}'.`,
    ],
  })
  return null
}