import { useCallback, useEffect, useLayoutEffect, useReducer, useRef, useState } from 'react'
import {
  campaignsApi,
  runsApi,
  type CampaignAnalysis,
  type CampaignAnalysisStatus,
  type CampaignComparison,
  type CampaignListItem,
  type CampaignReport,
  type CampaignTimelineEntry,
  type Run,
} from '../api'
import { usePolling } from './usePolling'

type ReadPhase = 'idle' | 'loading' | 'refreshing' | 'ready' | 'error'

interface ReadSlot<T> {
  phase: ReadPhase
  value: T
  error: string | null
}

export interface CampaignReportSnapshot {
  report: CampaignReport
  runs: Run[]
  timeline: CampaignTimelineEntry[]
  analysis: CampaignAnalysis
  comparison: CampaignComparison
}

interface CampaignReadState {
  list: ReadSlot<CampaignListItem[]>
  report: ReadSlot<CampaignReportSnapshot | null> & {
    selectedId: string | null
    requestId: number
  }
}

type CampaignReadAction =
  | { type: 'list_requested'; silent?: boolean }
  | { type: 'list_succeeded'; value: CampaignListItem[] }
  | { type: 'list_failed'; error: string }
  | { type: 'report_cleared'; requestId: number }
  | { type: 'report_requested'; id: string; requestId: number; silent?: boolean }
  | { type: 'report_succeeded'; id: string; requestId: number; value: CampaignReportSnapshot }
  | { type: 'report_failed'; id: string; requestId: number; error: string }

export interface CampaignReadAdapter {
  list: () => Promise<CampaignListItem[]>
  report: (campaignId: string) => Promise<CampaignReportSnapshot>
  timeline: (campaignId: string) => Promise<CampaignTimelineEntry[]>
  generateAnalysis: (campaignId: string) => Promise<void>
  generateComparison: (campaignId: string, baselineCampaignId?: string) => Promise<void>
}

export const campaignReadAdapter: CampaignReadAdapter = {
  list: () => campaignsApi.list().then((response) => response.data),
  report: async (campaignId) => {
    const [report, runs, timeline, analysis, comparison] = await Promise.all([
      campaignsApi.report(campaignId),
      runsApi.list(),
      campaignsApi.timeline(campaignId),
      campaignsApi.getAnalysis(campaignId),
      campaignsApi.getComparison(campaignId),
    ])
    return {
      report: report.data,
      runs: runs.data.filter((run) => run.campaign_id === campaignId),
      timeline: timeline.data.entries,
      analysis: analysis.data,
      comparison: comparison.data,
    }
  },
  timeline: (campaignId) => campaignsApi.timeline(campaignId).then((response) => response.data.entries),
  generateAnalysis: (campaignId) => campaignsApi.generateAnalysis(campaignId).then(() => undefined),
  generateComparison: (campaignId, baselineCampaignId) => (
    campaignsApi.generateComparison(campaignId, baselineCampaignId).then(() => undefined)
  ),
}

const initialState: CampaignReadState = {
  list: { phase: 'idle', value: [], error: null },
  report: {
    phase: 'idle', value: null, error: null, selectedId: null, requestId: 0,
  },
}

function requestSlot<T>(slot: ReadSlot<T>, silent: boolean | undefined): ReadSlot<T> {
  return {
    ...slot,
    phase: silent && slot.phase === 'ready' ? 'refreshing' : 'loading',
    error: null,
  }
}

function reducer(state: CampaignReadState, action: CampaignReadAction): CampaignReadState {
  switch (action.type) {
    case 'list_requested':
      return { ...state, list: requestSlot(state.list, action.silent) }
    case 'list_succeeded':
      return { ...state, list: { phase: 'ready', value: action.value, error: null } }
    case 'list_failed':
      return {
        ...state,
        list: state.list.value.length > 0
          ? { ...state.list, phase: 'ready', error: null }
          : { ...state.list, phase: 'error', error: action.error },
      }
    case 'report_cleared':
      return {
        ...state,
        report: {
          phase: 'idle', value: null, error: null, selectedId: null, requestId: action.requestId,
        },
      }
    case 'report_requested': {
      const sameSelection = state.report.selectedId === action.id
      const current = sameSelection
        ? state.report
        : { ...state.report, value: null, selectedId: action.id }
      return {
        ...state,
        report: {
          ...requestSlot(current, action.silent),
          selectedId: action.id,
          requestId: action.requestId,
        },
      }
    }
    case 'report_succeeded':
      if (state.report.selectedId !== action.id || state.report.requestId !== action.requestId) return state
      return {
        ...state,
        report: {
          phase: 'ready', value: action.value, error: null,
          selectedId: action.id, requestId: action.requestId,
        },
      }
    case 'report_failed':
      if (state.report.selectedId !== action.id || state.report.requestId !== action.requestId) return state
      return {
        ...state,
        report: state.report.value
          ? { ...state.report, phase: 'ready', error: null }
          : { ...state.report, phase: 'error', error: action.error },
      }
  }
}

