import { useCallback, useEffect, useRef, useState } from 'react'
import { CheckCircle, Loader2, XCircle, RefreshCw, ChevronRight } from 'lucide-react'
import { Button, Card } from '../common'
import { endpoints, getErrorMessage } from '../../api/client'
import { t, tf, isRtl } from '../../lib/i18n'
import type { SprintArticle, SprintResult, SprintStatusResponse, SprintJobStatus } from '../../types'
import type { GenerationConfig } from './SelectPostTypeStep'

const SPRINT_JOB_STORAGE_KEY = 'bc:active-sprint-job'
const POLL_INTERVAL_MS = 5000
const MAX_POLLS = 120

interface SprintStepProps {
  config: GenerationConfig
  intentState: Record<string, unknown>
  technicalState: Record<string, unknown>
  existingSprintJobId?: string | null
  onComplete: (result: SprintResult, articles: SprintArticle[]) => void
  onBack: () => void
  onError: (error: string) => void
  onSprintJobStarted?: (jobId: string) => void
}

type SprintPhase = 'submitting' | 'queued' | 'researching' | 'forming_articles' | 'complete' | 'error'

function formatElapsed(seconds: number): string {
  const mins = Math.floor(seconds / 60)
  const secs = seconds % 60
  if (mins === 0) return tf('%d seconds', secs)
  return tf('%d:%02d minutes', mins, String(secs).padStart(2, '0'))
}

function buildSprintRequest(
  config: GenerationConfig,
  intentState: Record<string, unknown>,
  technicalState: Record<string, unknown>,
): Record<string, unknown> {
  const contentPlan = intentState.content_plan || {}
  const capturedAnswers = Array.isArray(intentState.captured_answers) ? intentState.captured_answers : []

  return {
    post_type: config.postType,
    taxonomy: config.taxonomy || undefined,
    taxonomy_term: config.term || undefined,
    reporter_id: config.reporterId || undefined,
    topic: config.topic,
    keywords: config.keywords,
    length: config.length,
    count: config.count,
    generation_type: config.generationType,
    technical_summary: String(technicalState.technical_summary || ''),
    technical_answers: Array.isArray(technicalState.captured_answers) ? technicalState.captured_answers : [],
    content_plan: contentPlan,
    content_answers: capturedAnswers,
    sprint_mode: true,
  }
}

function getStatusMessage(phase: SprintPhase, roundsUsed: number, roundsMax: number, findingsCount: number): string {
  switch (phase) {
    case 'submitting':
      return t('Submitting research sprint...')
    case 'queued':
      return t('Waiting to start...')
    case 'researching':
      if (roundsUsed > 0) {
        return tf('Research round %d of %d — found %d findings so far', roundsUsed, roundsMax, findingsCount)
      }
      return t('Reporter is researching your field...')
    case 'forming_articles':
      return t('Analyzing findings and forming article candidates...')
    case 'complete':
      return t('Research complete!')
    case 'error':
      return t('Research encountered an error')
    default:
      return ''
  }
}

