import { useState } from 'react';
import * as Dialog from '@radix-ui/react-dialog';
import { useQuery, useMutation } from '@tanstack/react-query';
import { useSpaces } from '../../hooks/useSpaces';

// ── API types ─────────────────────────────────────────────────────────────

interface Candidate {
  type: 'mcp' | 'ssh';
  id: string;
  name: string;
  detail: string;
  willSkip: boolean;
  skipReason?: string;
}

interface CandidatesResponse {
  mcp: Candidate[];
  ssh: Candidate[];
}

interface TransferResult {
  copied: { type: string; id: string; name: string }[];
  skipped: { type: string; name: string; reason: string }[];
}

// ── API helpers ───────────────────────────────────────────────────────────

async function fetchCandidates(
  targetSpaceId: string,
  sourceId: string,
): Promise<CandidatesResponse> {
  const res = await fetch(
    `/api/local/spaces/${encodeURIComponent(targetSpaceId)}/transfer/candidates?sourceId=${encodeURIComponent(sourceId)}`,
    { credentials: 'include' },
  );
  if (res.status === 403) {
    const err = new Error('403');
    err.name = 'ForbiddenError';
    throw err;
  }
  if (!res.ok) throw new Error(`Failed to fetch candidates: ${res.status}`);
  return (await res.json()) as CandidatesResponse;
}

async function postTransfer(
  targetSpaceId: string,
  body: { sourceId: string; mcpServerIds: string[]; sshConnectionIds: string[] },
): Promise<TransferResult> {
  const res = await fetch(
    `/api/local/spaces/${encodeURIComponent(targetSpaceId)}/transfer`,
    {
      method: 'POST',
      credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body),
    },
  );
  if (!res.ok) {
    const text = await res.text().catch(() => '');
    let msg = `HTTP ${res.status}`;
    try { const j = JSON.parse(text); if (j?.error) msg = j.error; } catch { /* ignore */ }
    throw new Error(msg);
  }
  return (await res.json()) as TransferResult;
}

// ── Props ─────────────────────────────────────────────────────────────────

interface ImportFromWorkspaceDialogProps {
  targetSpaceId: string;
  kind: 'mcp' | 'ssh';
  open: boolean;
  onClose(): void;
  onImported(result: TransferResult): void;
}

// ── Component ─────────────────────────────────────────────────────────────

