import { useEffect, useRef, useState, type ReactNode } from 'react'
import {
  bulkDeleteReview,
  bulkUpdateReview,
  getReviewItem,
  getReviewPage,
  importReview,
  reviewDownloadUrl,
  updateReviewJudgment,
  type JudgmentRequest,
  type Org,
  type ReviewImportReport,
  type Code,
  type ReviewItem,
} from '../api/client'
import { NOTE_NO_PROCESSING } from '../codes/messages'
import StatusChip from '../ui/StatusChip'
import { attr, useCodes, useScopedCodes, useSplitByFirstRow } from '../codes/useCodes'

const PAGE_SIZE = 30

/** 공공누리 유형이 쓰이는 자리. 변호사 판정과 기존 부착 유형은 고를 수 있는 목록이 다르다. */
const SCOPE_REVIEW = 'review'
const SCOPE_PRIOR = 'prior'

// 대분류·세부·처리결과·공공누리 유형은 모두 코드표에서 온다.
// 판정 매트릭스는 종이 양식과 같은 자리에 칸을 놓아야 해서 목록을 순서대로 훑지 않고
// majors[0], minors[1] 처럼 자리로 집는다. 순서는 코드표의 정렬순서가 정한다.

/**
 * 대분류/세부 선택에 따른 처리결과 추천값. 매칭되는 조합이 없으면 null(현재 값 유지).
 *
 * 예전에는 대분류·세부 이름을 직접 비교하는 if 사슬이었다. 지금은 코드의 suggest 속성만
 * 읽는다 - 추천값이 바뀌어도 코드표만 고치면 된다.
 */
function suggestReviewResult(
  major: string,
  minor: string,
  majors: Code[],
  minors: Code[],
): string | null {
  const majorCode = majors.find((candidate) => candidate.code === major)
  if (!majorCode) {
    return null
  }
  // 세부항목을 쓰는 대분류(제3자 권리)는 세부에서 추천값이 나온다.
  if (majorCode.attrs.hasMinor === true) {
    return attr(minors.find((candidate) => candidate.code === minor), 'suggest')
  }
  return attr(majorCode, 'suggest')
}

/** 세부항목의 회색 부연설명. 없으면 undefined. */
function noteOf(code: Code | undefined): string | undefined {
  return attr(code, 'note') ?? undefined
}

/** 소제목: 작은 파란 사각 불릿 + 굵은 제목 + 회색 부연설명. */
function SectionHeader({ title, note }: { title: string; note?: string }) {
  return (
    <h3 className="flex items-center gap-2 text-sm font-bold">
      <span className="h-2.5 w-2.5 shrink-0 bg-blue-700" />
      {title}
      {note && <span className="text-xs font-normal text-gray-400">{note}</span>}
    </h3>
  )
}

/** 정보 테이블의 회색 라벨 셀. */
function LabelCell({ children }: { children: ReactNode }) {
  return (
    <div className="flex items-center bg-gray-50 px-3 py-2 text-xs font-medium text-gray-600">{children}</div>
  )
}

/** 정보 테이블의 값 셀. */
function ValueCell({ children }: { children: ReactNode }) {
  return <div className="flex items-center px-3 py-1.5">{children}</div>
}

/** 읽기전용 값 표시용 회색 박스. */
function ReadonlyBox({ value }: { value: string | null }) {
  return (
    <div className="w-full rounded border border-gray-200 bg-gray-100 px-3 py-1.5 text-sm text-gray-800">
      {value ? value : <span className="text-gray-400">-</span>}
    </div>
  )
}

/** 단일 라벨/값 행(150px + 1fr). */
function InfoRow({ label, children }: { label: string; children: ReactNode }) {
  return (
    <div className="grid grid-cols-[150px_1fr]">
      <LabelCell>{label}</LabelCell>
      <ValueCell>{children}</ValueCell>
    </div>
  )
}

/** 라벨/값 두 쌍을 한 행에 나란히 배치(150px + 1fr + 150px + 1fr). */
function InfoRowPair({
  leftLabel,
  left,
  rightLabel,
  right,
}: {
  leftLabel: string
  left: ReactNode
  rightLabel: string
  right: ReactNode
}) {
  return (
    <div className="grid grid-cols-[150px_1fr_150px_1fr]">
      <LabelCell>{leftLabel}</LabelCell>
      <ValueCell>{left}</ValueCell>
      <LabelCell>{rightLabel}</LabelCell>
      <ValueCell>{right}</ValueCell>
    </div>
  )
}

/** 권리확인 매트릭스 표의 라디오 한 칸. aria-label로 접근성 이름을 실제 저장값 텍스트로 고정해
 * 화면 표기(줄바꿈된 부연설명)가 늘어나도 접근성 이름은 항상 원래 값과 동일하게 유지한다. */
function MatrixRadio({
  name,
  value,
  label,
  note,
  checked,
  onChange,
  bold,
  disabled,
}: {
  name: string
  value: string
  label?: string
  note?: string
  checked: boolean
  onChange: () => void
  bold?: boolean
  /** 그 세부항목에서 고를 수 없는 처리결과를 잠근다(정책상 택일 범위 밖). */
  disabled?: boolean
}) {
  return (
    <label className={`flex flex-col gap-0.5 ${disabled ? 'cursor-not-allowed opacity-40' : 'cursor-pointer'}`}>
      <span className="flex items-center gap-1.5">
        <input
          type="radio"
          name={name}
          aria-label={value}
          checked={checked}
          disabled={disabled}
          onChange={onChange}
        />
        <span className={bold ? 'font-semibold text-gray-900' : 'text-gray-800'}>{label ?? value}</span>
      </span>
      {note && <span className="pl-5 text-[11px] text-gray-400">{note}</span>}
    </label>
  )
}