function errorMessage(error: unknown): string {
  return error instanceof Error ? error.message : '读取评估活动失败'
}

const isCampaignActive = (status: CampaignListItem['status'] | undefined) => (
  status === 'planned' || status === 'running'
)

const isIntelligenceJobActive = (status: CampaignAnalysisStatus | undefined) => (
  status === 'queued' || status === 'generating'
)

export function useCampaignReport(
  selectedId: string | null,
  enabled = true,
  adapter: CampaignReadAdapter = campaignReadAdapter,
) {
  const [state, dispatch] = useReducer(reducer, initialState)
  const [expandedIds, setExpandedIds] = useState<string[]>([])
  const [timelines, setTimelines] = useState<Record<string, CampaignTimelineEntry[]>>({})
  const [analysisBusy, setAnalysisBusy] = useState(false)
  const [comparisonBusy, setComparisonBusy] = useState(false)
  const reportRequestId = useRef(0)
  const selectedIdRef = useRef(selectedId)
  selectedIdRef.current = selectedId

  const loadList = useCallback(async (silent = false) => {
    dispatch({ type: 'list_requested', silent })
    try {
      dispatch({ type: 'list_succeeded', value: await adapter.list() })
    } catch (error) {
      dispatch({ type: 'list_failed', error: errorMessage(error) })
    }
  }, [adapter])

  const loadReport = useCallback(async (campaignId: string, silent = false) => {
    const requestId = ++reportRequestId.current
    dispatch({ type: 'report_requested', id: campaignId, requestId, silent })
    try {
      const value = await adapter.report(campaignId)
      dispatch({ type: 'report_succeeded', id: campaignId, requestId, value })
    } catch (error) {
      dispatch({ type: 'report_failed', id: campaignId, requestId, error: errorMessage(error) })
    }
  }, [adapter])

  const loadTimeline = useCallback(async (campaignId: string) => {
    const timeline = await adapter.timeline(campaignId)
    setTimelines((current) => ({ ...current, [campaignId]: timeline }))
  }, [adapter])

  const setTimelineExpanded = useCallback((campaignId: string, expanded: boolean) => {
    setExpandedIds((current) => (
      expanded
        ? current.includes(campaignId) ? current : [...current, campaignId]
        : current.filter((id) => id !== campaignId)
    ))
    if (expanded) void loadTimeline(campaignId)
  }, [loadTimeline])

  const generateAnalysis = useCallback(async () => {
    const campaignId = selectedIdRef.current
    if (!campaignId) return
    setAnalysisBusy(true)
    try {
      await adapter.generateAnalysis(campaignId)
      if (selectedIdRef.current === campaignId) await loadReport(campaignId, true)
    } finally {
      setAnalysisBusy(false)
    }
  }, [adapter, loadReport])

  const generateComparison = useCallback(async (baselineCampaignId?: string) => {
    const campaignId = selectedIdRef.current
    if (!campaignId) return
    setComparisonBusy(true)
    try {
      await adapter.generateComparison(campaignId, baselineCampaignId)
      if (selectedIdRef.current === campaignId) await loadReport(campaignId, true)
    } finally {
      setComparisonBusy(false)
    }
  }, [adapter, loadReport])

  useEffect(() => { void loadList() }, [loadList])

  useLayoutEffect(() => {
    if (selectedId == null) {
      dispatch({ type: 'report_cleared', requestId: ++reportRequestId.current })
      return
    }
    void loadReport(selectedId)
  }, [loadReport, selectedId])

  const listActive = state.list.value.some((campaign) => isCampaignActive(campaign.status))
  const snapshot = state.report.value
  const reportActive = isCampaignActive(snapshot?.report.status)
    || isIntelligenceJobActive(snapshot?.analysis.status)
    || isIntelligenceJobActive(snapshot?.comparison.status)
  const activeExpandedIds = expandedIds.filter(
    (id) => state.list.value.some((campaign) => campaign.id === id && isCampaignActive(campaign.status)),
  )

  usePolling(() => { void loadList(true) }, 5000, enabled && listActive)
  usePolling(
    () => { if (selectedId) void loadReport(selectedId, true) },
    5000,
    enabled && selectedId != null && reportActive,
  )
  usePolling(
    () => { activeExpandedIds.forEach((id) => { void loadTimeline(id) }) },
    5000,
    enabled && activeExpandedIds.length > 0,
  )

  return {
    list: state.list,
    report: state.report,
    reloadList: () => loadList(true),
    reloadReport: () => (selectedId ? loadReport(selectedId, true) : Promise.resolve()),
    expandedIds,
    timelines,
    setTimelineExpanded,
    generateAnalysis,
    generateComparison,
    analysisBusy,
    comparisonBusy,
  }
}
