import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
import { api, ApiError } from '../api/client'
import { CopyFlowNav } from '../components/CopyFlowNav'
import { CopyMissionActiveJobsCard } from '../components/CopyMissionActiveJobsCard'
import { CopyMissionDraftCard } from '../components/CopyMissionDraftCard'
import { CopyMissionResearchOverview } from '../components/CopyMissionResearchOverview'
import { VerifiedAuthorBadge } from '../components/VerifiedAuthorBadge'

import type { CopyMissionResearchMapDraft } from '../components/CopyMissionResearchEditor'
import { AcLink, Badge, Button, Card, ErrorText, Notice, PageTitle, SuccessText } from '../components/ui'
import {
  COPY_FLOW,
  copyAnalyzeActionLabel,
  getActiveCopyPersonaId,
  isPersonaNotFoundError,
  rememberCopyPersonaId,
  replicateJobScanPostId,
  type CopyFlowStepKey,
} from '../lib/copyFlow'
import { threadsAuthorPostUrl, threadsProfileUrl } from '../lib/threadsLinks'
import { hasPersona8D } from '../lib/styleProfile'
import { bindCopyIslanderActions, ensureCopyActionHandlers, unbindCopyIslanderActions } from '../lib/islander/copyActions'
import { useIslanderPage } from '../lib/islander'
import { formatActiveJobConflictError, isActiveJobStatus } from '../lib/jobStatus'
import type { CopyDraftData, JobData, PersonaData, ViralScanPostData } from '../types/api'
import {
  hydrateCopyMissionJobs,
  rememberCopyMissionJobIds,
  rememberCopyMissionReplicateJobId,
} from '../lib/copyMissionJobs'

import {
  viralAccountRowClass,
  viralAuthorTier,
  viralEngagementTier,
  viralPostRowClass,
} from '../lib/viralSignals'
import type { CopyMissionData, CopySimilarAccountData } from '../types/copyMission'
import { copyMissionStatusLabel, copyMissionStatusTone, hasMissionResearchMap } from '../types/copyMission'

type LocationState = {
  analyzeJobId?: string
  scanJobId?: string
}

