import React, { useEffect, useState } from 'react'
import type { CommandResultDisplay } from 'src/commands.js'
import { logEvent } from 'src/services/analytics/index.js'
import { logForDebugging } from 'src/utils/debug.js'
import { Box, Text, Dialog } from '@anthropic/ink'
import { execFileNoThrow } from '../utils/execFileNoThrow.js'
import { getPlansDirectory } from '../utils/plans.js'
import { setCwd } from '../utils/Shell.js'
import {
  cleanupWorktree,
  getCurrentWorktreeSession,
  keepWorktree,
  killTmuxSession,
} from '../utils/worktree.js'
import { Select } from './CustomSelect/select.js'
import { Spinner } from './Spinner.js'

// Inline require breaks the cycle this file would otherwise close:
// sessionStorage → commands → exit → ExitFlow → here. All call sites
// are inside callbacks, so the lazy require never sees an undefined import.
function recordWorktreeExit(): void {
  /* eslint-disable @typescript-eslint/no-require-imports */
  ;(
    require('../utils/sessionStorage.js') as typeof import('../utils/sessionStorage.js')
  ).saveWorktreeState(null)
  /* eslint-enable @typescript-eslint/no-require-imports */
}

type Props = {
  onDone: (
    result?: string,
    options?: { display?: CommandResultDisplay },
  ) => void
  onCancel?: () => void
}

