'use client';

import { useCallback, useEffect, useState } from 'react';
import Link from 'next/link';
import { useParams, useRouter } from 'next/navigation';
import {
  addIncidentIssue,
  addIncidentWorkspace,
  getIncident,
  getAdminJiraSiteUrl,
  mergeIncidentDossierInto,
  moveIncidentIssue,
  patchIncidentDossier,
  patchIncidentTitle,
  removeIncidentIssue,
  removeIncidentWorkspace,
  splitIncidentDossier,
  syncAllIncidentIssuesFromJira,
  syncIncidentIssueFromJira,
  type IncidentAnalysisStatus,
  type IncidentDetailResponse,
  type IncidentDispositionPatch,
} from '@/lib/admin-incidents';
import { useTranslation } from '@/lib/i18n';
import { jiraIssueBrowseUrl } from '@/lib/jira-ui';
import AdminModal, { AdminConfirmModal, AdminInfoModal } from '@/components/AdminModal';

function dispositionSelectValue(data: IncidentDetailResponse): IncidentAnalysisStatus | 'incident' {
  const st = data.dossier.analysis_status;
  if (st) return st;
  if (data.dossier.merged_into_dossier_id) return 'merged';
  if (data.dossier.manual_not_incident) return 'not_incident';
  const linked =
    (data.dossier.linked_workspace_count ?? 0) > 0 ||
    data.workspaces_derived.length + data.workspaces_manual.length > 0;
  if (linked) return 'analyzed';
  if (data.dossier.derive_mark_type === 'unsupported_incident') return 'unsupported';
  if (data.dossier.derive_mark_type === 'no_analysis_needed') return 'no_analysis_needed';
  return 'not_analyzed';
}

