import { useCallback, useEffect, useMemo, useState } from 'react'
import {
  getDashboard,
  updateStage,
  type DashboardData,
  type DashboardOrgRow,
} from '../api/client'
import { useStages } from '../codes/stage'
import { barClass } from '../codes/tone'
import HelpButton from '../help/HelpButton'

/**
 * 시안(3단계 대쉬보드 화면 설계안)의 대시보드 화면. 세 블록으로 구성된다.
 *   1) KPI 타일 7개
 *   2) 단계별 현황 칸반 - 카드를 끌어 단계를 옮긴다
 *   3) 최근 진행 기관 표 - 최근 변동순 상위 N개
 *
 * 칸반 맨 앞의 "단계 미지정" 컬럼은 <b>채널을 아직 만들지 않은 기관만</b> 담는다(요청자 확정).
 * 기관관리 상세 화면에는 이 개념이 없다 - 거기서는 채널 생성 전이면 1~12 스트립이 전부
 * 회색으로 보일 뿐이다(OrgOverview). 미지정 카드는 끌어도 단계가 바뀌지 않고, 채널을
 * 먼저 만들라는 경고창이 뜬다. 단계만 올려 두면 게시글·자료·권리검토가 전부 채널에 붙어
 * 있어서 빈 껍데기가 되기 때문이다.
 */

interface Props {
  /** 카드/표에서 기관을 클릭했을 때. 기관관리 화면으로 이동해 해당 기관을 연다. */
  onOpenOrg: (orgId: number) => void
  /** [전체 기관 보기]. 기관관리 화면으로만 이동한다. */
  onOpenOrgList: () => void
}

// 원문자(①②)와 구간 색은 더 이상 여기 박아두지 않는다. 원문자는 단계 번호에서 계산하고
// 색은 코드표의 tone에서 온다 - 단계가 하나 늘 때마다 배열을 같이 늘려야 했던 자리다.

/** 표/칸반이 한 번에 보여주는 기관 수. [더보기]를 누를 때마다 이만큼 늘어난다. */
const PAGE_SIZE = 8
/** 칸반 컬럼 하나가 한 번에 보여주는 카드 수. */
const COLUMN_SIZE = 2

const DAY_MS = 24 * 60 * 60 * 1000