export function CopyMissionDetailPage() {
  const navigate = useNavigate()
  const location = useLocation()
  const { missionId = '' } = useParams()
  const [personaId, setPersonaId] = useState(getActiveCopyPersonaId())
  const [personas, setPersonas] = useState<PersonaData[]>([])
  const [missions, setMissions] = useState<CopyMissionData[]>([])
  const [persona, setPersona] = useState<PersonaData | null>(null)
  const [mission, setMission] = useState<CopyMissionData | null>(null)
  const [posts, setPosts] = useState<ViralScanPostData[]>([])
  const [drafts, setDrafts] = useState<CopyDraftData[]>([])
  const [brief, setBrief] = useState('')
  const [selectedTags, setSelectedTags] = useState<string[]>([])
  const [analyzeJob, setAnalyzeJob] = useState<JobData | null>(null)
  const [analyzeJobId, setAnalyzeJobId] = useState<string | null>(null)
  const [scanJob, setScanJob] = useState<JobData | null>(null)
  const [scanJobId, setScanJobId] = useState<string | null>(null)
  const [matrixJob, setMatrixJob] = useState<JobData | null>(null)
  const [matrixJobId, setMatrixJobId] = useState<string | null>(null)
  const [replicateJobs, setReplicateJobs] = useState<JobData[]>([])
  const [loading, setLoading] = useState(true)
  const [analyzing, setAnalyzing] = useState(false)
  const [scanning, setScanning] = useState(false)

  const [generatingId, setGeneratingId] = useState('')
  const [generatingMatrix, setGeneratingMatrix] = useState(false)
  const [savingDraftId, setSavingDraftId] = useState('')
  const [publishingDraftId, setPublishingDraftId] = useState('')
  const [archiving, setArchiving] = useState(false)
  const [draftEdits, setDraftEdits] = useState<Record<string, { text: string; hook: string }>>({})
  const [editingDraftId, setEditingDraftId] = useState<string | null>(null)
  const [error, setError] = useState('')
  const [message, setMessage] = useState('')

  const activeStep = useMemo((): CopyFlowStepKey => {
    if (
      location.hash === '#copy-output' ||
      location.hash === '#copy-scan' ||
      location.hash === '#copy-matrix'
    ) {
      return 'output'
    }
    return 'research'
  }, [location.hash])

  const sortedDrafts = useMemo(() => {
    return [...drafts].sort((a, b) => {
      const aMatrix = a.draft_type === 'matrix'
      const bMatrix = b.draft_type === 'matrix'
      if (aMatrix && bMatrix) return (a.sort_order ?? 0) - (b.sort_order ?? 0)
      if (aMatrix !== bMatrix) return aMatrix ? -1 : 1
      return (b.create_at ?? 0) - (a.create_at ?? 0)
    })
  }, [drafts])

  const sortedPosts = useMemo(() => {
    return [...posts].sort((a, b) => {
      const aVerified = a.author_verified ? 1 : 0
      const bVerified = b.author_verified ? 1 : 0
      if (aVerified !== bVerified) return bVerified - aVerified
      return (b.engagement_score ?? 0) - (a.engagement_score ?? 0)
    })
  }, [posts])

  const sortedSimilarAccounts = useMemo((): CopySimilarAccountData[] => {
    const list = mission?.research_map?.similar_accounts ?? []
    return [...list].sort((a, b) => {
      const aVerified = a.author_verified ? 1 : 0
      const bVerified = b.author_verified ? 1 : 0
      if (aVerified !== bVerified) return bVerified - aVerified
      return (b.engagement_score ?? 0) - (a.engagement_score ?? 0)
    })
  }, [mission?.research_map?.similar_accounts])

  const syncMissionJobs = useCallback(
    async (lastScanJobId?: string) => {
      const snapshot = await hydrateCopyMissionJobs(missionId, lastScanJobId)
      setAnalyzeJob(snapshot.analyzeJob)
      setAnalyzeJobId(snapshot.analyzeJobId)
      setScanJob(snapshot.scanJob)
      setScanJobId(snapshot.scanJobId)
      setMatrixJob(snapshot.matrixJob)
      setMatrixJobId(snapshot.matrixJobId)
      setReplicateJobs(snapshot.replicateJobs)
    },
    [missionId],
  )

  const refreshJobById = useCallback(async (jobId: string) => {
    const id = jobId.trim()
    if (!id) return
    const job = await api.get<JobData>(`/api/v1/jobs/${encodeURIComponent(id)}`, { auth: true })
    if (job.template_type === 'analyze-copy-mission') {
      if (isActiveJobStatus(job.status)) {
        setAnalyzeJob(job)
        setAnalyzeJobId(job.id)
      } else {
        setAnalyzeJob(null)
        setAnalyzeJobId(null)
      }
      return
    }
    if (job.template_type === 'scan-viral') {
      if (isActiveJobStatus(job.status)) {
        setScanJob(job)
        setScanJobId(job.id)
      } else {
        setScanJob(null)
        setScanJobId(null)
      }
      return
    }
    if (job.template_type === 'generate-copy-matrix') {
      if (isActiveJobStatus(job.status)) {
        setMatrixJob(job)
        setMatrixJobId(job.id)
      } else {
        setMatrixJob(null)
        setMatrixJobId(null)
      }
      return
    }
    if (job.template_type === 'generate-copy-draft') {
      const postId = replicateJobScanPostId(job)
      setReplicateJobs((prev) => {
        const rest = prev.filter((item) => item.id !== job.id && replicateJobScanPostId(item) !== postId)
        if (isActiveJobStatus(job.status)) {
          return [...rest, job]
        }
        return rest
      })
    }
  }, [])

  const loadNavCatalog = useCallback(async (targetPersonaId: string) => {
    const [personasRes, missionsRes] = await Promise.all([
      api.get<{ list: PersonaData[] }>('/api/v1/personas', { auth: true }),
      api.get<{ list: CopyMissionData[] }>(
        `/api/v1/personas/${encodeURIComponent(targetPersonaId)}/copy-missions`,
        { auth: true },
      ),
    ])
    setPersonas(personasRes.list ?? [])
    setMissions(missionsRes.list ?? [])
  }, [])

  const loadWithPersona = useCallback(
    async (targetPersonaId: string) => {
      const [personaRes, missionRes, postsRes, draftsRes] = await Promise.all([
        api.get<PersonaData>(`/api/v1/personas/${encodeURIComponent(targetPersonaId)}`, { auth: true }),
        api.get<CopyMissionData>(
          `/api/v1/personas/${encodeURIComponent(targetPersonaId)}/copy-missions/${encodeURIComponent(missionId)}`,
          { auth: true },
        ),
        api.get<{ list: ViralScanPostData[]; total: number }>(
          `/api/v1/personas/${encodeURIComponent(targetPersonaId)}/copy-missions/${encodeURIComponent(missionId)}/scan-posts`,
          { auth: true },
        ),
        api.get<{ list: CopyDraftData[]; total: number }>(
          `/api/v1/personas/${encodeURIComponent(targetPersonaId)}/copy-missions/${encodeURIComponent(missionId)}/copy-drafts`,
          { auth: true },
        ),
      ])
      setPersona(personaRes)
      setMission(missionRes)
      setBrief(missionRes.brief ?? '')
      setSelectedTags(missionRes.selected_tags ?? [])
      setPosts(postsRes.list ?? [])
      setDrafts(draftsRes.list ?? [])
      await Promise.all([syncMissionJobs(missionRes.last_scan_job_id), loadNavCatalog(targetPersonaId)])
      return targetPersonaId
    },
    [loadNavCatalog, missionId, syncMissionJobs],
  )

  const discoverMissionPersonaId = useCallback(async (): Promise<string | null> => {
    const personas = await api.get<{ list: PersonaData[] }>('/api/v1/personas', { auth: true })
    for (const item of personas.list ?? []) {
      try {
        await api.get<CopyMissionData>(
          `/api/v1/personas/${encodeURIComponent(item.id)}/copy-missions/${encodeURIComponent(missionId)}`,
          { auth: true },
        )
        return item.id
      } catch {
        continue
      }
    }
    return null
  }, [missionId])

  const load = useCallback(async (options?: { silent?: boolean }) => {
    if (!personaId || !missionId) return
    const silent = options?.silent ?? false
    if (!silent) {
      setLoading(true)
      setError('')
    }
    try {
      await loadWithPersona(personaId)
    } catch (e) {
      if (silent) return
      const message = e instanceof ApiError ? e.message : '載入失敗'
      if (e instanceof ApiError && (isPersonaNotFoundError(message) || /copy-mission|找不到|not found/i.test(message))) {
        try {
          const discovered = await discoverMissionPersonaId()
          if (discovered && discovered !== personaId) {
            setPersonaId(discovered)
            rememberCopyPersonaId(discovered)
            await loadWithPersona(discovered)
            setError('先前選擇的人設已失效，已自動切換到任務所屬人設。')
            return
          }
          setError('找不到這個拷貝任務，已回到任務列表。')
          navigate('/matrix', { replace: true })
          return
        } catch {
          // fall through
        }
      }
      if (!silent) setError(message)
    } finally {
      if (!silent) setLoading(false)
    }
  }, [discoverMissionPersonaId, loadWithPersona, missionId, navigate, personaId])

  useEffect(() => {
    const id = getActiveCopyPersonaId()
    if (id) setPersonaId(id)
  }, [])

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

  useEffect(() => {
    const state = location.state as LocationState | null
    if (!state?.analyzeJobId && !state?.scanJobId) return
    if (state.analyzeJobId) {
      rememberCopyMissionJobIds(missionId, { analyzeJobId: state.analyzeJobId })
      setAnalyzeJobId(state.analyzeJobId)
    }
    if (state.scanJobId) {
      rememberCopyMissionJobIds(missionId, { scanJobId: state.scanJobId })
      setScanJobId(state.scanJobId)
    }
    navigate(`${location.pathname}${location.search}`, { replace: true, state: null })
  }, [location.pathname, location.search, location.state, missionId, navigate])

  const lastScrolledHash = useRef('')
  useEffect(() => {
    lastScrolledHash.current = ''
  }, [missionId])

  useEffect(() => {
    const hash = location.hash
    if (!hash || loading) return
    if (lastScrolledHash.current === hash) return
    lastScrolledHash.current = hash
    const id = hash.slice(1)
    const el = document.getElementById(id)
    el?.scrollIntoView({ behavior: 'smooth', block: 'start' })
  }, [location.hash, loading])

  useEffect(() => {
    ensureCopyActionHandlers()
    bindCopyIslanderActions({
      personaId,
      missionId,
      keywords: selectedTags,
      onStartAnalyze: () => setAnalyzing(true),
      onStartScan: () => setScanning(true),
      onGenerateMatrix: () => setGeneratingMatrix(true),
      reload: () => void load(),
    })
    return () => unbindCopyIslanderActions()
  }, [personaId, missionId, selectedTags, load])

  useIslanderPage({
    title: '拷貝任務詳情',
    purpose: '研究地圖 → 海巡爆款 → 產出矩陣或深仿寫草稿。',
    hints: [
      '海巡與產出矩陣是兩個獨立按鈕',
      '草稿預設唯讀，按「修改」才進入編輯',
      '開啟會員設定「收集留言」後，爆款會附熱門留言樣本',
    ],
    suggestions: ['產生研究地圖', '開始海巡', '產出 5 篇矩陣'],
  })

  const replicateJobIds = useMemo(() => replicateJobs.map((job) => job.id), [replicateJobs])

  useEffect(() => {
    const hasActiveOutputJobs =
      !!matrixJobId || replicateJobIds.length > 0 || generatingMatrix || generatingId !== ''
    if (!analyzeJobId && !scanJobId && !analyzing && !scanning && !hasActiveOutputJobs) return
    const timer = window.setInterval(() => {
      if (analyzeJobId) {
        void refreshJobById(analyzeJobId).catch(() => undefined)
      }
      if (scanJobId) {
        void refreshJobById(scanJobId).catch(() => undefined)
      }
      if (matrixJobId) {
        void refreshJobById(matrixJobId).catch(() => undefined)
      }
      for (const jobId of replicateJobIds) {
        void refreshJobById(jobId).catch(() => undefined)
      }
      void load({ silent: true }).catch(() => undefined)
    }, 3000)
    return () => window.clearInterval(timer)
  }, [
    analyzeJobId,
    analyzing,
    generatingId,
    generatingMatrix,
    load,
    matrixJobId,
    refreshJobById,
    replicateJobIds,
    scanJobId,
    scanning,
  ])

  const startAnalyze = async () => {
    if (!personaId || !missionId) return
    setAnalyzing(true)
    setError('')
    setMessage('')
    try {
      const started = await api.post<{ job_id: string; status: string; message?: string }>(
        `/api/v1/personas/${encodeURIComponent(personaId)}/copy-missions/${encodeURIComponent(missionId)}/analyze-jobs`,
        {},
        { auth: true },
      )
      if (started.job_id) {
        rememberCopyMissionJobIds(missionId, { analyzeJobId: started.job_id })
        setAnalyzeJobId(started.job_id)
        await refreshJobById(started.job_id)
      }
      setMessage(started.message || '研究地圖產生中…')
      await load()
    } catch (e) {
      const message = e instanceof ApiError ? formatActiveJobConflictError(e.message) : '啟動分析失敗'
      setError(message)
    } finally {
      setAnalyzing(false)
    }
  }

  const saveResearchMap = useCallback(
    async (draft: CopyMissionResearchMapDraft) => {
      if (!personaId || !missionId) return
      const updated = await api.patch<CopyMissionData>(
        `/api/v1/personas/${encodeURIComponent(personaId)}/copy-missions/${encodeURIComponent(missionId)}`,
        {
          audience_summary: draft.audience_summary,
          content_goal: draft.content_goal,
          questions: draft.questions,
          pillars: draft.pillars,
          exclusions: draft.exclusions,
          benchmark_notes: draft.benchmark_notes,
          selected_tags: draft.selected_tags,
        },
        { auth: true },
      )
      setMission(updated)
      setSelectedTags(updated.selected_tags ?? [])
      setMessage(
        draft.selected_tags.length > 0
          ? `研究地圖已儲存（${draft.selected_tags.length} 組搜尋標籤）`
          : '研究地圖已儲存',
      )
      setError('')
    },
    [missionId, personaId],
  )

  const persistSelectedTags = async (tags: string[]) => {
    if (!personaId || !missionId) return
    setError('')
    try {
      const updated = await api.patch<CopyMissionData>(
        `/api/v1/personas/${encodeURIComponent(personaId)}/copy-missions/${encodeURIComponent(missionId)}`,
        { selected_tags: tags },
        { auth: true },
      )
      setMission(updated)
      setSelectedTags(updated.selected_tags ?? [])
      setMessage('搜尋標籤已更新')
    } catch (e) {
      setError(e instanceof ApiError ? e.message : '儲存標籤失敗')
    }
  }

  const startScan = async () => {
    if (!personaId || !missionId) return
    setScanning(true)
    setError('')
    try {
      const started = await api.post<{ job_id: string; status: string; message?: string }>(
        `/api/v1/personas/${encodeURIComponent(personaId)}/copy-missions/${encodeURIComponent(missionId)}/scan-jobs`,
        {},
        { auth: true },
      )
      if (started.job_id) {
        rememberCopyMissionJobIds(missionId, { scanJobId: started.job_id })
        setScanJobId(started.job_id)
        await refreshJobById(started.job_id)
      }
      setPosts([])
      setDrafts([])
      setDraftEdits({})
      setEditingDraftId(null)
      setMessage(started.message || '已清除舊爆款，海巡進行中…')
      await load()
    } catch (e) {
      const message = e instanceof ApiError ? formatActiveJobConflictError(e.message) : '啟動海巡失敗'
      setError(message)
    } finally {
      setScanning(false)
    }
  }

  const addAccountTag = (username: string) => {
    const tag = username.startsWith('@') ? username : `@${username}`
    if (selectedTags.includes(tag)) return
    if (selectedTags.length >= 6) {
      setError('搜尋標籤最多 6 個，請先編輯研究地圖移除一個再加入帳號')
      return
    }
    void persistSelectedTags([...selectedTags, tag])
  }

  const generateMatrix = async () => {
    if (!personaId || !missionId) return
    setGeneratingMatrix(true)
    setError('')
    setMessage('')
    try {
      const started = await api.post<{ job_id: string; status: string; message?: string }>(
        `/api/v1/personas/${encodeURIComponent(personaId)}/copy-missions/${encodeURIComponent(missionId)}/matrix-jobs`,
        { count: 5 },
        { auth: true },
      )
      if (started.job_id) {
        rememberCopyMissionJobIds(missionId, { matrixJobId: started.job_id })
        setMatrixJobId(started.job_id)
        await refreshJobById(started.job_id)
      }
      setMessage(started.message || '內容矩陣產出中…')
      await load()
    } catch (e) {
      const message = e instanceof ApiError ? formatActiveJobConflictError(e.message) : '產出矩陣失敗'
      setError(message)
    } finally {
      setGeneratingMatrix(false)
    }
  }

  const draftEdit = (draft: CopyDraftData) =>
    draftEdits[draft.id] ?? { text: draft.text, hook: draft.hook ?? '' }

  const setDraftEdit = (draft: CopyDraftData, patch: Partial<{ text: string; hook: string }>) => {
    setDraftEdits((prev) => {
      const base = prev[draft.id] ?? { text: draft.text, hook: draft.hook ?? '' }
      return { ...prev, [draft.id]: { ...base, ...patch } }
    })
  }

  const saveDraft = async (draft: CopyDraftData, markReady = false) => {
    if (!personaId) return
    const edit = draftEdit(draft)
    setSavingDraftId(draft.id)
    setError('')
    try {
      const updated = await api.patch<CopyDraftData>(
        `/api/v1/personas/${encodeURIComponent(personaId)}/copy-drafts/${encodeURIComponent(draft.id)}`,
        {
          text: edit.text,
          hook: edit.hook || undefined,
          status: markReady ? 'ready' : undefined,
        },
        { auth: true },
      )
      setDrafts((prev) => prev.map((d) => (d.id === draft.id ? updated : d)))
      setDraftEdits((prev) => {
        const next = { ...prev }
        delete next[draft.id]
        return next
      })
      setEditingDraftId((cur) => (cur === draft.id ? null : cur))
    } catch (e) {
      setError(e instanceof ApiError ? e.message : '儲存草稿失敗')
    } finally {
      setSavingDraftId('')
    }
  }

  const publishDraft = async (draft: CopyDraftData) => {
    if (!personaId) return
    const edit = draftEdit(draft)
    if (!window.confirm('確定要透過 Threads API 發布這則貼文嗎？')) return
    setPublishingDraftId(draft.id)
    setError('')
    try {
      const result = await api.post<{
        draft_id: string
        permalink?: string
        status: string
        message: string
      }>(
        `/api/v1/personas/${encodeURIComponent(personaId)}/copy-drafts/${encodeURIComponent(draft.id)}/publish`,
        { text: edit.text, confirm: true },
        { auth: true },
      )
      if (result.message) {
        setError('')
      }
      await load()
    } catch (e) {
      setError(e instanceof ApiError ? e.message : '發布失敗')
    } finally {
      setPublishingDraftId('')
    }
  }

  const archiveMission = async () => {
    if (!personaId || !missionId) return
    if (!window.confirm('封存後會停用定期海巡，確定要封存此任務嗎？')) return
    setArchiving(true)
    setError('')
    try {
      await api.patch(
        `/api/v1/personas/${encodeURIComponent(personaId)}/copy-missions/${encodeURIComponent(missionId)}`,
        { status: 'archived' },
        { auth: true },
      )
      await load()
    } catch (e) {
      setError(e instanceof ApiError ? e.message : '封存失敗')
    } finally {
      setArchiving(false)
    }
  }

  const copyDraftText = async (text: string): Promise<boolean> => {
    try {
      await navigator.clipboard.writeText(text)
      setError('')
      setMessage('已複製內文到剪貼簿')
      return true
    } catch {
      setError('無法複製到剪貼簿')
      return false
    }
  }

  const generateDraft = async (scanPostId: string) => {
    if (!personaId || !missionId) return
    setGeneratingId(scanPostId)
    setError('')
    setMessage('')
    try {
      const started = await api.post<{ job_id: string; status: string; message?: string }>(
        `/api/v1/personas/${encodeURIComponent(personaId)}/copy-missions/${encodeURIComponent(missionId)}/copy-draft-jobs`,
        { scan_post_id: scanPostId },
        { auth: true },
      )
      if (started.job_id) {
        rememberCopyMissionReplicateJobId(missionId, scanPostId, started.job_id)
        await refreshJobById(started.job_id)
      }
      setMessage(started.message || '深仿寫產出中…')
      await load()
    } catch (e) {
      const message = e instanceof ApiError ? formatActiveJobConflictError(e.message) : '產出仿寫失敗'
      setError(message)
    } finally {
      setGeneratingId('')
    }
  }

  const handlePersonaChange = async (nextId: string) => {
    rememberCopyPersonaId(nextId)
    setPersonaId(nextId)
    setError('')
    try {
      const missionsRes = await api.get<{ list: CopyMissionData[] }>(
        `/api/v1/personas/${encodeURIComponent(nextId)}/copy-missions`,
        { auth: true },
      )
      const list = missionsRes.list ?? []
      setMissions(list)
      const keepsMission = list.some((item) => item.id === missionId)
      if (!keepsMission) {
        const first = list[0]?.id
        if (first) {
          navigate(`/matrix/missions/${encodeURIComponent(first)}${location.hash}`)
        } else {
          navigate('/matrix')
        }
      }
    } catch (e) {
      setError(e instanceof ApiError ? e.message : '切換人設失敗')
    }
  }

  const handleMissionChange = (nextId: string) => {
    navigate(`/matrix/missions/${encodeURIComponent(nextId)}${location.hash}`)
  }

  if (!personaId) {
    return (
      <Card className="p-8 text-center">
        <p className="text-sm text-ink-secondary">請先從拷貝忍者首頁選擇人設。</p>
        <Link to="/matrix" className="mt-3 inline-block font-semibold text-brand hover:underline">
          返回任務列表
        </Link>
      </Card>
    )
  }

  const has8D = hasPersona8D(persona)
  const mapReady = hasMissionResearchMap(mission)
  const displayName = persona?.display_name?.trim() || '未命名人設'
  const missionTitle = mission?.label?.trim() || mission?.seed_query?.trim() || '拷貝任務'
  const stepMeta = COPY_FLOW.find((step) => step.key === activeStep) ?? COPY_FLOW[0]
  const analyzeActive = analyzing || (analyzeJob != null && isActiveJobStatus(analyzeJob.status))
  const scanActive = scanning || (scanJob != null && isActiveJobStatus(scanJob.status))
  const matrixActive =
    generatingMatrix || (matrixJob != null && isActiveJobStatus(matrixJob.status))
  const replicateJobsByPost = useMemo(() => {
    const map = new Map<string, JobData>()
    for (const job of replicateJobs) {
      const postId = replicateJobScanPostId(job)
      if (postId) map.set(postId, job)
    }
    return map
  }, [replicateJobs])
  const hasActiveOutputJobs = matrixActive || replicateJobs.length > 0
  const analyzeActionLabel = copyAnalyzeActionLabel(analyzeJob, analyzing, mapReady)
  const scanBlockedReason = analyzeActive
    ? '研究地圖產生中，請稍候再海巡'
    : scanActive
      ? '海巡任務進行中'
      : !mapReady
        ? '請先產生研究地圖'
        : selectedTags.length === 0
          ? '請先勾選並儲存搜尋標籤'
          : ''
  const scanClearHint =
    posts.length > 0 || drafts.length > 0 ? '會清除此任務的舊爆款候選與產文草稿，再重新掃描' : undefined
  const matrixBlockedReason = !posts.length
    ? '請先完成海巡'
    : !has8D
      ? '請先完成 8D 分析'
      : matrixActive
        ? '矩陣產出中'
        : ''

  const openDraftEdit = (draft: CopyDraftData) => {
    setDraftEdits((prev) => ({
      ...prev,
      [draft.id]: prev[draft.id] ?? { text: draft.text, hook: draft.hook ?? '' },
    }))
    setEditingDraftId(draft.id)
  }

  const closeDraftEdit = (draftId: string) => {
    setEditingDraftId((cur) => (cur === draftId ? null : cur))
    setDraftEdits((prev) => {
      const next = { ...prev }
      delete next[draftId]
      return next
    })
  }

  return (
    <div className="mx-auto w-full max-w-6xl space-y-6">
      <AcLink to="/matrix" className="inline-flex items-center gap-1.5 text-sm">
        ← 拷貝忍者任務
      </AcLink>

      <CopyFlowNav
        active={activeStep}
        missionId={missionId}
        personaId={personaId}
        personas={personas}
        missions={missions}
        onPersonaChange={(id) => void handlePersonaChange(id)}
        onMissionChange={handleMissionChange}
        loading={loading}
        analyzeJobActive={analyzeActive}
        scanJobActive={scanActive}
      />

      <div className="flex flex-wrap items-start justify-between gap-4">
        <PageTitle
          title={stepMeta.label}
          subtitle={
            loading
              ? '載入中…'
              : `「${missionTitle}」· ${displayName} · ${stepMeta.desc}`
          }
        />
        <div className="hx-page-actions">
          {activeStep === 'research' ? (
            <>
              {mission?.status !== 'archived' ? (
                <Button variant="ghost" onClick={() => void archiveMission()} disabled={archiving || loading}>
                  {archiving ? '封存中…' : '封存任務'}
                </Button>
              ) : null}
              <Button
                variant="soft"
                onClick={() => void startAnalyze()}
                disabled={loading || analyzeActive}
                title={analyzeActive ? '研究地圖產生中，請稍候' : undefined}
              >
                {analyzeActionLabel}
              </Button>
            </>
          ) : null}
        </div>
      </div>

      <div className="flex flex-wrap items-center gap-2">
        {mission?.status ? (
          <Badge tone={copyMissionStatusTone(mission.status)}>{copyMissionStatusLabel(mission.status)}</Badge>
        ) : null}
        {mapReady ? <Badge tone="success">研究地圖就緒</Badge> : <Badge tone="warning">待產生地圖</Badge>}
        {posts.length ? <Badge tone="sky">爆款 {posts.length}</Badge> : null}
      </div>

      <SuccessText message={message} />
      <ErrorText message={error} />

      {analyzeActive || scanActive || hasActiveOutputJobs ? (
        <CopyMissionActiveJobsCard
          missionId={missionId}
          analyzeJob={analyzeJob}
          scanJob={scanJob}
          matrixJob={matrixJob}
          replicateJobs={replicateJobs}
        />
      ) : null}

      {!has8D ? (
        <Notice
          tone="warning"
          title="建議先完成 8D 分析"
          message="仿寫會套用 style_profile；請到人設頁完成對標帳號 8D。"
        />
      ) : null}

      {brief.trim() || mission?.seed_query ? (
        <Notice
          tone="info"
          title="任務種子"
          message={`種子熱詞：${mission?.seed_query || '—'}${brief.trim() ? ` · 補充：${brief.trim()}` : ''}`}
        />
      ) : null}

      <section id="copy-research" className="space-y-4">
        <div className="hx-research-map-workspace">
          {analyzeActive && !analyzeJob ? (
            <Card className="mb-5">
              <p className="text-sm text-ink-secondary">研究地圖產生中，正在建立背景任務…</p>
            </Card>
          ) : null}
          <CopyMissionResearchOverview
            mission={mission}
            loading={(loading && !mission) || !!analyzeJob}
            onSaveResearchMap={mapReady ? saveResearchMap : undefined}
          />
        </div>

        {posts.length > 0 && sortedSimilarAccounts.length ? (
          <Card className="hx-copy-similar-accounts">
            <h2 className="hx-copy-section-title">參考帳號</h2>
            <p className="text-xs text-muted">
              海巡後依主題與互動篩選；藍勾與高互動列會以邊框標示，沒有藍勾資料也不影響結果。
            </p>
            <ul className="hx-copy-similar-accounts__list">
              {sortedSimilarAccounts.map((acc) => {
                const profileHref = threadsProfileUrl(acc.username, acc.profile_url)
                const tier = viralAuthorTier(acc)
                const engagementTier = viralEngagementTier(acc.engagement_score)
                return (
                  <li key={acc.username} className={viralAccountRowClass(tier)}>
                    <div className="min-w-0 flex-1 space-y-1">
                      <div className="flex flex-wrap items-center gap-2">
                        {profileHref ? (
                          <a
                            href={profileHref}
                            target="_blank"
                            rel="noreferrer"
                            className="text-sm font-semibold text-brand hover:underline"
                          >
                            @{acc.username}
                          </a>
                        ) : (
                          <span className="text-sm font-semibold text-ink">@{acc.username}</span>
                        )}
                        {acc.author_verified ? <VerifiedAuthorBadge compact /> : null}
                        {engagementTier === 'hot' ? <Badge tone="success">高互動</Badge> : null}
                        {acc.engagement_score ? (
                          <Badge tone="brand">互動 {acc.engagement_score}</Badge>
                        ) : null}
                        {acc.follower_count ? (
                          <span className="text-xs text-muted">{acc.follower_count.toLocaleString()} 粉絲</span>
                        ) : null}
                        {acc.like_count ? <span className="text-xs text-muted">{acc.like_count} 讚</span> : null}
                        {acc.post_count ? <span className="text-xs text-muted">{acc.post_count} 則相關貼文</span> : null}
                      </div>
                      {acc.reason ? <p className="text-xs leading-relaxed text-ink-secondary">{acc.reason}</p> : null}
                    </div>
                    <Button variant="ghost" onClick={() => addAccountTag(acc.username)}>
                      加入海巡
                    </Button>
                  </li>
                )
              })}
            </ul>
          </Card>
        ) : null}
      </section>

      <section id="copy-output" className="hx-copy-output-workspace space-y-4">
        <div className="hx-copy-output-toolbar">
          <div className="min-w-0 space-y-0.5">
            <h2 className="hx-copy-section-title">海巡與產文</h2>
            <p className="text-xs leading-relaxed text-ink-secondary">
              先海巡找爆款，再產矩陣或深仿寫。Threads 主文上限 500 字；爆款常見 80～220 字。
            </p>
          </div>
          <div className="flex flex-wrap gap-2">
            <Button
              variant="soft"
              onClick={() => void startScan()}
              disabled={loading || !!scanBlockedReason}
              title={scanBlockedReason || scanClearHint}
            >
              {scanActive ? '海巡進行中…' : '開始海巡'}
            </Button>
            {scanActive ? (
              <a href="#copy-mission-active-jobs" className="self-center text-xs font-semibold text-brand hover:underline">
                查看任務進度
              </a>
            ) : null}
            <Button
              onClick={() => void generateMatrix()}
              disabled={loading || !!matrixBlockedReason}
              title={matrixBlockedReason || undefined}
            >
              {matrixActive ? '矩陣產出中…' : '產出 5 篇矩陣'}
            </Button>
          </div>
        </div>

        <div className="space-y-3">
          <h3 className="hx-copy-subsection-title">爆款候選 · {sortedPosts.length}</h3>
          {sortedPosts.length ? (
            <ul className="hx-copy-post-list">
              {sortedPosts.map((post) => {
                const authorHref = threadsAuthorPostUrl(post.author, post.permalink)
                const tier = viralAuthorTier(post)
                const engagementTier = viralEngagementTier(post.engagement_score)
                return (
                  <li key={post.id} className={viralPostRowClass(tier)}>
                    <div className="hx-copy-post-row__meta">
                      <Badge tone="sky">{post.search_tag || '爆款'}</Badge>
                      {post.author_verified ? <VerifiedAuthorBadge compact /> : null}
                      {engagementTier === 'hot' ? <Badge tone="success">高互動</Badge> : null}
                      <span>互動 {post.engagement_score}</span>
                      {post.like_count ? <span className="text-xs text-muted">{post.like_count} 讚</span> : null}
                      {post.author ? (
                        authorHref ? (
                          <a href={authorHref} target="_blank" rel="noreferrer" className="font-semibold text-brand hover:underline">
                            @{post.author}
                          </a>
                        ) : (
                          <span>@{post.author}</span>
                        )
                      ) : null}
                    </div>
                    <p className="hx-copy-post-row__text">{post.text}</p>
                    {post.replies?.length ? (
                      <div className="hx-copy-post-row__replies">
                        {post.replies.slice(0, 2).map((reply, idx) => (
                          <p key={`${post.id}-reply-${idx}`}>
                            {reply.author ? `@${reply.author}：` : ''}
                            {reply.text}
                          </p>
                        ))}
                      </div>
                    ) : null}
                    <Button
                      variant="ghost"
                      type="button"
                      onClick={() => void generateDraft(post.id)}
                      disabled={
                        generatingId === post.id ||
                        replicateJobsByPost.has(post.id) ||
                        !has8D
                      }
                      title={!has8D ? '請先完成 8D 分析' : undefined}
                    >
                      {generatingId === post.id || replicateJobsByPost.has(post.id)
                        ? '仿寫中…'
                        : '深仿寫'}
                    </Button>
                  </li>
                )
              })}
            </ul>
          ) : (
            <p className="hx-copy-empty">完成海巡後會列出爆款候選</p>
          )}
        </div>

        <div className="space-y-3 border-t border-line pt-4">
          <h3 className="hx-copy-subsection-title">產文草稿 · {sortedDrafts.length}</h3>
          {sortedDrafts.length ? (
            <div className="hx-copy-draft-list">
              {sortedDrafts.map((draft) => (
                <CopyMissionDraftCard
                  key={draft.id}
                  draft={draft}
                  editing={editingDraftId === draft.id}
                  edit={draftEdit(draft)}
                  onEdit={() => openDraftEdit(draft)}
                  onCancelEdit={() => closeDraftEdit(draft.id)}
                  onChange={(patch) => setDraftEdit(draft, patch)}
                  onSave={() => void saveDraft(draft)}
                  onMarkReady={() => void saveDraft(draft, true)}
                  onCopy={() => copyDraftText(draftEdit(draft).text)}
                  onPublish={() => void publishDraft(draft)}
                  saving={savingDraftId === draft.id}
                  publishing={publishingDraftId === draft.id}
                />
              ))}
            </div>
          ) : (
            <p className="hx-copy-empty">海巡後可產出矩陣，或從爆款候選單篇深仿寫</p>
          )}
        </div>
      </section>
    </div>
  )
}