export default function AdminIncidentDetailPage() {
  const { t } = useTranslation();
  const params = useParams();
  const router = useRouter();
  const dossierId = decodeURIComponent(String(params.dossierId ?? ''));
  const [data, setData] = useState<IncidentDetailResponse | null>(null);
  const [title, setTitle] = useState('');
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [savingTitle, setSavingTitle] = useState(false);
  const [newIssue, setNewIssue] = useState('');
  const [newWs, setNewWs] = useState('');
  const [busy, setBusy] = useState(false);
  const [syncingIssue, setSyncingIssue] = useState<string | null>(null);
  const [syncAllBusy, setSyncAllBusy] = useState(false);
  const [bulkSyncMessage, setBulkSyncMessage] = useState<string | null>(null);
  const [mergeTargetId, setMergeTargetId] = useState('');
  const [mergeBusy, setMergeBusy] = useState(false);
  const [splitPick, setSplitPick] = useState<Set<string>>(new Set());
  const [splitTitle, setSplitTitle] = useState('');
  const [splitBusy, setSplitBusy] = useState(false);
  const [moveIssueKey, setMoveIssueKey] = useState<string | null>(null);
  const [moveTargetId, setMoveTargetId] = useState('');
  const [moveBusy, setMoveBusy] = useState(false);
  const [jiraBrowseBase, setJiraBrowseBase] = useState('');
  const [manualFlagBusy, setManualFlagBusy] = useState(false);
  const [confirmDialog, setConfirmDialog] = useState<{
    title: string;
    message: string;
    confirmLabel?: string;
    danger?: boolean;
    onConfirm: () => void;
  } | null>(null);
  const [infoDialog, setInfoDialog] = useState<{ title: string; message: string } | null>(null);

  const refresh = useCallback(async () => {
    if (!dossierId) return;
    setLoading(true);
    setError(null);
    try {
      const d = await getIncident(dossierId);
      setData(d);
      setTitle(d.dossier.title ?? '');
      setSplitPick(new Set());
      setSplitTitle('');
    } catch {
      setError('Failed to load or dossier not found');
      setData(null);
    } finally {
      setLoading(false);
    }
  }, [dossierId]);

  useEffect(() => {
    refresh();
  }, [refresh]);

  useEffect(() => {
    let cancelled = false;
    void getAdminJiraSiteUrl().then((b) => {
      if (!cancelled) setJiraBrowseBase(b);
    });
    return () => {
      cancelled = true;
    };
  }, []);

  async function saveTitle() {
    if (!data || !title.trim()) return;
    setSavingTitle(true);
    setError(null);
    try {
      await patchIncidentTitle(dossierId, title.trim());
      await refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Failed to save title');
    } finally {
      setSavingTitle(false);
    }
  }

  async function setDisposition(next: IncidentDispositionPatch) {
    setManualFlagBusy(true);
    setError(null);
    try {
      await patchIncidentDossier(dossierId, { disposition: next });
      await refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Failed to update disposition');
    } finally {
      setManualFlagBusy(false);
    }
  }

  async function runMergeInto() {
    const target = mergeTargetId.trim();
    if (!target || target === dossierId) return;
    setMergeBusy(true);
    setError(null);
    try {
      const r = await mergeIncidentDossierInto(dossierId, target);
      setMergeTargetId('');
      router.replace(`/admin/incidents/${encodeURIComponent(r.canonical_target_dossier_id)}`);
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Merge failed');
    } finally {
      setMergeBusy(false);
    }
  }

  function requestMergeConfirm() {
    const target = mergeTargetId.trim();
    if (!target || target === dossierId) return;
    setConfirmDialog({
      title: 'Merge dossiers',
      message:
        `Merge this dossier (${dossierId.slice(0, 8)}…) into ${target}?\n\n` +
        'This dossier will be marked merged; the list shows canonical dossiers by default. This cannot be undone from the UI.',
      danger: true,
      confirmLabel: 'Merge',
      onConfirm: () => {
        void runMergeInto();
      },
    });
  }

  async function handleAddIssue() {
    const k = newIssue.trim().toUpperCase();
    if (!k) return;
    setBusy(true);
    setError(null);
    try {
      await addIncidentIssue(dossierId, k);
      setNewIssue('');
      await refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Failed to add issue');
    } finally {
      setBusy(false);
    }
  }

  async function handleSyncIssue(issue_key: string) {
    setSyncingIssue(issue_key);
    setError(null);
    try {
      await syncIncidentIssueFromJira(dossierId, issue_key);
      await refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Jira sync failed');
    } finally {
      setSyncingIssue(null);
    }
  }

  async function runSyncAllIssuesFromJira() {
    const n = data?.issues.length ?? 0;
    if (!n) return;
    setSyncAllBusy(true);
    setError(null);
    setBulkSyncMessage(null);
    try {
      const r = await syncAllIncidentIssuesFromJira(dossierId);
      if (r.failed.length) {
        setBulkSyncMessage(
          `Done ${r.synced}/${r.total}, ${r.failed.length} failed: ` +
            r.failed
              .map((x) => `${x.issue_key} (${x.detail.slice(0, 120)})`)
              .join('; ')
        );
      } else {
        setBulkSyncMessage(`All issues updated from Jira (${r.synced})`);
      }
      await refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Bulk sync failed');
    } finally {
      setSyncAllBusy(false);
    }
  }

  function requestSyncAllConfirm() {
    const n = data?.issues.length ?? 0;
    if (!n) return;
    setConfirmDialog({
      title: 'Sync all from Jira',
      message: `Sync all ${n} linked issues from Jira?\n(Sequential requests; time grows with count.)`,
      confirmLabel: 'Sync all',
      onConfirm: () => {
        void runSyncAllIssuesFromJira();
      },
    });
  }

  async function doRemoveIssue(issue_key: string) {
    setBusy(true);
    setError(null);
    try {
      await removeIncidentIssue(dossierId, issue_key);
      await refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Remove failed');
    } finally {
      setBusy(false);
    }
  }

  function requestRemoveIssue(issue_key: string) {
    setConfirmDialog({
      title: 'Remove issue link',
      message: `Remove issue link ${issue_key}?`,
      danger: true,
      confirmLabel: 'Remove',
      onConfirm: () => {
        void doRemoveIssue(issue_key);
      },
    });
  }

  async function handleAddWorkspace() {
    const w = newWs.trim();
    if (!w) return;
    setBusy(true);
    setError(null);
    try {
      await addIncidentWorkspace(dossierId, w);
      setNewWs('');
      await refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Failed to add workspace');
    } finally {
      setBusy(false);
    }
  }

  async function doRemoveWorkspace(workspace_id: string) {
    setBusy(true);
    setError(null);
    try {
      await removeIncidentWorkspace(dossierId, workspace_id);
      await refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Remove failed');
    } finally {
      setBusy(false);
    }
  }

  function requestRemoveWorkspace(workspace_id: string) {
    setConfirmDialog({
      title: 'Remove workspace link',
      message: `Remove manual workspace link ${workspace_id}?`,
      danger: true,
      confirmLabel: 'Remove',
      onConfirm: () => {
        void doRemoveWorkspace(workspace_id);
      },
    });
  }

  async function runSplitSelected() {
    if (!data) return;
    const keys = [...splitPick];
    if (!keys.length || keys.length >= data.issues.length) return;
    setSplitBusy(true);
    setError(null);
    try {
      await splitIncidentDossier(dossierId, keys, splitTitle.trim() || undefined);
      await refresh();
      setSplitPick(new Set());
      setSplitTitle('');
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Split failed');
    } finally {
      setSplitBusy(false);
    }
  }

  function requestSplitConfirm() {
    if (!data) return;
    const keys = [...splitPick];
    if (!keys.length || keys.length >= data.issues.length) return;
    setConfirmDialog({
      title: 'Split dossier',
      message: `Move ${keys.length} selected issue(s) into a new dossier?\n\n${keys.join(', ')}\n\nAt least one issue stays on this dossier.`,
      confirmLabel: 'Split',
      onConfirm: () => {
        void runSplitSelected();
      },
    });
  }

  async function submitMoveIssue() {
    if (!moveIssueKey || !moveTargetId.trim()) return;
    setMoveBusy(true);
    setError(null);
    try {
      await moveIncidentIssue(dossierId, moveIssueKey, moveTargetId.trim());
      setMoveIssueKey(null);
      setMoveTargetId('');
      await refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Move failed');
    } finally {
      setMoveBusy(false);
    }
  }

  const structureFrozen =
    Boolean(data?.dossier.merged_into_dossier_id) ||
    (data?.dossier.status ?? 'active') !== 'active';
  const canSplit = Boolean(!structureFrozen && data && data.issues.length >= 2);
  const issueRowBusy =
    busy ||
    mergeBusy ||
    splitBusy ||
    moveBusy ||
    syncAllBusy ||
    syncingIssue !== null;

  if (!dossierId) {
    return <div style={{ padding: 24 }}>Invalid id</div>;
  }

  return (
    <div style={{ paddingTop: 8 }}>
      <div style={{ marginBottom: 20 }}>
        <button
          type="button"
          onClick={() => router.push('/admin/incidents')}
          style={{
            border: 'none',
            background: 'none',
            color: '#4f46e5',
            cursor: 'pointer',
            fontSize: 14,
            padding: 0,
            marginBottom: 12,
          }}
        >
          ← Back to dashboard
        </button>
        <h1 style={{ fontSize: 22, fontWeight: 700, margin: 0, color: '#1a1a2e' }}>Incident dossier</h1>
      </div>

      {loading && <p style={{ color: '#6b7280' }}>Loading…</p>}
      {error && (
        <div
          style={{
            padding: 12,
            borderRadius: 8,
            background: '#fef2f2',
            color: '#991b1b',
            marginBottom: 16,
            fontSize: 14,
          }}
        >
          {error}
        </div>
      )}

      {!loading && data && (
        <>
          {data.dossier.merged_into_dossier_id && (
            <div
              style={{
                padding: 12,
                borderRadius: 8,
                background: '#fffbeb',
                color: '#92400e',
                marginBottom: 16,
                fontSize: 14,
              }}
            >
              This dossier was merged into{' '}
              <Link
                href={`/admin/incidents/${encodeURIComponent(data.dossier.merged_into_dossier_id)}`}
                style={{ fontWeight: 600, color: '#b45309' }}
              >
                {data.dossier.merged_into_dossier_id}
              </Link>
            </div>
          )}

          <section
            style={{
              background: '#fff',
              borderRadius: 12,
              border: '1px solid #f0f0f5',
              padding: 18,
              marginBottom: 18,
            }}
          >
            <h2 style={{ fontSize: 15, fontWeight: 600, margin: '0 0 12px', color: '#374151' }}>
              Title
            </h2>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center' }}>
              <input
                value={title}
                onChange={(e) => setTitle(e.target.value)}
                style={{
                  flex: '1 1 280px',
                  padding: '10px 12px',
                  borderRadius: 8,
                  border: '1px solid #e5e7eb',
                  fontSize: 14,
                }}
              />
              <button
                type="button"
                disabled={savingTitle || title.trim() === (data.dossier.title || '').trim()}
                onClick={saveTitle}
                style={{
                  padding: '10px 18px',
                  borderRadius: 8,
                  border: 'none',
                  background: 'linear-gradient(135deg, #5c5fbc, #7c7fdc)',
                  color: '#fff',
                  fontWeight: 600,
                  cursor: savingTitle ? 'wait' : 'pointer',
                  opacity: savingTitle ? 0.7 : 1,
                }}
              >
                {savingTitle ? 'Saving…' : 'Save title'}
              </button>
            </div>
            <div style={{ fontSize: 12, color: '#9ca3af', marginTop: 8 }}>ID: {data.dossier.id}</div>
          </section>

          {!data.dossier.merged_into_dossier_id && (
            <section
              style={{
                background: '#fff',
                borderRadius: 12,
                border: '1px solid #f0f0f5',
                padding: '14px 18px',
                marginBottom: 18,
              }}
            >
              <label style={{ display: 'block', fontSize: 13, fontWeight: 600, color: '#374151', marginBottom: 8 }}>
                {t('admin.incidents.disposition.label')}
              </label>
              {(() => {
                const current = dispositionSelectValue(data);
                const readOnly = current === 'merged' || current === 'analyzed';
                const selectValue =
                  current === 'not_analyzed'
                    ? 'incident'
                    : current === 'not_incident' ||
                        current === 'unsupported' ||
                        current === 'no_analysis_needed'
                      ? current
                      : current;
                return (
                  <>
                    <select
                      value={selectValue}
                      disabled={manualFlagBusy || readOnly}
                      onChange={(e) => {
                        const v = e.target.value;
                        if (v === 'analyzed' || v === 'merged') return;
                        void setDisposition(v as IncidentDispositionPatch);
                      }}
                      style={{
                        width: '100%',
                        maxWidth: 420,
                        padding: '10px 12px',
                        borderRadius: 8,
                        border: '1px solid #e5e7eb',
                        fontSize: 14,
                        background: readOnly ? '#f9fafb' : '#fff',
                      }}
                    >
                      <option value="not_incident">{t('admin.incidents.analysisStatus.not_incident')}</option>
                      <option value="unsupported">{t('admin.incidents.analysisStatus.unsupported')}</option>
                      <option value="no_analysis_needed">
                        {t('admin.incidents.analysisStatus.no_analysis_needed')}
                      </option>
                      <option value="incident">{t('admin.incidents.analysisStatus.not_analyzed')}</option>
                      {current === 'analyzed' ? (
                        <option value="analyzed">{t('admin.incidents.analysisStatus.analyzed')}</option>
                      ) : null}
                      {current === 'merged' ? (
                        <option value="merged">{t('admin.incidents.analysisStatus.merged')}</option>
                      ) : null}
                    </select>
                    {readOnly ? (
                      <p style={{ margin: '8px 0 0', fontSize: 12, color: '#9ca3af', lineHeight: 1.5 }}>
                        {t('admin.incidents.disposition.readOnlyHint')}
                      </p>
                    ) : null}
                  </>
                );
              })()}
              {data.dossier.manual_not_incident && data.dossier.manual_not_incident_at ? (
                <div style={{ marginTop: 8, fontSize: 12, color: '#9ca3af' }}>
                  Marked at {data.dossier.manual_not_incident_at}
                  {data.dossier.manual_not_incident_by
                    ? ` · ${data.dossier.manual_not_incident_by}`
                    : ''}
                </div>
              ) : null}
            </section>
          )}

          {!data.dossier.merged_into_dossier_id && (
            <section
              style={{
                background: '#fff7ed',
                borderRadius: 12,
                border: '1px solid #fed7aa',
                padding: 18,
                marginBottom: 18,
              }}
            >
              <h2 style={{ fontSize: 15, fontWeight: 600, margin: '0 0 8px', color: '#9a3412' }}>
                Merge into another dossier
              </h2>
              <p style={{ margin: '0 0 12px', fontSize: 13, color: '#c2410c', lineHeight: 1.5 }}>
                Merge <strong>this</strong> dossier into the target you enter (if the target was merged, rows roll up to its canonical dossier).
                Linked <strong>issues</strong> and <strong>manually linked workspaces</strong> move to the canonical dossier; duplicates are deduped.
                Derived workspaces (from issue links) follow their issues to the canonical dossier.
              </p>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center' }}>
                <input
                  type="text"
                  placeholder="Target dossier UUID (canonical id)"
                  value={mergeTargetId}
                  onChange={(e) => setMergeTargetId(e.target.value)}
                  style={{
                    flex: '1 1 260px',
                    padding: '10px 12px',
                    borderRadius: 8,
                    border: '1px solid #fdba74',
                    fontSize: 13,
                    fontFamily: 'ui-monospace, monospace',
                  }}
                />
                <button
                  type="button"
                  disabled={
                    mergeBusy ||
                    busy ||
                    !mergeTargetId.trim() ||
                    mergeTargetId.trim() === dossierId
                  }
                  onClick={requestMergeConfirm}
                  style={{
                    padding: '10px 18px',
                    borderRadius: 8,
                    border: '1px solid #ea580c',
                    background: '#fff',
                    color: '#c2410c',
                    fontWeight: 600,
                    fontSize: 13,
                    cursor: mergeBusy ? 'wait' : 'pointer',
                    opacity: mergeBusy ? 0.7 : 1,
                  }}
                >
                  {mergeBusy ? 'Merging…' : 'Confirm merge'}
                </button>
              </div>
            </section>
          )}

          <section
            style={{
              background: '#fff',
              borderRadius: 12,
              border: '1px solid #f0f0f5',
              padding: 18,
              marginBottom: 18,
            }}
          >
            <>
              <div
                style={{
                  display: 'flex',
                  flexWrap: 'wrap',
                  alignItems: 'center',
                  justifyContent: 'space-between',
                  gap: 12,
                  marginBottom: 12,
                }}
              >
                <h2 style={{ fontSize: 15, fontWeight: 600, margin: 0, color: '#374151' }}>
                  Linked issues
                </h2>
                <button
                  type="button"
                  disabled={!data.issues.length || issueRowBusy}
                  onClick={requestSyncAllConfirm}
                  style={{
                    padding: '8px 14px',
                    borderRadius: 8,
                    border: '1px solid #a7f3d0',
                    background: '#fff',
                    color: '#047857',
                    fontWeight: 600,
                    fontSize: 13,
                    cursor:
                      !data.issues.length || issueRowBusy ? 'not-allowed' : 'pointer',
                    opacity: !data.issues.length || issueRowBusy ? 0.55 : 1,
                  }}
                >
                  {syncAllBusy ? 'Syncing all…' : 'Sync all from Jira'}
                </button>
              </div>
              {!structureFrozen && (
                <p style={{ fontSize: 12, color: '#6b7280', margin: '0 0 14px', lineHeight: 1.55 }}>
                  Split mis-grouped tickets or move an issue to another dossier.
                </p>
              )}
              {bulkSyncMessage && (
                <div
                  style={{
                    padding: 10,
                    borderRadius: 8,
                    background: '#eff6ff',
                    color: '#1e40af',
                    marginBottom: 12,
                    fontSize: 12,
                    lineHeight: 1.5,
                  }}
                >
                  {bulkSyncMessage}
                </div>
              )}
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 14 }}>
                <input
                  placeholder="e.g. TCOC-12345"
                  value={newIssue}
                  onChange={(e) => setNewIssue(e.target.value)}
                  style={{
                    flex: '1 1 200px',
                    padding: '10px 12px',
                    borderRadius: 8,
                    border: '1px solid #e5e7eb',
                    fontSize: 14,
                  }}
                />
                <button
                  type="button"
                  disabled={busy || structureFrozen}
                  onClick={handleAddIssue}
                  style={{
                    padding: '10px 16px',
                    borderRadius: 8,
                    border: '1px solid #e5e7eb',
                    background: '#fff',
                    cursor: busy ? 'wait' : 'pointer',
                  }}
                >
                  Add
                </button>
              </div>
              <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
                <thead>
                  <tr style={{ borderBottom: '1px solid #ececf4', textAlign: 'left' }}>
                    {canSplit && (
                      <th style={{ padding: '8px 6px', color: '#6b7280', width: 44 }}>
                        Split
                      </th>
                    )}
                    <th style={{ padding: '8px 6px', color: '#6b7280' }}>Issue</th>
                    <th style={{ padding: '8px 6px', color: '#6b7280' }}>Summary</th>
                    <th style={{ padding: '8px 6px', color: '#6b7280' }}>Status</th>
                    <th style={{ padding: '8px 6px', color: '#6b7280' }}>Jira incident</th>
                    <th style={{ padding: '8px 6px', color: '#6b7280' }}>Type</th>
                    <th
                      style={{
                        padding: '8px 0',
                        color: '#6b7280',
                        textAlign: 'right',
                        whiteSpace: 'nowrap',
                      }}
                    >
                      Actions
                    </th>
                  </tr>
                </thead>
                <tbody>
                  {data.issues.map((row) => {
                    const jiraHref = jiraIssueBrowseUrl(row.issue_key, jiraBrowseBase);
                    return (
                    <tr key={row.issue_key} style={{ borderBottom: '1px solid #f3f4f6' }}>
                      {canSplit && (
                        <td style={{ padding: '10px 6px', verticalAlign: 'top' }}>
                          <input
                            type="checkbox"
                            checked={splitPick.has(row.issue_key)}
                            disabled={structureFrozen || issueRowBusy || splitBusy}
                            onChange={() => {
                              setSplitPick((prev) => {
                                const n = new Set(prev);
                                if (n.has(row.issue_key)) n.delete(row.issue_key);
                                else n.add(row.issue_key);
                                return n;
                              });
                            }}
                            aria-label={`Include ${row.issue_key} in split`}
                          />
                        </td>
                      )}
                      <td style={{ padding: '10px 6px', fontWeight: 600, verticalAlign: 'top' }}>
                        {jiraHref ? (
                          <a
                            href={jiraHref}
                            target="_blank"
                            rel="noopener noreferrer"
                            style={{ color: '#4f46e5', textDecoration: 'none', fontWeight: 600 }}
                          >
                            {row.issue_key}
                          </a>
                        ) : (
                          row.issue_key
                        )}
                      </td>
                      <td style={{ padding: '10px 6px', color: '#4b5563', verticalAlign: 'top' }}>
                        {row.summary || '—'}
                      </td>
                      <td style={{ padding: '10px 6px', verticalAlign: 'top' }}>
                        {row.status || '—'}
                      </td>
                      <td
                        style={{ padding: '10px 6px', verticalAlign: 'top' }}
                        title={row.jira_incident_flag_raw_json || undefined}
                      >
                        {row.jira_incident_flag === 'yes'
                          ? 'Yes'
                          : row.jira_incident_flag === 'no'
                            ? 'No'
                            : row.jira_incident_flag === 'unknown'
                              ? 'Unknown'
                              : '—'}
                      </td>
                      <td style={{ padding: '10px 6px', verticalAlign: 'top' }}>
                        {row.issue_type || '—'}
                      </td>
                      <td
                        style={{
                          padding: '10px 0',
                          textAlign: 'right',
                          whiteSpace: 'nowrap',
                          verticalAlign: 'top',
                        }}
                      >
                        <div
                          style={{
                            display: 'flex',
                            flexWrap: 'wrap',
                            gap: 8,
                            justifyContent: 'flex-end',
                            alignItems: 'center',
                          }}
                        >
                          <button
                            type="button"
                            disabled={issueRowBusy}
                            onClick={() => handleSyncIssue(row.issue_key)}
                            style={{
                              border: 'none',
                              background: 'none',
                              color: '#4f46e5',
                              cursor: issueRowBusy ? 'wait' : 'pointer',
                              fontSize: 13,
                            }}
                          >
                            {syncingIssue === row.issue_key ? 'Syncing…' : 'Sync'}
                          </button>
                          {!structureFrozen && (
                            <button
                              type="button"
                              disabled={issueRowBusy}
                              onClick={() => {
                                setMoveIssueKey(row.issue_key);
                                setMoveTargetId('');
                              }}
                              style={{
                                border: 'none',
                                background: 'none',
                                color: '#0369a1',
                                cursor: issueRowBusy ? 'wait' : 'pointer',
                                fontSize: 13,
                              }}
                            >
                              Move…
                            </button>
                          )}
                          <button
                            type="button"
                            disabled={issueRowBusy}
                            onClick={() => requestRemoveIssue(row.issue_key)}
                            style={{
                              border: 'none',
                              background: 'none',
                              color: '#dc2626',
                              cursor: issueRowBusy ? 'wait' : 'pointer',
                              fontSize: 13,
                            }}
                          >
                            Remove
                          </button>
                        </div>
                      </td>
                    </tr>
                    );
                  })}
                </tbody>
              </table>
              {canSplit && (
                <div
                  style={{
                    marginTop: 16,
                    paddingTop: 14,
                    borderTop: '1px solid #f3f4f6',
                  }}
                >
                  <div style={{ fontSize: 13, fontWeight: 600, color: '#374151', marginBottom: 8 }}>
                    Split into new dossier
                  </div>
                  <p style={{ fontSize: 12, color: '#6b7280', margin: '0 0 10px' }}>
                    Select tickets via Split column; source dossier must keep at least one issue.
                  </p>
                  <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center' }}>
                    <input
                      type="text"
                      placeholder="Optional title for new dossier"
                      value={splitTitle}
                      onChange={(e) => setSplitTitle(e.target.value)}
                      style={{
                        flex: '1 1 220px',
                        padding: '10px 12px',
                        borderRadius: 8,
                        border: '1px solid #e5e7eb',
                        fontSize: 13,
                      }}
                    />
                    <button
                      type="button"
                      disabled={
                        splitBusy ||
                        issueRowBusy ||
                        splitPick.size < 1 ||
                        splitPick.size >= data.issues.length
                      }
                      onClick={requestSplitConfirm}
                      style={{
                        padding: '10px 16px',
                        borderRadius: 8,
                        border: '1px solid #818cf8',
                        background: '#eef2ff',
                        color: '#4338ca',
                        fontWeight: 600,
                        fontSize: 13,
                        cursor:
                          splitBusy ||
                          issueRowBusy ||
                          splitPick.size < 1 ||
                          splitPick.size >= data.issues.length
                            ? 'not-allowed'
                            : 'pointer',
                        opacity:
                          splitBusy ||
                          issueRowBusy ||
                          splitPick.size < 1 ||
                          splitPick.size >= data.issues.length
                            ? 0.55
                            : 1,
                      }}
                    >
                      {splitBusy ? 'Splitting…' : 'Split selected'}
                    </button>
                  </div>
                </div>
              )}
            {data.issues.length === 0 && (
              <p style={{ color: '#9ca3af', fontSize: 14 }}>No linked issues</p>
            )}
            </>
          </section>

          <section
            style={{
              background: '#fff',
              borderRadius: 12,
              border: '1px solid #f0f0f5',
              padding: 18,
              marginBottom: 18,
            }}
          >
            <h2 style={{ fontSize: 15, fontWeight: 600, margin: '0 0 8px', color: '#374151' }}>
              Workspaces (derived from issues)
            </h2>
            <p style={{ fontSize: 12, color: '#9ca3af', margin: '0 0 14px' }}>
              These workspaces are linked via workspace_jira_links to issues on this dossier.
            </p>
            <ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
              {data.workspaces_derived.map((w) => (
                <li
                  key={w.workspace_id}
                  style={{
                    padding: '10px 0',
                    borderBottom: '1px solid #f3f4f6',
                    display: 'flex',
                    flexWrap: 'wrap',
                    gap: 8,
                    alignItems: 'center',
                    justifyContent: 'space-between',
                  }}
                >
                  <div>
                    <Link
                      href={`/workspace/${encodeURIComponent(w.workspace_id)}`}
                      style={{ fontWeight: 600, color: '#4f46e5', textDecoration: 'none' }}
                    >
                      {w.workspace_name}
                    </Link>
                    <div style={{ fontSize: 12, color: '#9ca3af' }}>
                      {w.workspace_id} · issues {w.issue_keys.join(', ')}
                    </div>
                  </div>
                  <button
                    type="button"
                    disabled={busy}
                    onClick={() =>
                      setInfoDialog({
                        title: 'Derived workspace links',
                        message:
                          'Derived links are built from issues on this dossier and Jira sources on each workspace. To remove one, delete the Jira source in the workspace or remove the issue from this dossier.',
                      })
                    }
                    style={{
                      border: 'none',
                      background: '#f3f4f6',
                      color: '#6b7280',
                      padding: '6px 10px',
                      borderRadius: 6,
                      fontSize: 12,
                      cursor: 'pointer',
                    }}
                  >
                    Note
                  </button>
                </li>
              ))}
            </ul>
            {data.workspaces_derived.length === 0 && (
              <p style={{ color: '#9ca3af', fontSize: 14 }}>No derived workspaces</p>
            )}
          </section>

          <section
            style={{
              background: '#fff',
              borderRadius: 12,
              border: '1px solid #f0f0f5',
              padding: 18,
            }}
          >
            <h2 style={{ fontSize: 15, fontWeight: 600, margin: '0 0 12px', color: '#374151' }}>
              Workspaces (manual links)
            </h2>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 14 }}>
              <input
                placeholder="Workspace id, e.g. ws-…"
                value={newWs}
                onChange={(e) => setNewWs(e.target.value)}
                style={{
                  flex: '1 1 240px',
                  padding: '10px 12px',
                  borderRadius: 8,
                  border: '1px solid #e5e7eb',
                  fontSize: 14,
                }}
              />
              <button
                type="button"
                disabled={busy}
                onClick={handleAddWorkspace}
                style={{
                  padding: '10px 16px',
                  borderRadius: 8,
                  border: '1px solid #e5e7eb',
                  background: '#fff',
                  cursor: busy ? 'wait' : 'pointer',
                }}
              >
                Add
              </button>
            </div>
            <ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
              {data.workspaces_manual.map((w) => (
                <li
                  key={w.workspace_id}
                  style={{
                    padding: '10px 0',
                    borderBottom: '1px solid #f3f4f6',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'space-between',
                    flexWrap: 'wrap',
                    gap: 8,
                  }}
                >
                  <div>
                    <Link
                      href={`/workspace/${encodeURIComponent(w.workspace_id)}`}
                      style={{ fontWeight: 600, color: '#4f46e5', textDecoration: 'none' }}
                    >
                      {w.workspace_name}
                    </Link>
                    <div style={{ fontSize: 12, color: '#9ca3af' }}>
                      {w.workspace_id}
                      {w.created_at && ` · ${w.created_at}`}
                    </div>
                  </div>
                  <button
                    type="button"
                    disabled={busy}
                    onClick={() => requestRemoveWorkspace(w.workspace_id)}
                    style={{
                      border: 'none',
                      background: 'none',
                      color: '#dc2626',
                      cursor: 'pointer',
                      fontSize: 13,
                    }}
                  >
                    Remove
                  </button>
                </li>
              ))}
            </ul>
            {data.workspaces_manual.length === 0 && (
              <p style={{ color: '#9ca3af', fontSize: 14 }}>No manual links</p>
            )}
          </section>
        </>
      )}
      <AdminModal
        opened={moveIssueKey !== null}
        onClose={() => {
          if (!moveBusy) {
            setMoveIssueKey(null);
            setMoveTargetId('');
          }
        }}
        title={moveIssueKey ? `Move ${moveIssueKey}` : 'Move issue'}
        width={460}
      >
        <p style={{ margin: '0 0 12px', fontSize: 13, color: '#6b7280', lineHeight: 1.5 }}>
          Target dossier UUID (canonical root). The issue is removed from this dossier and linked on the target.
        </p>
        <input
          type="text"
          value={moveTargetId}
          onChange={(e) => setMoveTargetId(e.target.value)}
          placeholder="e.g. a1b2c3d4-..."
          autoFocus
          style={{
            width: '100%',
            boxSizing: 'border-box',
            padding: '10px 12px',
            borderRadius: 8,
            border: '1px solid #e5e7eb',
            fontSize: 13,
            fontFamily: 'ui-monospace, monospace',
            marginBottom: 18,
          }}
        />
        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
          <button
            type="button"
            disabled={moveBusy}
            onClick={() => {
              setMoveIssueKey(null);
              setMoveTargetId('');
            }}
            style={{
              padding: '8px 16px',
              borderRadius: 8,
              border: '1px solid #e5e7eb',
              background: '#fff',
              fontWeight: 600,
              cursor: moveBusy ? 'wait' : 'pointer',
            }}
          >
            Cancel
          </button>
          <button
            type="button"
            disabled={moveBusy || !moveTargetId.trim()}
            onClick={() => void submitMoveIssue()}
            style={{
              padding: '8px 16px',
              borderRadius: 8,
              border: 'none',
              background: 'linear-gradient(135deg, #5c5fbc, #7c7fdc)',
              color: '#fff',
              fontWeight: 600,
              cursor:
                moveBusy || !moveTargetId.trim() ? 'not-allowed' : 'pointer',
              opacity: moveBusy || !moveTargetId.trim() ? 0.65 : 1,
            }}
          >
            {moveBusy ? 'Moving…' : 'Move'}
          </button>
        </div>
      </AdminModal>

      <AdminConfirmModal
        opened={confirmDialog !== null}
        onClose={() => setConfirmDialog(null)}
        title={confirmDialog?.title ?? ''}
        message={confirmDialog?.message ?? ''}
        confirmLabel={confirmDialog?.confirmLabel}
        danger={confirmDialog?.danger}
        onConfirm={confirmDialog?.onConfirm ?? (() => {})}
      />
      <AdminInfoModal
        opened={infoDialog !== null}
        onClose={() => setInfoDialog(null)}
        title={infoDialog?.title ?? ''}
        message={infoDialog?.message ?? ''}
      />
    </div>
  );
}