function ReviewResultBadge({ value }: { value: string | null }) {
  // 색과 모양이 모두 코드의 tone에서 파생된다. 코드표에 없는 값이면 회색 빈 사각으로 떨어진다.
  const results = useCodes('REVIEW_RESULT')
  if (!value) {
    return <StatusChip label="미판정" tone="gray" glyph="idle" />
  }
  const tone = attr(results.find((candidate) => candidate.code === value), 'tone')
  return <StatusChip label={value} tone={tone} />
}

/** 기관관리 상세의 권리확인 탭. 목록↔상세 두 화면을 이 컴포넌트 하나에서 전환한다. */
export default function ReviewBoard({ org }: { org: Org }) {
  // 일괄 등록 드롭다운이 쓰는 선택지. 변호사가 고르는 자리라 review scope다.
  const results = useCodes('REVIEW_RESULT')
  const koglReview = useScopedCodes('KOGL_TYPE', SCOPE_REVIEW)

  const [mode, setMode] = useState<'list' | number>('list')

  const [items, setItems] = useState<ReviewItem[]>([])
  const [total, setTotal] = useState(0)
  const [done, setDone] = useState(0)
  const [page, setPage] = useState(0)
  const [keywordInput, setKeywordInput] = useState('')
  const [appliedKeyword, setAppliedKeyword] = useState('')
  /** 복합검색(열별 조건). 서로 AND로 겹친다. */
  const [advancedOpen, setAdvancedOpen] = useState(false)
  const [siteInput, setSiteInput] = useState('')
  const [boardInput, setBoardInput] = useState('')
  const [titleInput, setTitleInput] = useState('')
  const [applied, setApplied] = useState<{ site: string; board: string; title: string }>({
    site: '',
    board: '',
    title: '',
  })
  /** 일괄 등록/삭제 대상. 체크한 행의 id. */
  const [selected, setSelected] = useState<number[]>([])
  const [bulkResult, setBulkResult] = useState('')
  const [bulkKoglType, setBulkKoglType] = useState('')
  const [bulkOpinion, setBulkOpinion] = useState('')
  /** 요구사항 [15]로 추가된 비고 일괄 입력. */
  const [bulkNote, setBulkNote] = useState('')
  const [bulkBusy, setBulkBusy] = useState(false)
  const [bulkMessage, setBulkMessage] = useState<string | null>(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [reloadKey, setReloadKey] = useState(0)

  const importInputRef = useRef<HTMLInputElement>(null)
  const [importBusy, setImportBusy] = useState(false)
  const [importReport, setImportReport] = useState<ReviewImportReport | null>(null)
  const [importError, setImportError] = useState<string | null>(null)

  useEffect(() => {
    if (mode !== 'list') {
      return
    }
    let cancelled = false
    setLoading(true)
    setError(null)

    getReviewPage(org.id, {
      keyword: appliedKeyword || undefined,
      site: applied.site || undefined,
      board: applied.board || undefined,
      title: applied.title || undefined,
      page,
      size: PAGE_SIZE,
    })
      .then((data) => {
        if (cancelled) {
          return
        }
        setItems(data.items)
        setTotal(data.total)
        setDone(data.done)
      })
      .catch((e: Error) => {
        if (!cancelled) {
          setError(e.message)
        }
      })
      .finally(() => {
        if (!cancelled) {
          setLoading(false)
        }
      })

    return () => {
      cancelled = true
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mode, org.id, appliedKeyword, applied, page, reloadKey])

  function handleSearch() {
    setPage(0)
    setAppliedKeyword(keywordInput.trim())
    setApplied({ site: siteInput.trim(), board: boardInput.trim(), title: titleInput.trim() })
  }

  function handleResetSearch() {
    setKeywordInput('')
    setSiteInput('')
    setBoardInput('')
    setTitleInput('')
    setPage(0)
    setAppliedKeyword('')
    setApplied({ site: '', board: '', title: '' })
  }

  const allChecked = items.length > 0 && items.every((item) => selected.includes(item.id))

  function toggleAll() {
    setSelected(allChecked ? [] : items.map((item) => item.id))
  }

  function toggleOne(id: number) {
    setSelected((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]))
  }

  /** 체크한 건들에 채운 항목만 한 번에 반영한다. */
  async function handleBulkApply() {
    if (selected.length === 0 || bulkBusy) {
      return
    }
    if (!bulkResult && !bulkKoglType && !bulkOpinion.trim() && !bulkNote.trim()) {
      setBulkMessage('반영할 값을 하나 이상 채워주세요.')
      return
    }
    setBulkBusy(true)
    setBulkMessage(null)
    try {
      const res = await bulkUpdateReview(org.id, {
        ids: selected,
        reviewResult: bulkResult || undefined,
        judgedKoglType: bulkKoglType || undefined,
        opinion: bulkOpinion.trim() || undefined,
        lawyerNote: bulkNote.trim() || undefined,
      })
      setBulkMessage(`${res.updated}건에 반영했습니다.`)
      setSelected([])
      setBulkResult('')
      setBulkKoglType('')
      setBulkOpinion('')
      setBulkNote('')
      setReloadKey((k) => k + 1)
    } catch (e) {
      setBulkMessage(e instanceof Error ? e.message : '일괄 등록에 실패했습니다.')
    } finally {
      setBulkBusy(false)
    }
  }

  async function handleBulkDelete() {
    if (selected.length === 0 || bulkBusy) {
      return
    }
    if (!window.confirm(`선택한 ${selected.length}건을 삭제할까요?`)) {
      return
    }
    setBulkBusy(true)
    setBulkMessage(null)
    try {
      const res = await bulkDeleteReview(org.id, selected)
      setBulkMessage(`${res.deleted}건을 삭제했습니다.`)
      setSelected([])
      setReloadKey((k) => k + 1)
    } catch (e) {
      setBulkMessage(e instanceof Error ? e.message : '삭제에 실패했습니다.')
    } finally {
      setBulkBusy(false)
    }
  }

  async function handleImportFile(file: File) {
    setImportBusy(true)
    setImportError(null)
    try {
      const report = await importReview(org.id, file)
      setImportReport(report)
      setReloadKey((k) => k + 1)
    } catch (e) {
      setImportError(e instanceof Error ? e.message : '엑셀 업로드에 실패했습니다.')
    } finally {
      setImportBusy(false)
    }
  }

  if (mode !== 'list') {
    return (
      <ReviewDetail
        org={org}
        itemId={mode}
        onBack={() => setMode('list')}
        onSaved={() => {
          setMode('list')
          setReloadKey((k) => k + 1)
        }}
      />
    )
  }

  const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE))

  return (
    <div className="p-4">
      <div className="flex flex-wrap items-center justify-between gap-3">
        <div className="flex items-center gap-2">
          <input
            type="text"
            aria-label="검색어"
            value={keywordInput}
            onChange={(e) => setKeywordInput(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === 'Enter') {
                handleSearch()
              }
            }}
            placeholder="사이트명·게시판명·제목 통합검색"
            className="w-64 rounded-md border border-gray-300 px-2 py-1.5 text-sm"
          />
          <button
            type="button"
            aria-expanded={advancedOpen}
            onClick={() => setAdvancedOpen((v) => !v)}
            className="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-50"
          >
            상세검색
          </button>
          <button
            type="button"
            onClick={handleSearch}
            className="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-50"
          >
            검색
          </button>
          <button
            type="button"
            onClick={handleResetSearch}
            className="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-50"
          >
            초기화
          </button>
        </div>

        <div className="flex items-center gap-2">
          <input
            ref={importInputRef}
            type="file"
            accept=".xlsx,.xlsm"
            className="hidden"
            onChange={(e) => {
              const file = e.target.files?.[0]
              if (file) {
                void handleImportFile(file)
              }
              e.target.value = ''
            }}
          />
          <button
            type="button"
            disabled={importBusy}
            onClick={() => importInputRef.current?.click()}
            className="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-50 disabled:opacity-50"
          >
            {importBusy ? '업로드 중…' : '엑셀 업로드'}
          </button>
          <a
            href={reviewDownloadUrl(org.id)}
            className="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-50"
          >
            엑셀 다운로드
          </a>
        </div>
      </div>

      {importReport && (
        <p className="mt-3 text-sm text-gray-600">
          {`신규 ${importReport.created} · 갱신 ${importReport.updated} · 전체 ${importReport.total}`}
        </p>
      )}
      {importError && <p className="mt-3 text-sm text-red-600">{importError}</p>}

      {advancedOpen && (
        <div className="mt-3 flex flex-wrap items-end gap-2 rounded-md border border-gray-200 bg-gray-50 p-3">
          {(
            [
              ['사이트명', siteInput, setSiteInput],
              ['게시판명', boardInput, setBoardInput],
              ['게시물 제목', titleInput, setTitleInput],
            ] as const
          ).map(([label, value, setter]) => (
            <label key={label} className="flex flex-col gap-1 text-xs text-gray-500">
              {label}
              <input
                type="text"
                aria-label={label}
                value={value}
                onChange={(e) => setter(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === 'Enter') {
                    handleSearch()
                  }
                }}
                className="w-48 rounded-md border border-gray-300 px-2 py-1.5 text-sm"
              />
            </label>
          ))}
          <span className="pb-2 text-xs text-gray-400">세 조건은 모두 만족하는 건만 찾습니다(AND)</span>
        </div>
      )}

      <p className="mt-4 text-sm font-medium text-gray-700">
        {`총 ${total}건 · 처리 ${done}건`}
      </p>

      {/* 일괄 등록: 체크한 건들에 같은 값을 한 번에 넣는다. 삭제도 여기서 한다(행마다 두지 않는다). */}
      <div className="mt-3 flex flex-wrap items-center gap-2 rounded-md border border-gray-200 bg-gray-50 p-3">
        <span className="text-sm font-medium text-gray-700">선택 {selected.length}건</span>
        <select
          aria-label="일괄 처리결과"
          value={bulkResult}
          onChange={(e) => setBulkResult(e.target.value)}
          className="rounded-md border border-gray-300 px-2 py-1.5 text-sm"
        >
          <option value="">처리결과 그대로</option>
          {results.map((option) => (
            <option key={option.code} value={option.code}>{option.label}</option>
          ))}
        </select>
        <select
          aria-label="일괄 공공누리 유형"
          value={bulkKoglType}
          onChange={(e) => setBulkKoglType(e.target.value)}
          className="rounded-md border border-gray-300 px-2 py-1.5 text-sm"
        >
          <option value="">공공누리 유형 그대로</option>
          {koglReview.map((option) => (
            <option key={option.code} value={option.code}>{option.label}</option>
          ))}
        </select>
        <input
          type="text"
          aria-label="일괄 의견"
          value={bulkOpinion}
          onChange={(e) => setBulkOpinion(e.target.value)}
          placeholder="의견(비우면 유지)"
          className="w-56 rounded-md border border-gray-300 px-2 py-1.5 text-sm"
        />
        <input
          type="text"
          aria-label="일괄 비고"
          value={bulkNote}
          onChange={(e) => setBulkNote(e.target.value)}
          placeholder="비고(비우면 유지)"
          className="w-56 rounded-md border border-gray-300 px-2 py-1.5 text-sm"
        />
        <button
          type="button"
          disabled={selected.length === 0 || bulkBusy}
          onClick={() => void handleBulkApply()}
          className="rounded-md bg-blue-600 px-3 py-1.5 text-sm text-white disabled:bg-gray-300"
        >
          일괄 등록
        </button>
        <button
          type="button"
          disabled={selected.length === 0 || bulkBusy}
          onClick={() => void handleBulkDelete()}
          className="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-50 disabled:text-gray-300"
        >
          선택 삭제
        </button>
        {bulkMessage && <span className="text-sm text-gray-600">{bulkMessage}</span>}
      </div>

      {error && <p className="mt-2 text-sm text-red-600">{error}</p>}

      <div className="mt-3 overflow-x-auto rounded-lg border border-gray-200">
        <table className="w-full min-w-[1100px] text-left text-sm">
          <thead>
            <tr className="border-b border-gray-200 bg-gray-50 text-xs text-gray-500">
              <th className="w-8 px-3 py-2 font-medium">
                <input
                  type="checkbox"
                  aria-label="전체 선택"
                  checked={allChecked}
                  onChange={toggleAll}
                />
              </th>
              <th className="whitespace-nowrap px-3 py-2 font-medium">No</th>
              <th className="whitespace-nowrap px-3 py-2 font-medium">사이트/게시판</th>
              <th className="px-3 py-2 font-medium">게시물 경로/제목</th>
              <th className="whitespace-nowrap px-3 py-2 font-medium">제작/공표일</th>
              <th className="whitespace-nowrap px-3 py-2 font-medium">첨부파일</th>
              <th className="whitespace-nowrap px-3 py-2 font-medium">기존 공공누리</th>
              <th className="whitespace-nowrap px-3 py-2 font-medium">권리확인</th>
              <th className="whitespace-nowrap px-3 py-2 font-medium">처리결과</th>
              <th className="whitespace-nowrap px-3 py-2 font-medium">공공누리유형(판정)</th>
              <th className="whitespace-nowrap px-3 py-2 font-medium">관리</th>
            </tr>
          </thead>
          <tbody>
            {loading ? (
              <tr>
                <td colSpan={11} className="py-8 text-center text-sm text-gray-400">
                  불러오는 중…
                </td>
              </tr>
            ) : items.length === 0 ? (
              <tr>
                <td colSpan={11} className="py-10 text-center text-sm text-gray-300">
                  업로드된 권리확인 자료가 없습니다. 엑셀 업로드로 시작하세요.
                </td>
              </tr>
            ) : (
              items.map((item) => (
                <tr key={item.id} className="border-b border-gray-100 align-top last:border-b-0">
                  <td className="px-3 py-2">
                    <input
                      type="checkbox"
                      aria-label={`${item.seq}번 선택`}
                      checked={selected.includes(item.id)}
                      onChange={() => toggleOne(item.id)}
                    />
                  </td>
                  <td className="px-3 py-2 text-gray-500">{item.seq}</td>
                  <td className="px-3 py-2">
                    <div className="text-gray-900">{item.siteName ?? '-'}</div>
                    <div className="text-xs text-gray-400">{item.boardName ?? '-'}</div>
                  </td>
                  <td className="px-3 py-2">
                    <div className="text-gray-500">{item.category ?? '-'}</div>
                    <div className="flex items-center gap-1 text-gray-900">
                      <span>{item.postTitle ?? '-'}</span>
                      {item.url && (
                        <a
                          href={item.url}
                          target="_blank"
                          rel="noreferrer"
                          title="원문 보기"
                          aria-label="원문 보기"
                          className="text-blue-500 hover:underline"
                        >
                          🔗
                        </a>
                      )}
                    </div>
                  </td>
                  <td className="px-3 py-2 text-gray-600">
                    <div>{item.producedDate ?? '-'}</div>
                    <div className="text-xs text-gray-400">{item.publishedDate ?? '-'}</div>
                  </td>
                  <td className="px-3 py-2 text-gray-600">{item.hasAttachment ?? '-'}</td>
                  <td className="px-3 py-2 text-gray-600">
                    <div>
                      {item.koglAttached ?? '-'} / {item.koglType ?? '-'}
                    </div>
                    <div className="text-xs text-gray-400">{item.aiType ? 'AI' : ''}</div>
                  </td>
                  <td className="px-3 py-2 text-gray-600">
                    <div>{item.reviewMajor ?? '-'}</div>
                    <div className="text-xs text-gray-400">{item.reviewMinor ?? ''}</div>
                  </td>
                  <td className="px-3 py-2">
                    <ReviewResultBadge value={item.reviewResult} />
                  </td>
                  <td className="px-3 py-2 text-gray-600">
                    <div>{item.judgedKoglType ?? '-'}</div>
                    <div className="text-xs text-gray-400">{item.judgedAiType === 'Y' ? 'AI' : ''}</div>
                  </td>
                  <td className="px-3 py-2">
                    <div className="flex gap-2">
                      <button
                        type="button"
                        onClick={() => setMode(item.id)}
                        className="rounded-md border border-gray-200 px-2 py-0.5 text-xs text-gray-600 hover:bg-gray-50"
                      >
                        수정
                      </button>
                    </div>
                  </td>
                </tr>
              ))
            )}
          </tbody>
        </table>
      </div>

      {items.length > 0 && (
        <div className="mt-3 flex items-center justify-center gap-3 text-sm text-gray-600">
          <button
            type="button"
            disabled={page <= 0}
            onClick={() => setPage((p) => Math.max(0, p - 1))}
            className="rounded-md border border-gray-300 px-2 py-1 disabled:opacity-40"
          >
            ‹
          </button>
          <span>{`${page + 1} / ${totalPages} Page`}</span>
          <button
            type="button"
            disabled={page + 1 >= totalPages}
            onClick={() => setPage((p) => p + 1)}
            className="rounded-md border border-gray-300 px-2 py-1 disabled:opacity-40"
          >
            ›
          </button>
        </div>
      )}
    </div>
  )
}

