import { execa } from 'execa'
import * as React from 'react'
import { useEffect, useState } from 'react'
import { Select } from '../../components/CustomSelect/index.js'
import { Dialog } from '../../components/design-system/Dialog.js'
import { LoadingState } from '../../components/design-system/LoadingState.js'
import { Box, Text } from '../../ink.js'
import {
  logEvent,
  type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS as SafeString,
} from '../../services/analytics/index.js'
import type { LocalJSXCommandOnDone } from '../../types/command.js'
import { openBrowser } from '../../utils/browser.js'
import { getGhAuthStatus } from '../../utils/github/ghAuthStatus.js'
import {
  createDefaultEnvironment,
  getCodeWebUrl,
  type ImportTokenError,
  importGithubToken,
  isSignedIn,
  RedactedGithubToken,
} from './api.js'

type CheckResult =
  | { status: 'not_signed_in' }
  | { status: 'has_gh_token'; token: RedactedGithubToken }
  | { status: 'gh_not_installed' }
  | { status: 'gh_not_authenticated' }

async function checkLoginState(): Promise<CheckResult> {
  if (!(await isSignedIn())) {
    return { status: 'not_signed_in' }
  }

  const ghStatus = await getGhAuthStatus()
  if (ghStatus === 'not_installed') {
    return { status: 'gh_not_installed' }
  }
  if (ghStatus === 'not_authenticated') {
    return { status: 'gh_not_authenticated' }
  }

  // ghStatus === 'authenticated'. getGhAuthStatus spawns with stdout:'ignore'
  // (telemetry-safe); spawn once more with stdout:'pipe' to read the token.
  const { stdout } = await execa('gh', ['auth', 'token'], {
    stdout: 'pipe',
    stderr: 'ignore',
    timeout: 5000,
    reject: false,
  })
  const trimmed = stdout.trim()
  if (!trimmed) {
    return { status: 'gh_not_authenticated' }
  }
  return { status: 'has_gh_token', token: new RedactedGithubToken(trimmed) }
}

function errorMessage(err: ImportTokenError, codeUrl: string): string {
  switch (err.kind) {
    case 'not_signed_in':
      return `Login failed. Please visit ${codeUrl} and login using the GitHub App`
    case 'invalid_token':
      return 'GitHub rejected that token. Run `gh auth login` and try again.'
    case 'server':
      return `Server error (${err.status}). Try again in a moment.`
    case 'network':
      return "Couldn't reach the server. Check your connection."
  }
}

type Step =
  | { name: 'checking' }
  | { name: 'confirm'; token: RedactedGithubToken }
  | { name: 'uploading' }

function Web({ onDone }: { onDone: LocalJSXCommandOnDone }) {
  const [step, setStep] = useState<Step>({ name: 'checking' })

  useEffect(() => {
    logEvent('tengu_remote_setup_started', {})
    void checkLoginState().then(async result => {
      switch (result.status) {
        case 'not_signed_in':
          logEvent('tengu_remote_setup_result', {
            result: 'not_signed_in' as SafeString,
          })
          onDone('Not signed in to Claude. Run /login first.')
          return
        case 'gh_not_installed':
        case 'gh_not_authenticated': {
          const url = `${getCodeWebUrl()}/onboarding?step=alt-auth`
          await openBrowser(url)
          logEvent('tengu_remote_setup_result', {
            result: result.status as SafeString,
          })
          onDone(
            result.status === 'gh_not_installed'
              ? `GitHub CLI not found. Install it via https://cli.github.com/, then run \`gh auth login\`, or connect GitHub on the web: ${url}`
              : `GitHub CLI not authenticated. Run \`gh auth login\` and try again, or connect GitHub on the web: ${url}`,
          )
          return
        }
        case 'has_gh_token':
          setStep({ name: 'confirm', token: result.token })
      }
    })
    // onDone is stable across renders; intentionally not in deps.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  const handleCancel = () => {
    logEvent('tengu_remote_setup_result', {
      result: 'cancelled' as SafeString,
    })
    onDone()
  }

  const handleConfirm = async (token: RedactedGithubToken) => {
    setStep({ name: 'uploading' })

    const result = await importGithubToken(token)
    if (!result.ok) {
      logEvent('tengu_remote_setup_result', {
        result: 'import_failed' as SafeString,
        error_kind: result.error.kind as SafeString,
      })
      onDone(errorMessage(result.error, getCodeWebUrl()))
      return
    }

    // Token import succeeded. Environment creation is best-effort — if it
    // fails, the web state machine routes to env-setup on landing, which is
    // one extra click but still better than the OAuth dance.
    await createDefaultEnvironment()

    const url = getCodeWebUrl()
    await openBrowser(url)

    logEvent('tengu_remote_setup_result', {
      result: 'success' as SafeString,
    })
    onDone(`Connected as ${result.result.github_username}. Opened ${url}`)
  }

  if (step.name === 'checking') {
    return <LoadingState message="Checking login status…" />
  }

  if (step.name === 'uploading') {
    return <LoadingState message="Connecting GitHub to Claude…" />
  }

  const token = step.token
  return (
    <Dialog
      title="Connect Claude on the web to GitHub?"
      onCancel={handleCancel}
      hideInputGuide
    >
      <Box flexDirection="column">
        <Text>
          Claude on the web requires connecting to your GitHub account to clone
          and push code on your behalf.
        </Text>
        <Text dimColor>
          Your local credentials are used to authenticate with GitHub
        </Text>
      </Box>
      <Select
        options={[
          { label: 'Continue', value: 'send' },
          { label: 'Cancel', value: 'cancel' },
        ]}
        onChange={value => {
          if (value === 'send') {
            void handleConfirm(token)
          } else {
            handleCancel()
          }
        }}
        onCancel={handleCancel}
      />
    </Dialog>
  )
}

export async function call(
  onDone: LocalJSXCommandOnDone,
): Promise<React.ReactNode> {
  return <Web onDone={onDone} />
}