function formatDate(ms: number | null): string {
  if (!ms) {
    return '-'
  }
  const d = new Date(ms)
  const pad = (n: number) => String(n).padStart(2, '0')
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`
}

/** 현재 단계로 넘어온 뒤 며칠 지났는지. 이력이 없으면 null이라 배지를 아예 안 그린다. */
function daysSince(ms: number | null): number | null {
  if (!ms) {
    return null
  }
  return Math.max(0, Math.floor((Date.now() - ms) / DAY_MS))
}

function percent(done: number, total: number): number {
  return total === 0 ? 0 : Math.round((done / total) * 100)
}

/**
 * 카드가 들어갈 칸반 컬럼. null이면 "단계 미지정"이다.
 *
 * 채널이 있는데 단계가 비어 있는 기관은 V11 백필과 채널 생성 로직 덕에 원칙적으로 없지만,
 * 혹시 생기더라도 보드에서 사라지지 않도록 1단계로 본다.
 */
function columnOf(row: DashboardOrgRow): number | null {
  if (!row.hasChannel) {
    return null
  }
  return row.stage ?? 1
}

function KpiTile({
  label,
  value,
  sub,
  accent,
}: {
  label: string
  value: string
  sub?: string
  accent?: boolean
}) {
  return (
    <div
      className={`flex min-w-0 flex-1 flex-col items-center gap-1 rounded-lg border bg-white px-3 py-4 ${
        accent ? 'border-gray-300 shadow-sm' : 'border-gray-200'
      }`}
    >
      <span className="text-xs text-gray-500">{label}</span>
      <span className="text-2xl font-semibold tabular-nums">{value}</span>
      {sub && <span className="text-xs text-gray-400 tabular-nums">{sub}</span>}
    </div>
  )
}

function ProgressLine({ label, done, total }: { label: string; done: number; total: number }) {
  const value = percent(done, total)
  return (
    <div className="space-y-1">
      <div className="flex items-baseline justify-between text-xs">
        <span className="text-gray-500">
          {label} <span className="font-medium text-gray-800 tabular-nums">{done.toLocaleString()} / {total.toLocaleString()}</span>
        </span>
        <span className="font-medium text-gray-600 tabular-nums">{value}%</span>
      </div>
      <div className="h-1 w-full rounded-full bg-gray-100">
        <div className="h-1 rounded-full bg-blue-500" style={{ width: `${value}%` }} />
      </div>
    </div>
  )
}

/** 단계 구간에 따라 카드 본문이 달라진다(시안의 컬럼별 카드 모양). */
function CardBody({ row }: { row: DashboardOrgRow }) {
  const stages = useStages()
  const stage = columnOf(row)
  const group = stages.groupKey(stage)
  const lawyer = row.lawyerName ?? '변호사 미배정'

  if (stage === null) {
    return <p className="text-xs text-gray-500">채널 미생성 · 담당자 배정 후 채널을 만드세요</p>
  }
  if (stage === 1) {
    return <p className="text-xs text-gray-500">신청일 {formatDate(row.stageChangedAt)}</p>
  }
  if (stage === 2 || stage === 3) {
    return (
      <div className="space-y-0.5 text-xs text-gray-500">
        <p>목록 {row.reviewTotal.toLocaleString()}건</p>
        <p>접수일 {formatDate(row.stageChangedAt)}</p>
      </div>
    )
  }
  if (group === 'assign') {
    return (
      <div className="space-y-0.5 text-xs text-gray-500">
        <p>{lawyer}</p>
        <p>배정일 {row.lawyerAssignedDate ?? formatDate(row.stageChangedAt)}</p>
      </div>
    )
  }
  if (group === 'check') {
    return (
      <div className="space-y-1.5">
        <p className="text-xs text-gray-500">{lawyer}</p>
        <ProgressLine label="확인" done={row.reviewDone} total={row.reviewTotal} />
      </div>
    )
  }
  if (group === 'process') {
    return (
      <div className="space-y-1.5">
        <p className="text-xs text-gray-500">{lawyer}</p>
        <ProgressLine label="확인" done={row.reviewDone} total={row.reviewTotal} />
        <ProgressLine label="처리" done={row.processDone} total={row.processTotal} />
      </div>
    )
  }
  return (
    <div className="space-y-1 text-xs text-gray-500">
      <p>{lawyer}</p>
      <span className="inline-block rounded-full bg-violet-50 px-2 py-0.5 text-[11px] text-violet-700">
        {stages.symbol(stage)} {stages.label(stage)}
      </span>
    </div>
  )
}

export default function Dashboard({ onOpenOrg, onOpenOrgList }: Props) {
  const stages = useStages()
  const [data, setData] = useState<DashboardData | null>(null)
  const [error, setError] = useState<string | null>(null)
  const [busy, setBusy] = useState(false)
  const [warning, setWarning] = useState<string | null>(null)

  // 칸반 필터 (업무 구간은 요구사항 [6]으로 제거했다)
  const [lawyerFilter, setLawyerFilter] = useState('')
  const [stageFilter, setStageFilter] = useState('')
  const [statusFilter, setStatusFilter] = useState('')
  const [boardKeyword, setBoardKeyword] = useState('')
  const [expandedColumns, setExpandedColumns] = useState<Record<string, boolean>>({})

  // 최근 진행 기관 표 필터
  const [tableKeyword, setTableKeyword] = useState('')
  const [tableStage, setTableStage] = useState('')
  const [tableMj, setTableMj] = useState('')
  const [tableLawyer, setTableLawyer] = useState('')
  const [tableData, setTableData] = useState('')
  const [visible, setVisible] = useState(PAGE_SIZE)

  const reload = useCallback(async () => {
    try {
      setData(await getDashboard())
      setError(null)
    } catch (e) {
      setError((e as Error).message)
    }
  }, [])

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

  const rows = useMemo(() => data?.orgs ?? [], [data])

  const lawyers = useMemo(
    () => [...new Set(rows.map((r) => r.lawyerName).filter((n): n is string => !!n))].sort(),
    [rows],
  )
  const mjs = useMemo(
    () => [...new Set(rows.map((r) => r.mjName).filter((n): n is string => !!n))].sort(),
    [rows],
  )

  const boardRows = useMemo(() => {
    const keyword = boardKeyword.trim()
    return rows.filter((row) => {
      const stage = columnOf(row)
      if (lawyerFilter && row.lawyerName !== lawyerFilter) {
        return false
      }
      if (stageFilter && String(stage ?? '') !== stageFilter) {
        return false
      }
      // 진행상태는 코드표(STAGE의 progress)에서 온다 - 신청은 ①만, 완료는 ⑬만,
      // 나머지가 진행중이다. 채널이 없어 단계가 아예 없는 기관은 '단계 미지정'으로 따로 본다.
      if (statusFilter === 'unassigned' && row.hasChannel) {
        return false
      }
      if (
        statusFilter &&
        statusFilter !== 'unassigned' &&
        (!row.hasChannel || stages.progressOf(stage) !== statusFilter)
      ) {
        return false
      }
      if (keyword && !row.orgName.includes(keyword) && !row.orgNo.includes(keyword)) {
        return false
      }
      return true
    })
  }, [rows, lawyerFilter, stageFilter, statusFilter, boardKeyword, stages])

  /** 시안의 "정렬 기준 최근 진행순" - 칸반 카드도 최근 변동이 위로 온다. */
  const byRecent = useCallback(
    (a: DashboardOrgRow, b: DashboardOrgRow) => (b.lastChangedAt ?? 0) - (a.lastChangedAt ?? 0),
    [],
  )

  const tableRows = useMemo(() => {
    const keyword = tableKeyword.trim()
    return rows
      .filter((row) => {
        if (keyword && !row.orgName.includes(keyword) && !row.orgNo.includes(keyword)) {
          return false
        }
        if (tableStage && String(columnOf(row) ?? '') !== tableStage) {
          return false
        }
        if (tableMj && row.mjName !== tableMj) {
          return false
        }
        if (tableLawyer && row.lawyerName !== tableLawyer) {
          return false
        }
        // 요구사항 [8]: 권리확인·권리처리를 구분하지 않는다. 어느 쪽이든 한 건이라도
        // 올라와 있으면 접수로 본다.
        const received = row.reviewTotal + row.processTotal > 0
        if (tableData === 'received' && !received) {
          return false
        }
        if (tableData === 'pending' && received) {
          return false
        }
        return true
      })
      .sort(byRecent)
  }, [rows, tableKeyword, tableStage, tableMj, tableLawyer, tableData, byRecent])

  async function moveStage(row: DashboardOrgRow, stage: number) {
    if (!row.hasChannel) {
      setWarning(`${row.orgNo} ${row.orgName}\n\n채널을 먼저 생성해야 단계를 옮길 수 있습니다. 채널관리에서 채널을 생성하면 자동으로 ①신청 단계로 들어갑니다.`)
      return
    }
    if (columnOf(row) === stage) {
      return
    }
    setBusy(true)
    try {
      await updateStage(row.id, stage)
      await reload()
    } catch (e) {
      setError((e as Error).message)
    } finally {
      setBusy(false)
    }
  }

  function handleDragStart(e: React.DragEvent, row: DashboardOrgRow) {
    if (!row.hasChannel) {
      // 드래그 자체를 취소하고 경고창을 띄운다. draggable을 false로 두면 이벤트가
      // 아예 안 와서 "왜 안 움직이는지" 알려줄 기회가 없다.
      e.preventDefault()
      setWarning(`${row.orgNo} ${row.orgName}\n\n채널이 아직 없어 단계를 옮길 수 없습니다. 채널관리에서 채널을 먼저 생성해 주세요. 채널을 만들면 자동으로 ①신청 단계로 들어갑니다.`)
      return
    }
    e.dataTransfer.setData('text/plain', String(row.id))
    e.dataTransfer.effectAllowed = 'move'
  }

  function handleDrop(e: React.DragEvent, stage: number) {
    e.preventDefault()
    const id = Number(e.dataTransfer.getData('text/plain'))
    const row = rows.find((r) => r.id === id)
    if (row) {
      void moveStage(row, stage)
    }
  }

  if (error && !data) {
    return (
      <div className="flex flex-col items-center gap-3 p-8 text-sm">
        <p className="text-red-600">{error}</p>
        <button
          type="button"
          onClick={() => void reload()}
          className="rounded-md border border-gray-300 px-3 py-1.5"
        >
          다시 시도
        </button>
      </div>
    )
  }

  if (!data) {
    return <p className="p-8 text-sm text-gray-500">불러오는 중…</p>
  }

  const s = data.summary
  // 첫 칸은 '단계 미지정'(채널 미생성 기관 자리)이고, 나머지는 코드표의 단계 수만큼이다.
  const columns: (number | null)[] = [null, ...stages.numbers]

  return (
    <div className="space-y-4 p-6">
      {error && <p className="rounded-md bg-red-50 px-3 py-2 text-sm text-red-700">{error}</p>}

      <section className="rounded-lg border border-gray-200 bg-white p-5">
        <div className="flex items-start justify-between">
          <div>
            <h1 className="flex items-center gap-1.5 text-lg font-semibold">
              대시보드
              <HelpButton topic="dashboard.kpi" />
            </h1>
            <p className="text-xs text-gray-500">전체기관의 진행 상태 및 검토 현황</p>
          </div>
          <span className="text-xs text-gray-500">● 시스템 정상</span>
        </div>

        <div className="mt-4 flex flex-wrap gap-2">
          <KpiTile label="신청" value={String(s.applied)} />
          <KpiTile label="서류제출" value={String(s.docSubmitted)} />
          <KpiTile
            label="권리확인"
            value={`${s.reviewDone.toLocaleString()} / ${s.reviewTotal.toLocaleString()}`}
            sub={`${percent(s.reviewDone, s.reviewTotal)}%`}
            accent
          />
          <KpiTile
            label="권리처리"
            value={`${s.processDone.toLocaleString()} / ${s.processTotal.toLocaleString()}`}
            sub={`${percent(s.processDone, s.processTotal)}%`}
            accent
          />
          <KpiTile label="미해결 RE" value={String(s.unresolvedRe)} sub="데이터 준비 중" />
          <KpiTile label="보고서 작성" value={String(s.reportWriting)} />
          <KpiTile label="완료" value={String(s.completed)} />
        </div>
      </section>

      <section className="rounded-lg border border-gray-200 bg-white p-5">
        <h2 className="flex items-center gap-1.5 text-sm font-semibold">
          단계별 현황
          <HelpButton topic="dashboard.kanban" />
        </h2>

        {/*
          요구사항 [6]: 업무 구간 필터는 뺐다. 현재 단계와 기능이 겹친다는 지적이 있었고,
          순서도 현재 단계를 앞으로 옮겼다. 구간 구분 자체는 칸반 컬럼 색에 계속 쓰이므로
          코드표에는 그대로 남겨 둔다.
        */}
        <div className="mt-3 flex flex-wrap gap-2">
          <select
            aria-label="현재 단계"
            value={stageFilter}
            onChange={(e) => setStageFilter(e.target.value)}
            className="rounded-md border border-gray-300 px-2 py-1.5 text-sm"
          >
            <option value="">현재 단계 전체</option>
            {stages.numbers.map((n) => (
              <option key={n} value={String(n)}>{stages.symbol(n)} {stages.label(n)}</option>
            ))}
          </select>
          <select
            aria-label="담당 변호사"
            value={lawyerFilter}
            onChange={(e) => setLawyerFilter(e.target.value)}
            className="rounded-md border border-gray-300 px-2 py-1.5 text-sm"
          >
            <option value="">담당 변호사 전체</option>
            {lawyers.map((n) => (
              <option key={n} value={n}>{n}</option>
            ))}
          </select>
          <select
            aria-label="진행 상태"
            value={statusFilter}
            onChange={(e) => setStatusFilter(e.target.value)}
            className="rounded-md border border-gray-300 px-2 py-1.5 text-sm"
          >
            <option value="">진행 상태 전체</option>
            <option value="unassigned">단계 미지정</option>
            <option value="applied">신청</option>
            <option value="active">진행중</option>
            <option value="done">완료</option>
          </select>
          <input
            aria-label="기관명 검색"
            placeholder="기관명 검색"
            value={boardKeyword}
            onChange={(e) => setBoardKeyword(e.target.value)}
            className="min-w-40 flex-1 rounded-md border border-gray-300 px-2 py-1.5 text-sm"
          />
        </div>

        <div className="mt-3 flex items-center justify-between rounded-md bg-gray-50 px-3 py-2 text-xs text-gray-500">
          <span>기관 카드를 드래그하여 단계 이동이 가능합니다. 단계 이동 시 현재 단계가 자동 변경됩니다.</span>
          <span>정렬 기준 최근 진행순</span>
        </div>

        <div className="mt-3 flex gap-3 overflow-x-auto pb-2">
          {columns.map((stage) => {
            const key = stage === null ? 'unassigned' : String(stage)
            const cards = boardRows.filter((row) => columnOf(row) === stage).sort(byRecent)
            const expanded = expandedColumns[key]
            const shown = expanded ? cards : cards.slice(0, COLUMN_SIZE)
            const color = stage === null ? 'bg-gray-300' : barClass(stages.groupTone(stages.groupKey(stage)))

            return (
              <div
                key={key}
                data-testid={`column-${key}`}
                onDragOver={(e) => {
                  if (stage !== null) {
                    e.preventDefault()
                  }
                }}
                onDrop={(e) => {
                  if (stage !== null) {
                    handleDrop(e, stage)
                  }
                }}
                className="flex w-56 shrink-0 flex-col rounded-lg border border-gray-200 bg-gray-50/60"
              >
                <div className={`h-1 rounded-t-lg ${color}`} />
                <div className="flex items-center justify-between px-3 py-2">
                  <span className="text-sm font-medium">
                    {stage === null ? '단계 미지정' : `${stages.symbol(stage)} ${stages.label(stage)}`}
                  </span>
                  <span className="text-sm text-gray-500 tabular-nums">{cards.length}</span>
                </div>

                <div className="space-y-2 px-2 pb-2">
                  {shown.map((row) => (
                    <div
                      key={row.id}
                      draggable
                      onDragStart={(e) => handleDragStart(e, row)}
                      onClick={() => onOpenOrg(row.id)}
                      className={`space-y-1.5 rounded-md border border-gray-200 bg-white p-2.5 ${
                        row.hasChannel ? 'cursor-grab' : 'cursor-pointer border-dashed'
                      } ${busy ? 'opacity-60' : ''}`}
                    >
                      <p className="truncate text-sm font-medium">{row.orgName}</p>
                      <CardBody row={row} />
                      {(() => {
                        const days = daysSince(row.stageChangedAt)
                        return days === null ? null : (
                          <span className="inline-block rounded bg-gray-100 px-1.5 py-0.5 text-[11px] text-gray-500">
                            D+{days}
                          </span>
                        )
                      })()}
                    </div>
                  ))}

                  {cards.length > shown.length && (
                    <button
                      type="button"
                      onClick={() => setExpandedColumns((prev) => ({ ...prev, [key]: true }))}
                      className="w-full rounded-md py-1.5 text-xs text-gray-500 hover:bg-gray-100"
                    >
                      + {cards.length - shown.length}건 더보기
                    </button>
                  )}
                </div>
              </div>
            )
          })}
        </div>
      </section>

      <section className="rounded-lg border border-gray-200 bg-white p-5">
        <h2 className="flex items-center gap-1.5 text-sm font-semibold">
          최근 진행 기관
          <HelpButton topic="dashboard.recent" />
        </h2>
        <p className="text-xs text-gray-500">최근 변경 기준 {Math.min(visible, tableRows.length)}개 기관</p>

        <div className="mt-3 flex flex-wrap gap-2">
          <input
            aria-label="기관명/기관코드 검색"
            placeholder="기관명/기관코드 검색"
            value={tableKeyword}
            onChange={(e) => setTableKeyword(e.target.value)}
            className="min-w-48 flex-1 rounded-md border border-gray-300 px-2 py-1.5 text-sm"
          />
          <select
            aria-label="표 현재 단계"
            value={tableStage}
            onChange={(e) => setTableStage(e.target.value)}
            className="rounded-md border border-gray-300 px-2 py-1.5 text-sm"
          >
            <option value="">현재 단계 전체</option>
            {stages.numbers.map((n) => (
              <option key={n} value={String(n)}>{stages.symbol(n)} {stages.label(n)}</option>
            ))}
          </select>
          <select
            aria-label="문정원 담당"
            value={tableMj}
            onChange={(e) => setTableMj(e.target.value)}
            className="rounded-md border border-gray-300 px-2 py-1.5 text-sm"
          >
            <option value="">문정원 담당 전체</option>
            {mjs.map((n) => (
              <option key={n} value={n}>{n}</option>
            ))}
          </select>
          <select
            aria-label="표 담당변호사"
            value={tableLawyer}
            onChange={(e) => setTableLawyer(e.target.value)}
            className="rounded-md border border-gray-300 px-2 py-1.5 text-sm"
          >
            <option value="">담당변호사 전체</option>
            {lawyers.map((n) => (
              <option key={n} value={n}>{n}</option>
            ))}
          </select>
          <select
            aria-label="자료접수 상태"
            value={tableData}
            onChange={(e) => setTableData(e.target.value)}
            className="rounded-md border border-gray-300 px-2 py-1.5 text-sm"
          >
            <option value="">자료접수 상태 전체</option>
            <option value="received">접수</option>
            <option value="pending">미접수</option>
          </select>
          <button
            type="button"
            onClick={() => {
              setTableKeyword('')
              setTableStage('')
              setTableMj('')
              setTableLawyer('')
              setTableData('')
              setVisible(PAGE_SIZE)
            }}
            className="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-600 hover:bg-gray-50"
          >
            초기화
          </button>
        </div>

        <div className="mt-3 overflow-x-auto">
          <table data-testid="recent-table" className="w-full text-sm">
            <thead>
              <tr className="border-b border-gray-200 text-xs text-gray-500">
                <th className="px-2 py-2 text-left font-normal">연번</th>
                <th className="px-2 py-2 text-left font-normal">기관명</th>
                <th className="px-2 py-2 text-left font-normal">현재 단계</th>
                <th className="px-2 py-2 text-left font-normal">문정원 담당</th>
                <th className="px-2 py-2 text-left font-normal">담당 변호사</th>
                <th className="px-2 py-2 text-right font-normal">권리확인</th>
                <th className="px-2 py-2 text-right font-normal">권리처리</th>
                <th className="px-2 py-2 text-right font-normal">RE</th>
                <th className="px-2 py-2 text-right font-normal">최근 변경</th>
              </tr>
            </thead>
            <tbody>
              {tableRows.slice(0, visible).map((row) => {
                const stage = columnOf(row)
                return (
                  <tr
                    key={row.id}
                    onClick={() => onOpenOrg(row.id)}
                    className="cursor-pointer border-b border-gray-100 hover:bg-gray-50"
                  >
                    <td className="px-2 py-2 text-gray-500 tabular-nums">{row.orgNo}</td>
                    <td className="px-2 py-2 font-medium">{row.orgName}</td>
                    <td className="px-2 py-2">
                      <span className="inline-block rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-700">
                        {stage === null ? '단계 미지정' : `${stages.symbol(stage)} ${stages.label(stage)}`}
                      </span>
                    </td>
                    <td className="px-2 py-2 text-gray-600">{row.mjName ?? '-'}</td>
                    <td className="px-2 py-2 text-gray-600">{row.lawyerName ?? '-'}</td>
                    <td className="px-2 py-2 text-right tabular-nums">
                      {row.reviewDone.toLocaleString()} / {row.reviewTotal.toLocaleString()}
                    </td>
                    <td className="px-2 py-2 text-right tabular-nums">
                      {row.processDone.toLocaleString()} / {row.processTotal.toLocaleString()}
                    </td>
                    <td className="px-2 py-2 text-right tabular-nums">{row.reCount}</td>
                    <td className="px-2 py-2 text-right text-gray-500 tabular-nums">
                      {formatDate(row.lastChangedAt)}
                    </td>
                  </tr>
                )
              })}
              {tableRows.length === 0 && (
                <tr>
                  <td colSpan={9} className="px-2 py-6 text-center text-sm text-gray-500">
                    조건에 맞는 기관이 없습니다.
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </div>

        <div className="mt-3 flex items-center justify-between">
          {tableRows.length > visible ? (
            <button
              type="button"
              onClick={() => setVisible((v) => v + PAGE_SIZE)}
              className="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-600 hover:bg-gray-50"
            >
              더보기 ({tableRows.length - visible}개 남음)
            </button>
          ) : (
            <span />
          )}
          <button
            type="button"
            onClick={onOpenOrgList}
            className="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-600 hover:bg-gray-50"
          >
            사업관리에서 전체 보기 →
          </button>
        </div>
      </section>

      {warning && (
        <div className="fixed inset-0 flex items-center justify-center bg-black/30" role="alertdialog">
          <div className="w-96 rounded-lg bg-white p-5">
            <h2 className="text-base font-semibold">채널을 먼저 생성해 주세요</h2>
            <p className="mt-3 whitespace-pre-line rounded-md bg-gray-50 p-3 text-sm text-gray-700">
              {warning}
            </p>
            <div className="mt-5 flex justify-end">
              <button
                type="button"
                onClick={() => setWarning(null)}
                className="rounded-md bg-blue-600 px-3 py-1.5 text-sm text-white"
              >
                확인
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  )
}