export function SprintStep({
  config,
  intentState,
  technicalState,
  existingSprintJobId,
  onComplete,
  onBack,
  onError,
  onSprintJobStarted,
}: SprintStepProps) {
  const [phase, setPhase] = useState<SprintPhase>('submitting')
  const [errorMsg, setErrorMsg] = useState('')
  const [elapsedSeconds, setElapsedSeconds] = useState(0)
  const [, setSprintJobId] = useState<string | null>(existingSprintJobId || null)
  const [roundsUsed, setRoundsUsed] = useState(0)
  const [roundsMax, setRoundsMax] = useState(0)
  const [findingsCount, setFindingsCount] = useState(0)
  const abortRef = useRef(false)
  const hasStartedRef = useRef(false)

  useEffect(() => {
    const isActive = phase === 'submitting' || phase === 'queued' || phase === 'researching' || phase === 'forming_articles'
    if (!isActive) return
    const timer = setInterval(() => {
      setElapsedSeconds((prev) => prev + 1)
    }, 1000)
    return () => clearInterval(timer)
  }, [phase])

  const pollSprintStatus = useCallback(async (jobId: string): Promise<void> => {
    for (let attempt = 0; attempt < MAX_POLLS; attempt++) {
      if (abortRef.current) return

      await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS))
      if (abortRef.current) return

      try {
        const res = await endpoints.getSprintStatus(jobId)
        const status = res.data as SprintStatusResponse

        setRoundsUsed(status.rounds_used || 0)
        setRoundsMax(status.rounds_max || 0)
        setFindingsCount(status.findings_count || 0)

        const statusToPhase: Record<SprintJobStatus, SprintPhase> = {
          queued: 'queued',
          running: 'researching',
          forming_articles: 'forming_articles',
          complete: 'complete',
          failed: 'error',
        }
        const nextPhase = statusToPhase[status.status] || 'researching'
        setPhase(nextPhase)

        if (status.status === 'complete') {
          sessionStorage.removeItem(SPRINT_JOB_STORAGE_KEY)

          const sprintResult: SprintResult = {
            runId: status.sprint_job_id,
            rounds: status.rounds_used || 0,
            findings: status.findings_count || 0,
            costUsd: status.cost_usd || 0,
            articleCount: status.articles_formed || 0,
            jobs: status.jobs || [],
          }

          const articles: SprintArticle[] = Array.isArray(status.articles)
            ? status.articles.map((a) => ({
                angle: a.angle || '',
                hook: a.hook || '',
                confidence: a.confidence || 'medium',
                backingFindings: Array.isArray(a.backingFindings) ? a.backingFindings : [],
                branchName: a.branchName,
                branchId: a.branchId,
              }))
            : []

          onComplete(sprintResult, articles)
          return
        }

        if (status.status === 'failed') {
          sessionStorage.removeItem(SPRINT_JOB_STORAGE_KEY)
          const msg = status.error_message || t('Sprint failed')
          setErrorMsg(msg)
          onError(msg)
          return
        }
      } catch (err) {
        // Transient poll failure — continue polling
        console.warn('Sprint poll error:', err)
      }
    }

    sessionStorage.removeItem(SPRINT_JOB_STORAGE_KEY)
    const msg = t('Sprint timed out')
    setPhase('error')
    setErrorMsg(msg)
    onError(msg)
  }, [onComplete, onError])

  const runSprint = useCallback(async () => {
    if (abortRef.current) return

    try {
      setPhase('submitting')
      setErrorMsg('')
      setElapsedSeconds(0)
      setRoundsUsed(0)
      setRoundsMax(0)
      setFindingsCount(0)

      const sprintConfig = buildSprintRequest(config, intentState, technicalState)
      const result = await endpoints.sprintGenerate(sprintConfig)
      const data = result.data as {
        sprint_job_id?: string
        status?: string
        error?: string
      }

      if (abortRef.current) return

      if (!data?.sprint_job_id) {
        throw new Error(data?.error || t('Sprint submission failed'))
      }

      const jobId = data.sprint_job_id
      setSprintJobId(jobId)
      setPhase('queued')

      sessionStorage.setItem(SPRINT_JOB_STORAGE_KEY, jobId)
      onSprintJobStarted?.(jobId)

      await pollSprintStatus(jobId)
    } catch (err) {
      if (abortRef.current) return

      const msg = getErrorMessage(err, String(err))
      setPhase('error')
      setErrorMsg(msg)
      onError(msg)
    }
  }, [config, intentState, technicalState, onError, onSprintJobStarted, pollSprintStatus])

  useEffect(() => {
    if (hasStartedRef.current) return
    hasStartedRef.current = true
    abortRef.current = false

    if (existingSprintJobId) {
      setSprintJobId(existingSprintJobId)
      setPhase('researching')
      void pollSprintStatus(existingSprintJobId)
    } else {
      void runSprint()
    }

    return () => {
      abortRef.current = true
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  const handleRetry = () => {
    abortRef.current = false
    hasStartedRef.current = false
    setPhase('submitting')
    setErrorMsg('')
    setElapsedSeconds(0)
    setSprintJobId(null)
    sessionStorage.removeItem(SPRINT_JOB_STORAGE_KEY)
    void runSprint()
  }

  const isActive = phase === 'submitting' || phase === 'queued' || phase === 'researching' || phase === 'forming_articles'

  return (
    <div className="max-w-2xl mx-auto">
      <div className="text-center mb-8">
        <h2 className="text-2xl font-bold text-bc-gray-800 mb-2">
          {isActive && t('Reporter is researching your field...')}
          {phase === 'complete' && t('Research complete!')}
          {phase === 'error' && t('Research encountered an error')}
        </h2>
      </div>

      <Card className="mb-8">
        <div className="flex flex-col items-center py-8">
          {isActive && (
            <div className="w-24 h-24 rounded-full bg-bc-primary-light flex items-center justify-center mb-6">
              <Loader2 className="w-12 h-12 text-bc-primary animate-spin" />
            </div>
          )}

          {phase === 'complete' && (
            <div className="w-24 h-24 rounded-full bg-green-50 flex items-center justify-center">
              <CheckCircle className="w-12 h-12 text-bc-success" />
            </div>
          )}

          {phase === 'error' && (
            <div className="w-24 h-24 rounded-full bg-red-50 flex items-center justify-center">
              <XCircle className="w-12 h-12 text-bc-error" />
            </div>
          )}
        </div>

        {isActive && (
          <div className="text-center">
            <p className="text-bc-gray-600 font-medium min-h-[1.5em] transition-opacity duration-500">
              {getStatusMessage(phase, roundsUsed, roundsMax, findingsCount)}
            </p>

            {phase === 'researching' && roundsMax > 0 && (
              <div className="mt-4 mx-auto max-w-xs">
                <div className="flex justify-between text-xs text-bc-gray-400 mb-1">
                  <span>{tf('Round %d', roundsUsed)}</span>
                  <span>{tf('%d of %d', roundsUsed, roundsMax)}</span>
                </div>
                <div className="w-full bg-bc-gray-100 rounded-full h-2">
                  <div
                    className="bg-bc-primary h-2 rounded-full transition-all duration-500"
                    style={{ width: `${roundsMax > 0 ? (roundsUsed / roundsMax) * 100 : 0}%` }}
                  />
                </div>
                {findingsCount > 0 && (
                  <p className="text-xs text-bc-gray-500 mt-2">
                    {tf('%d findings discovered', findingsCount)}
                  </p>
                )}
              </div>
            )}

            <p className="text-xs text-bc-gray-400 mt-6">
              {t('This usually takes 1-2 minutes. You can navigate away — we\'ll notify you when done.')}
            </p>

            <p className="text-xs text-bc-gray-400 mt-1">
              {tf('Elapsed: %s', formatElapsed(elapsedSeconds))}
            </p>
          </div>
        )}

        {phase === 'error' && errorMsg && (
          <div className="mt-4 p-4 bg-red-50 rounded-bc border border-red-100">
            <p className="text-bc-error text-sm">{errorMsg}</p>
          </div>
        )}
      </Card>

      <div className="flex flex-wrap justify-between gap-3 items-center">
        <Button
          variant="ghost"
          onClick={onBack}
          disabled={phase === 'submitting'}
          icon={<ChevronRight className="w-5 h-5 ltr:rotate-180" />}
          iconPosition={isRtl() ? 'end' : 'start'}
        >
          {t('Back')}
        </Button>

        {phase === 'error' && (
          <Button
            variant="secondary"
            onClick={handleRetry}
            icon={<RefreshCw className="w-5 h-5" />}
          >
            {t('Try again')}
          </Button>
        )}
      </div>
    </div>
  )
}