export function WorktreeExitDialog({
  onDone,
  onCancel,
}: Props): React.ReactNode {
  const [status, setStatus] = useState<
    'loading' | 'asking' | 'keeping' | 'removing' | 'done'
  >('loading')
  const [changes, setChanges] = useState<string[]>([])
  const [commitCount, setCommitCount] = useState<number>(0)
  const [resultMessage, setResultMessage] = useState<string | undefined>()
  const worktreeSession = getCurrentWorktreeSession()

  useEffect(() => {
    async function loadChanges() {
      let changeLines: string[] = []
      const gitStatus = await execFileNoThrow('git', ['status', '--porcelain'])
      if (gitStatus.stdout) {
        changeLines = gitStatus.stdout.split('\n').filter(_ => _.trim() !== '')
        setChanges(changeLines)
      }

      // Check for commits to eject
      if (worktreeSession) {
        // Get commits in worktree that are not in original branch
        const { stdout: commitsStr } = await execFileNoThrow('git', [
          'rev-list',
          '--count',
          `${worktreeSession.originalHeadCommit}..HEAD`,
        ])
        const count = parseInt(commitsStr.trim()) || 0
        setCommitCount(count)

        // If no changes and no commits, clean up silently
        if (changeLines.length === 0 && count === 0) {
          setStatus('removing')
          void cleanupWorktree()
            .then(() => {
              process.chdir(worktreeSession.originalCwd)
              setCwd(worktreeSession.originalCwd)
              recordWorktreeExit()
              getPlansDirectory.cache.clear?.()
              setResultMessage('Worktree removed (no changes)')
            })
            .catch(error => {
              logForDebugging(`Failed to clean up worktree: ${error}`, {
                level: 'error',
              })
              setResultMessage('Worktree cleanup failed, exiting anyway')
            })
            .then(() => {
              setStatus('done')
            })
          return
        } else {
          setStatus('asking')
        }
      }
    }
    void loadChanges()
    // eslint-disable-next-line react-hooks/exhaustive-deps
    // biome-ignore lint/correctness/useExhaustiveDependencies: intentional
  }, [worktreeSession])

  useEffect(() => {
    if (status === 'done') {
      onDone(resultMessage)
    }
  }, [status, onDone, resultMessage])

  if (!worktreeSession) {
    onDone('No active worktree session found', { display: 'system' })
    return null
  }

  if (status === 'loading' || status === 'done') {
    return null
  }

  async function handleSelect(value: string) {
    if (!worktreeSession) return

    const hasTmux = Boolean(worktreeSession.tmuxSessionName)

    if (value === 'keep' || value === 'keep-with-tmux') {
      setStatus('keeping')
      logEvent('tengu_worktree_kept', {
        commits: commitCount,
        changed_files: changes.length,
      })
      await keepWorktree()
      process.chdir(worktreeSession.originalCwd)
      setCwd(worktreeSession.originalCwd)
      recordWorktreeExit()
      getPlansDirectory.cache.clear?.()
      if (hasTmux) {
        setResultMessage(
          `Worktree kept. Your work is saved at ${worktreeSession.worktreePath} on branch ${worktreeSession.worktreeBranch}. Reattach to tmux session with: tmux attach -t ${worktreeSession.tmuxSessionName}`,
        )
      } else {
        setResultMessage(
          `Worktree kept. Your work is saved at ${worktreeSession.worktreePath} on branch ${worktreeSession.worktreeBranch}`,
        )
      }
      setStatus('done')
    } else if (value === 'keep-kill-tmux') {
      setStatus('keeping')
      logEvent('tengu_worktree_kept', {
        commits: commitCount,
        changed_files: changes.length,
      })
      if (worktreeSession.tmuxSessionName) {
        await killTmuxSession(worktreeSession.tmuxSessionName)
      }
      await keepWorktree()
      process.chdir(worktreeSession.originalCwd)
      setCwd(worktreeSession.originalCwd)
      recordWorktreeExit()
      getPlansDirectory.cache.clear?.()
      setResultMessage(
        `Worktree kept at ${worktreeSession.worktreePath} on branch ${worktreeSession.worktreeBranch}. Tmux session terminated.`,
      )
      setStatus('done')
    } else if (value === 'remove' || value === 'remove-with-tmux') {
      setStatus('removing')
      logEvent('tengu_worktree_removed', {
        commits: commitCount,
        changed_files: changes.length,
      })
      if (worktreeSession.tmuxSessionName) {
        await killTmuxSession(worktreeSession.tmuxSessionName)
      }
      try {
        await cleanupWorktree()
        process.chdir(worktreeSession.originalCwd)
        setCwd(worktreeSession.originalCwd)
        recordWorktreeExit()
        getPlansDirectory.cache.clear?.()
      } catch (error) {
        logForDebugging(`Failed to clean up worktree: ${error}`, {
          level: 'error',
        })
        setResultMessage('Worktree cleanup failed, exiting anyway')
        setStatus('done')
        return
      }
      const tmuxNote = hasTmux ? ' Tmux session terminated.' : ''
      if (commitCount > 0 && changes.length > 0) {
        setResultMessage(
          `Worktree removed. ${commitCount} ${commitCount === 1 ? 'commit' : 'commits'} and uncommitted changes were discarded.${tmuxNote}`,
        )
      } else if (commitCount > 0) {
        setResultMessage(
          `Worktree removed. ${commitCount} ${commitCount === 1 ? 'commit' : 'commits'} on ${worktreeSession.worktreeBranch} ${commitCount === 1 ? 'was' : 'were'} discarded.${tmuxNote}`,
        )
      } else if (changes.length > 0) {
        setResultMessage(
          `Worktree removed. Uncommitted changes were discarded.${tmuxNote}`,
        )
      } else {
        setResultMessage(`Worktree removed.${tmuxNote}`)
      }
      setStatus('done')
    }
  }

  if (status === 'keeping') {
    return (
      <Box flexDirection="row" marginY={1}>
        <Spinner />
        <Text>Keeping worktree…</Text>
      </Box>
    )
  }

  if (status === 'removing') {
    return (
      <Box flexDirection="row" marginY={1}>
        <Spinner />
        <Text>Removing worktree…</Text>
      </Box>
    )
  }

  const branchName = worktreeSession.worktreeBranch
  const hasUncommitted = changes.length > 0
  const hasCommits = commitCount > 0

  let subtitle = ''
  if (hasUncommitted && hasCommits) {
    subtitle = `You have ${changes.length} uncommitted ${changes.length === 1 ? 'file' : 'files'} and ${commitCount} ${commitCount === 1 ? 'commit' : 'commits'} on ${branchName}. All will be lost if you remove.`
  } else if (hasUncommitted) {
    subtitle = `You have ${changes.length} uncommitted ${changes.length === 1 ? 'file' : 'files'}. These will be lost if you remove the worktree.`
  } else if (hasCommits) {
    subtitle = `You have ${commitCount} ${commitCount === 1 ? 'commit' : 'commits'} on ${branchName}. The branch will be deleted if you remove the worktree.`
  } else {
    subtitle =
      'You are working in a worktree. Keep it to continue working there, or remove it to clean up.'
  }

  function handleCancel() {
    if (onCancel) {
      // Abort exit and return to the session
      onCancel()
      return
    }
    // Fallback: treat Escape as "keep" if no onCancel provided
    void handleSelect('keep')
  }

  const removeDescription =
    hasUncommitted || hasCommits
      ? 'All changes and commits will be lost.'
      : 'Clean up the worktree directory.'

  const hasTmuxSession = Boolean(worktreeSession.tmuxSessionName)

  const options = hasTmuxSession
    ? [
        {
          label: 'Keep worktree and tmux session',
          value: 'keep-with-tmux',
          description: `Stays at ${worktreeSession.worktreePath}. Reattach with: tmux attach -t ${worktreeSession.tmuxSessionName}`,
        },
        {
          label: 'Keep worktree, kill tmux session',
          value: 'keep-kill-tmux',
          description: `Keeps worktree at ${worktreeSession.worktreePath}, terminates tmux session.`,
        },
        {
          label: 'Remove worktree and tmux session',
          value: 'remove-with-tmux',
          description: removeDescription,
        },
      ]
    : [
        {
          label: 'Keep worktree',
          value: 'keep',
          description: `Stays at ${worktreeSession.worktreePath}`,
        },
        {
          label: 'Remove worktree',
          value: 'remove',
          description: removeDescription,
        },
      ]

  const defaultValue = hasTmuxSession ? 'keep-with-tmux' : 'keep'

  return (
    <Dialog
      title="Exiting worktree session"
      subtitle={subtitle}
      onCancel={handleCancel}
    >
      <Select
        defaultFocusValue={defaultValue}
        options={options}
        onChange={handleSelect}
      />
    </Dialog>
  )
}