/** 권리확인 상세(변호사 판정 입력) 화면. */
function ReviewDetail({
  org,
  itemId,
  onBack,
  onSaved,
}: {
  org: Org
  itemId: number
  onBack: () => void
  onSaved: () => void
}) {
  // 판정 매트릭스가 쓰는 선택지. 자리(majors[0] 등)로 집으므로 코드표의 정렬순서가 곧 배치다.
  const majors = useCodes('REVIEW_MAJOR')
  const minors = useCodes('REVIEW_MINOR')
  const results = useCodes('REVIEW_RESULT')
  // 변호사 판정값과 기존 부착 유형은 고를 수 있는 목록이 다르다.
  const kogl = useSplitByFirstRow('KOGL_TYPE', SCOPE_REVIEW)
  const koglPrior = useScopedCodes('KOGL_TYPE', SCOPE_PRIOR)
  const attachmentYn = useCodes('ATTACHMENT_YN')
  const koglAttachedOptions = useCodes('KOGL_ATTACHED')

  const [item, setItem] = useState<ReviewItem | null>(null)
  const [loading, setLoading] = useState(true)
  const [loadError, setLoadError] = useState<string | null>(null)

  const [hasAttachment, setHasAttachment] = useState('')
  const [koglAttached, setKoglAttached] = useState('')
  const [koglType, setKoglType] = useState('')
  const [aiType, setAiType] = useState(false)
  const [reviewMajor, setReviewMajor] = useState('')
  const [reviewMinor, setReviewMinor] = useState('')
  const [reviewResult, setReviewResult] = useState('')
  const [resultManuallySet, setResultManuallySet] = useState(false)
  const [judgedKoglType, setJudgedKoglType] = useState('')
  const [judgedAiType, setJudgedAiType] = useState(false)
  const [needsProcessing, setNeedsProcessing] = useState(false)
  const [opinion, setOpinion] = useState('')
  const [lawyerNote, setLawyerNote] = useState('')

  const [busy, setBusy] = useState(false)
  const [saveError, setSaveError] = useState<string | null>(null)

  useEffect(() => {
    let cancelled = false
    setLoading(true)
    setLoadError(null)

    getReviewItem(org.id, itemId)
      .then((data) => {
        if (cancelled) {
          return
        }
        setItem(data)
        setHasAttachment(data.hasAttachment ?? '')
        setKoglAttached(data.koglAttached ?? '')
        setKoglType(data.koglType ?? '')
        setAiType(data.aiType === 'Y')
        setReviewMajor(data.reviewMajor ?? '')
        setReviewMinor(data.reviewMinor ?? '')
        setReviewResult(data.reviewResult ?? '')
        setResultManuallySet(false)
        setJudgedKoglType(data.judgedKoglType ?? '')
        setJudgedAiType(data.judgedAiType === 'Y')
        setNeedsProcessing(data.needsProcessing === 'Y')
        setOpinion(data.opinion ?? '')
        setLawyerNote(data.lawyerNote ?? '')
      })
      .catch((e: Error) => {
        if (!cancelled) {
          setLoadError(e.message)
        }
      })
      .finally(() => {
        if (!cancelled) {
          setLoading(false)
        }
      })

    return () => {
      cancelled = true
    }
  }, [org.id, itemId])

  function applySuggestion(major: string, minor: string) {
    if (resultManuallySet) {
      return
    }
    const suggestion = suggestReviewResult(major, minor, majors, minors)
    if (suggestion) {
      setReviewResult(suggestion)
      applyResultDerivations(suggestion)
    }
  }

  /**
   * 처리결과가 정해지면 공공누리 유형·비고·권리처리필요를 규칙대로 함께 맞춘다
   * (2026-07-20 제3자 권리처리 정책변경).
   *
   *  - 권리처리 추진      → 공공누리 유형 '보류'
   *  - 권리처리 추진 미희망 → 기존 공공누리 유형을 그대로 쓰고 비고에 사유를 남긴다.
   *                        기존 유형이 없으면 미선택 상태로 둔다(별도 제한 없음)
   *  - 개방불가          → 공공누리 유형 '개방불가'
   *
   * 사용자가 이 값들을 다시 손으로 바꿀 수 있게, 잠그지는 않고 값만 채워 준다.
   *
   * 어떤 값을 채울지는 코드표(needsProcessing·koglSet·koglCopiesPrior)에서 오고,
   * "기존 유형을 복사하고 비고에 문구를 덧붙인다"는 동작만 여기 남는다. 동작까지 데이터로
   * 옮기면 코드표 안에 규칙 언어를 새로 만드는 꼴이 된다.
   */
  function applyResultDerivations(result: string) {
    const resultCode = results.find((candidate) => candidate.code === result)
    if (!resultCode) {
      return
    }

    const needs = resultCode.attrs.needsProcessing
    if (typeof needs === 'boolean') {
      setNeedsProcessing(needs)
    }

    if (resultCode.attrs.koglCopiesPrior === true) {
      // 기존에 부착돼 있던 유형을 그대로 쓰고, 그 사유를 비고에 남긴다.
      setJudgedKoglType(koglType || '')
      if (koglType) {
        setLawyerNote((prev) =>
          prev.includes(NOTE_NO_PROCESSING)
            ? prev
            : [prev.trim(), NOTE_NO_PROCESSING].filter(Boolean).join('\n'),
        )
      }
      return
    }

    const koglSet = attr(resultCode, 'koglSet')
    if (koglSet) {
      setJudgedKoglType(koglSet)
    }
  }

  /** 지금 고른 세부항목에서 그 처리결과를 고를 수 있는지. 제한이 없는 세부항목은 전부 허용이다. */
  function resultAllowed(option: string): boolean {
    const minorCode = minors.find((candidate) => candidate.code === reviewMinor)
    const allowed = minorCode?.attrs.allowed
    return !Array.isArray(allowed) || (allowed as string[]).includes(option)
  }

  function pickMajor(major: string) {
    setReviewMajor(major)
    // 세부항목을 쓰지 않는 대분류로 옮기면 세부 선택을 비운다.
    const majorCode = majors.find((candidate) => candidate.code === major)
    const minor = majorCode?.attrs.hasMinor === true ? reviewMinor : ''
    setReviewMinor(minor)
    applySuggestion(major, minor)
  }

  function pickMinor(minor: string) {
    setReviewMinor(minor)
    applySuggestion(reviewMajor, minor)
  }

  function pickResult(result: string) {
    setReviewResult(result)
    setResultManuallySet(true)
    applyResultDerivations(result)
  }

  async function handleSave() {
    setBusy(true)
    setSaveError(null)
    try {
      const body: JudgmentRequest = {
        hasAttachment: hasAttachment || null,
        koglAttached: koglAttached || null,
        koglType: koglType || null,
        aiType: aiType ? 'Y' : null,
        // 개방가능여부는 별도 입력칸 없이 처리결과에서 파생한다("신유형 개방"일 때만 개방 가능).
        openable: reviewResult === '신유형 개방' ? 'Y' : reviewResult ? 'N' : null,
        reviewMajor: reviewMajor || null,
        reviewMinor: reviewMinor || null,
        reviewResult: reviewResult || null,
        judgedKoglType: judgedKoglType || null,
        judgedAiType: judgedAiType ? 'Y' : null,
        opinion: opinion || null,
        lawyerNote: lawyerNote || null,
        needsProcessing: needsProcessing ? 'Y' : null,
      }
      await updateReviewJudgment(org.id, itemId, body)
      onSaved()
    } catch (e) {
      setSaveError(e instanceof Error ? e.message : '저장에 실패했습니다.')
    } finally {
      setBusy(false)
    }
  }

  if (loading) {
    return <p className="p-4 py-10 text-center text-sm text-gray-400">불러오는 중…</p>
  }
  if (loadError || !item) {
    return <p className="p-4 py-10 text-center text-sm text-red-600">{loadError ?? '게시물을 찾을 수 없습니다.'}</p>
  }

  return (
    <div className="p-4">
      <section className="rounded-lg border border-gray-200 p-4">
        <SectionHeader title="게시물 정보" note="(조사원)" />

        <div className="mt-3 overflow-hidden rounded border border-gray-300 divide-y divide-gray-200">
          <InfoRow label="기관명">
            <ReadonlyBox value={org.orgName} />
          </InfoRow>
          <InfoRowPair
            leftLabel="사이트명"
            left={<ReadonlyBox value={item.siteName} />}
            rightLabel="범주(카테고리)"
            right={<ReadonlyBox value={item.category} />}
          />
          <InfoRow label="게시판명">
            <ReadonlyBox value={item.boardName} />
          </InfoRow>
          <InfoRow label="게시물제목">
            <ReadonlyBox value={item.postTitle} />
          </InfoRow>
          <InfoRow label="URL주소">
            {item.url ? (
              <a
                href={item.url}
                target="_blank"
                rel="noreferrer"
                className="w-full break-all rounded border border-gray-200 bg-gray-100 px-3 py-1.5 text-sm text-blue-600 hover:underline"
              >
                {item.url}
              </a>
            ) : (
              <ReadonlyBox value={null} />
            )}
          </InfoRow>
          <InfoRowPair
            leftLabel="제작일"
            left={<ReadonlyBox value={item.producedDate} />}
            rightLabel="공표일"
            right={<ReadonlyBox value={item.publishedDate} />}
          />
          <InfoRow label="첨부파일 여부">
            <div className="flex items-center gap-5 text-sm">
              {attachmentYn.map((option) => (
                <label key={option.code} className="flex cursor-pointer items-center gap-1.5">
                  <input
                    type="radio"
                    name="has-attachment"
                    aria-label={`첨부파일 ${option.code}`}
                    checked={hasAttachment === option.code}
                    onChange={() => setHasAttachment(option.code)}
                    className="h-3.5 w-3.5 accent-blue-600"
                  />
                  {option.label}
                </label>
              ))}
            </div>
          </InfoRow>
          <InfoRow label="기존 공공누리">
            <div className="flex flex-wrap items-center gap-5 text-sm">
              {koglAttachedOptions.map((option) => (
                <label key={option.code} className="flex cursor-pointer items-center gap-1.5">
                  <input
                    type="radio"
                    name="kogl-attached"
                    aria-label={`기존 공공누리 ${option.code}`}
                    checked={koglAttached === option.code}
                    onChange={() => setKoglAttached(option.code)}
                    className="h-3.5 w-3.5 accent-blue-600"
                  />
                  {option.label}
                </label>
              ))}
              <label className="flex items-center gap-1.5">
                유형
                <select
                  aria-label="기존 공공누리 유형"
                  value={koglType}
                  onChange={(e) => setKoglType(e.target.value)}
                  className="rounded border border-gray-300 px-2 py-1 text-sm"
                >
                  <option value="">선택</option>
                  {/* 기관 조사표에서 온 값('유형 확인 필요' 등)도 여기 들어간다 - 없으면
                      이미 그 값으로 저장된 1,900여 건이 "선택 안 됨"으로 보인다. */}
                  {koglPrior.map((option) => (
                    <option key={option.code} value={option.code}>{option.label}</option>
                  ))}
                </select>
              </label>
              <label className="flex cursor-pointer items-center gap-1.5">
                <input
                  type="checkbox"
                  aria-label="기존 공공누리 AI유형"
                  checked={aiType}
                  onChange={(e) => setAiType(e.target.checked)}
                  className="h-3.5 w-3.5 accent-blue-600"
                />
                AI유형
              </label>
            </div>
          </InfoRow>
        </div>
      </section>

      <section className="mt-4 rounded-lg border border-gray-200 p-4">
        <SectionHeader title="권리확인" note="(변호사)" />

        <div className="mt-3 overflow-x-auto">
          <table className="w-full border-collapse text-sm">
            <thead>
              <tr className="bg-gray-50 text-xs font-semibold text-gray-600">
                <th className="border border-gray-300 px-3 py-2">좌(대분류)</th>
                <th className="border border-gray-300 px-3 py-2">중(세부)</th>
                <th className="border border-gray-300 px-3 py-2">우(처리결과)</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td className="border border-gray-300 px-3 py-2 align-top" rowSpan={1}>
                  <MatrixRadio
                    name="review-major"
                    value={majors[0].code}
                    note="저작권성 없음, 배포용, 단순데이터"
                    checked={reviewMajor === majors[0].code}
                    onChange={() => pickMajor(majors[0].code)}
                  />
                </td>
                <td className="border border-gray-300 px-3 py-2" />
                <td
                  className="border border-gray-300 bg-gray-50 px-3 py-2 text-center align-middle"
                  rowSpan={2}
                >
                  <MatrixRadio
                    name="review-result"
                    value={results[0].code}
                    checked={reviewResult === results[0].code}
                    onChange={() => pickResult(results[0].code)}
                    disabled={!resultAllowed(results[0].code)}
                    bold
                  />
                </td>
              </tr>
              <tr>
                <td className="border border-gray-300 px-3 py-2 align-top" rowSpan={1}>
                  <MatrixRadio
                    name="review-major"
                    value={majors[1].code}
                    checked={reviewMajor === majors[1].code}
                    onChange={() => pickMajor(majors[1].code)}
                  />
                </td>
                <td className="border border-gray-300 px-3 py-2 align-top">
                  <MatrixRadio
                    name="review-minor"
                    value={minors[0].code}
                    label={minors[0].label}
                    checked={reviewMinor === minors[0].code}
                    onChange={() => pickMinor(minors[0].code)}
                  />
                </td>
              </tr>
              <tr>
                <td className="border border-gray-300 px-3 py-2 align-top" rowSpan={4}>
                  <MatrixRadio
                    name="review-major"
                    value={majors[2].code}
                    checked={reviewMajor === majors[2].code}
                    onChange={() => pickMajor(majors[2].code)}
                  />
                </td>
                <td className="border border-gray-300 px-3 py-2 align-top">
                  <MatrixRadio
                    name="review-minor"
                    value={minors[1].code}
                    label={minors[1].label}
                    note={noteOf(minors[1])}
                    checked={reviewMinor === minors[1].code}
                    onChange={() => pickMinor(minors[1].code)}
                  />
                </td>
                <td
                  className="border border-gray-300 bg-gray-50 px-3 py-2 text-center align-middle"
                  rowSpan={1}
                >
                  <MatrixRadio
                    name="review-result"
                    value={results[1].code}
                    checked={reviewResult === results[1].code}
                    onChange={() => pickResult(results[1].code)}
                    disabled={!resultAllowed(results[1].code)}
                    bold
                  />
                </td>
              </tr>
              <tr>
                <td className="border border-gray-300 px-3 py-2 align-top">
                  <MatrixRadio
                    name="review-minor"
                    value={minors[2].code}
                    label={minors[2].label}
                    note={noteOf(minors[2])}
                    checked={reviewMinor === minors[2].code}
                    onChange={() => pickMinor(minors[2].code)}
                  />
                </td>
                <td
                  className="border border-gray-300 bg-gray-50 px-3 py-2 align-middle"
                  rowSpan={3}
                >
                  {/* 정책변경: 3·4·초상권은 추진/미희망 택일이다. 초상권만 개방불가까지 고를 수 있다. */}
                  <div className="flex flex-col items-start gap-1.5">
                    <MatrixRadio
                      name="review-result"
                      value={results[2].code}
                      checked={reviewResult === results[2].code}
                      onChange={() => pickResult(results[2].code)}
                      disabled={!resultAllowed(results[2].code)}
                      bold
                    />
                    <MatrixRadio
                      name="review-result"
                      value={results[4].code}
                      checked={reviewResult === results[4].code}
                      onChange={() => pickResult(results[4].code)}
                      disabled={!resultAllowed(results[4].code)}
                    />
                    {reviewMinor === minors[4].code && (
                      <span className="text-[11px] text-gray-400">개방불가도 선택 가능</span>
                    )}
                  </div>
                </td>
              </tr>
              <tr>
                <td className="border border-gray-300 px-3 py-2 align-top">
                  <MatrixRadio
                    name="review-minor"
                    value={minors[3].code}
                    label={minors[3].label}
                    note={noteOf(minors[3])}
                    checked={reviewMinor === minors[3].code}
                    onChange={() => pickMinor(minors[3].code)}
                  />
                </td>
              </tr>
              <tr>
                <td className="border border-gray-300 px-3 py-2 align-top">
                  <MatrixRadio
                    name="review-minor"
                    value={minors[4].code}
                    label={minors[4].label}
                    checked={reviewMinor === minors[4].code}
                    onChange={() => pickMinor(minors[4].code)}
                  />
                </td>
              </tr>
              <tr>
                <td className="border border-gray-300 px-3 py-2 align-top" rowSpan={1}>
                  <MatrixRadio
                    name="review-major"
                    value={majors[3].code}
                    note="공공누리 적용 제외 대상, 검토불가"
                    checked={reviewMajor === majors[3].code}
                    onChange={() => pickMajor(majors[3].code)}
                  />
                </td>
                <td className="border border-gray-300 px-3 py-2" />
                <td
                  className="border border-gray-300 bg-gray-50 px-3 py-2 text-center align-middle"
                  rowSpan={1}
                >
                  <MatrixRadio
                    name="review-result"
                    value={results[3].code}
                    checked={reviewResult === results[3].code}
                    onChange={() => pickResult(results[3].code)}
                    disabled={!resultAllowed(results[3].code)}
                    bold
                  />
                </td>
              </tr>
            </tbody>
          </table>
        </div>

        <div className="mt-3 overflow-hidden rounded border border-gray-300 divide-y divide-gray-200">
          <InfoRow label="공공누리 유형">
            <div className="flex flex-col gap-1.5">
              <div className="flex flex-wrap gap-x-4 gap-y-1">
                {kogl.first.map((option) => (
                  <label key={option.code} className="flex items-center gap-1.5 text-sm text-gray-800">
                    <input
                      type="radio"
                      name="judged-kogl-type"
                      // 처리결과에도 '개방불가'가 있어 라벨만으로는 구분되지 않는다.
                      aria-label={`공공누리 유형 ${option.code}`}
                      checked={judgedKoglType === option.code}
                      onChange={() => setJudgedKoglType(option.code)}
                    />
                    {option.label}
                  </label>
                ))}
              </div>
              <div className="flex flex-wrap items-center gap-x-4 gap-y-1">
                {kogl.rest.map((option) => (
                  <label key={option.code} className="flex items-center gap-1.5 text-sm text-gray-800">
                    <input
                      type="radio"
                      name="judged-kogl-type"
                      // 처리결과에도 '개방불가'가 있어 라벨만으로는 구분되지 않는다.
                      aria-label={`공공누리 유형 ${option.code}`}
                      checked={judgedKoglType === option.code}
                      onChange={() => setJudgedKoglType(option.code)}
                    />
                    {option.label}
                  </label>
                ))}
                <label className="ml-2 flex items-center gap-1.5 text-sm text-gray-800">
                  <input
                    type="checkbox"
                    checked={judgedAiType}
                    onChange={(e) => setJudgedAiType(e.target.checked)}
                  />
                  AI유형
                </label>
              </div>
            </div>
          </InfoRow>

          <InfoRow label="권리처리필요">
            <label className="flex items-center gap-1.5 text-sm text-gray-800">
              <input
                type="checkbox"
                checked={needsProcessing}
                onChange={(e) => setNeedsProcessing(e.target.checked)}
              />
              권리처리필요
            </label>
          </InfoRow>

          <InfoRow label="의견">
            <textarea
              id="review-opinion"
              aria-label="의견"
              rows={3}
              value={opinion}
              onChange={(e) => setOpinion(e.target.value)}
              className="w-full rounded border border-gray-300 px-2 py-1.5 text-sm"
            />
          </InfoRow>

          <InfoRow label="비고">
            <textarea
              id="review-lawyer-note"
              aria-label="비고"
              rows={3}
              value={lawyerNote}
              onChange={(e) => setLawyerNote(e.target.value)}
              className="w-full rounded border border-gray-300 px-2 py-1.5 text-sm"
            />
          </InfoRow>
        </div>

        {saveError && <p className="mt-3 text-sm text-red-600">{saveError}</p>}

        <div className="mt-4 flex justify-end gap-2">
          <button
            type="button"
            disabled={busy}
            onClick={onBack}
            className="rounded-md bg-gray-600 px-3 py-1.5 text-sm font-semibold text-white hover:bg-gray-700 disabled:opacity-50"
          >
            목록
          </button>
          <button
            type="button"
            disabled={busy}
            onClick={() => void handleSave()}
            className="rounded-md bg-blue-700 px-4 py-1.5 text-sm font-semibold text-white hover:bg-blue-800 disabled:cursor-not-allowed disabled:bg-gray-300"
          >
            수정
          </button>
        </div>
      </section>
    </div>
  )
}