export function ImportFromWorkspaceDialog({
  targetSpaceId,
  kind,
  open,
  onClose,
  onImported,
}: ImportFromWorkspaceDialogProps) {
  const { data: allSpaces, isLoading: spacesLoading } = useSpaces();
  const [sourceId, setSourceId] = useState<string>('');
  const [checked, setChecked] = useState<Set<string>>(new Set());
  const [transferError, setTransferError] = useState<string | null>(null);

  // Exclude the target space from the source options.
  const sourceOptions = (allSpaces ?? []).filter(s => s.id !== targetSpaceId && s.status !== 'archived');

  // Fetch candidates whenever a source is selected.
  const {
    data: candidates,
    isLoading: candidatesLoading,
    error: candidatesError,
  } = useQuery({
    queryKey: ['transfer-candidates', targetSpaceId, sourceId],
    queryFn: () => fetchCandidates(targetSpaceId, sourceId),
    enabled: !!sourceId,
    staleTime: 15_000,
  });

  const isForbidden =
    candidatesError instanceof Error && candidatesError.name === 'ForbiddenError';

  // The list to display based on kind.
  const items: Candidate[] = candidates ? candidates[kind] : [];

  const transferMut = useMutation({
    mutationFn: (ids: string[]) => {
      const body =
        kind === 'mcp'
          ? { sourceId, mcpServerIds: ids, sshConnectionIds: [] }
          : { sourceId, mcpServerIds: [], sshConnectionIds: ids };
      return postTransfer(targetSpaceId, body);
    },
    onSuccess: (result) => {
      onImported(result);
      onClose();
    },
    onError: (err) => {
      setTransferError(err instanceof Error ? err.message : String(err));
    },
  });

  const handleToggle = (id: string) => {
    setChecked(prev => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  };

  const handleSourceChange = (id: string) => {
    setSourceId(id);
    setChecked(new Set());
    setTransferError(null);
  };

  const handleImport = () => {
    setTransferError(null);
    transferMut.mutate(Array.from(checked));
  };

  // Reset state when dialog opens/closes.
  const handleOpenChange = (isOpen: boolean) => {
    if (!isOpen) {
      setSourceId('');
      setChecked(new Set());
      setTransferError(null);
      onClose();
    }
  };

  const checkedCount = checked.size;
  const canImport = checkedCount > 0 && !transferMut.isPending;

  const kindLabel = kind === 'mcp' ? 'MCP サーバー' : 'SSH 接続';

  return (
    <Dialog.Root open={open} onOpenChange={handleOpenChange}>
      <Dialog.Portal>
        <Dialog.Overlay className="fixed inset-0 bg-slate-900/50 z-30" />
        <Dialog.Content
          className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-surface rounded-2xl shadow-2xl w-full overflow-auto z-40 focus:outline-none"
          style={{ maxWidth: 'min(520px, 94vw)', maxHeight: '88dvh' }}
        >
          <div className="p-5">
            {/* Header */}
            <div className="flex items-start justify-between gap-3 mb-5">
              <div>
                <Dialog.Title className="text-xl font-extrabold text-slate-900 m-0">
                  他のワークスペースから取り込む
                </Dialog.Title>
                <Dialog.Description className="mt-1 text-[13px] text-slate-500">
                  {kindLabel}を別のワークスペースからコピーします。
                </Dialog.Description>
              </div>
              <Dialog.Close asChild>
                <button
                  aria-label="閉じる"
                  className="p-1.5 rounded-lg text-slate-400 hover:text-slate-600 hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-ring"
                >
                  <svg className="w-4 h-4" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
                    <path d="M4 4l8 8M12 4l-8 8" />
                  </svg>
                </button>
              </Dialog.Close>
            </div>

            {/* Source workspace selector */}
            <div className="flex flex-col gap-1 mb-4">
              <label className="text-xs font-semibold text-slate-600">
                コピー元ワークスペース
              </label>
              {spacesLoading ? (
                <div className="text-xs text-slate-400">読み込み中…</div>
              ) : sourceOptions.length === 0 ? (
                <div className="text-xs text-slate-400">
                  他にワークスペースがありません。
                </div>
              ) : (
                <select
                  value={sourceId}
                  onChange={e => handleSourceChange(e.target.value)}
                  className="rounded-md border border-hairline bg-canvas px-3 py-2 text-sm text-slate-800 focus:outline-none focus:ring-2 focus:ring-accent-ring"
                >
                  <option value="">-- 選択してください --</option>
                  {sourceOptions.map(s => (
                    <option key={s.id} value={s.id}>
                      {s.title}
                    </option>
                  ))}
                </select>
              )}
            </div>

            {/* Candidates list */}
            {sourceId && (
              <div className="mb-4">
                {candidatesLoading && (
                  <div className="text-xs text-slate-400 py-3">候補を読み込み中…</div>
                )}

                {isForbidden && (
                  <div className="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-700">
                    このワークスペースを管理する権限がありません。
                  </div>
                )}

                {candidatesError && !isForbidden && (
                  <div className="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-700">
                    候補の取得に失敗しました: {(candidatesError as Error).message}
                  </div>
                )}

                {!candidatesLoading && !candidatesError && items.length === 0 && (
                  <div className="text-xs text-slate-400 py-3 text-center">
                    取り込める{kindLabel}がありません。
                  </div>
                )}

                {!candidatesLoading && !candidatesError && items.length > 0 && (
                  <div>
                    <div className="text-xs font-semibold text-slate-600 mb-2">{kindLabel}</div>
                    <ul className="divide-y divide-hairline border border-hairline rounded-md overflow-hidden">
                      {items.map(item => (
                        <li
                          key={item.id}
                          className={`flex items-start gap-3 px-3 py-2.5 ${
                            item.willSkip ? 'opacity-60 bg-slate-50' : 'bg-canvas'
                          }`}
                        >
                          <input
                            type="checkbox"
                            id={`candidate-${item.id}`}
                            checked={checked.has(item.id)}
                            onChange={() => handleToggle(item.id)}
                            disabled={item.willSkip}
                            className="mt-0.5 h-4 w-4 flex-shrink-0 rounded border-hairline text-accent focus:ring-accent-ring disabled:cursor-not-allowed"
                          />
                          <label
                            htmlFor={`candidate-${item.id}`}
                            className={`flex-1 min-w-0 ${item.willSkip ? 'cursor-not-allowed' : 'cursor-pointer'}`}
                          >
                            <div className="flex items-center gap-2 flex-wrap">
                              <span className="text-[13px] font-medium text-slate-800">
                                {item.name}
                              </span>
                              {item.willSkip && (
                                <span
                                  title={item.skipReason ?? 'すでに存在します'}
                                  className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-amber-100 text-amber-700 leading-none cursor-help"
                                >
                                  スキップ（既存）
                                </span>
                              )}
                            </div>
                            <div className="text-2xs text-slate-500 mt-0.5 font-mono truncate">
                              {item.detail}
                            </div>
                          </label>
                        </li>
                      ))}
                    </ul>

                    {/* Select all / deselect */}
                    {items.some(i => !i.willSkip) && (
                      <div className="mt-1.5 flex gap-3">
                        <button
                          type="button"
                          onClick={() => setChecked(new Set(items.filter(i => !i.willSkip).map(i => i.id)))}
                          className="text-2xs text-accent hover:underline"
                        >
                          すべて選択
                        </button>
                        <button
                          type="button"
                          onClick={() => setChecked(new Set())}
                          className="text-2xs text-slate-500 hover:underline"
                        >
                          選択解除
                        </button>
                      </div>
                    )}
                  </div>
                )}
              </div>
            )}

            {/* Transfer error */}
            {transferError && (
              <div className="mb-3 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-700">
                取り込みに失敗しました: {transferError}
              </div>
            )}

            {/* Footer buttons */}
            <div className="flex justify-end gap-2 pt-1">
              <Dialog.Close asChild>
                <button
                  type="button"
                  className="rounded-md border border-hairline px-4 py-2 text-sm font-semibold text-slate-700 transition-colors hover:bg-surface-2"
                >
                  キャンセル
                </button>
              </Dialog.Close>
              <button
                type="button"
                onClick={handleImport}
                disabled={!canImport}
                className="rounded-md bg-accent px-4 py-2 text-sm font-bold text-accent-fg transition-colors hover:opacity-90 disabled:opacity-50"
              >
                {transferMut.isPending
                  ? '取り込み中…'
                  : `取り込む${checkedCount > 0 ? ` (${checkedCount})` : ''}`}
              </button>
            </div>
          </div>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  );
}
