import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQueryClient } from '@tanstack/react-query';
import { useUrlState } from '../hooks/useUrlState';
import { usePieceList } from '../hooks/usePieces';
import { createPiece, fetchPiece, PieceDef, DriftStatus, PieceSummary } from '../api';
import { PieceEditor } from '../components/settings/PieceEditor';
import { splitPieces } from '../lib/splitPieces';
import { useBackdropClose } from '../lib/useBackdropClose';

type PieceSource = 'builtin' | 'user-custom' | 'global-custom';

/** Composite selection key so same-named builtin and custom rows are independently selectable. */
type SelectionKey = string; // `${name}::${source}`
function makeKey(name: string, source: PieceSource): SelectionKey { return `${name}::${source}`; }

function shortSha(sha: string | null): string {
  return sha ? sha.slice(0, 7) : '???????';
}

function DriftBadge({ drift }: { drift: DriftStatus }) {
  const { t } = useTranslation('pieces');
  const [open, setOpen] = useState(false);
  const ref = useRef<HTMLDivElement>(null);

  // Close popover when clicking outside.
  useEffect(() => {
    if (!open) return;
    function handleClick(e: MouseEvent) {
      if (ref.current && !ref.current.contains(e.target as Node)) {
        setOpen(false);
      }
    }
    document.addEventListener('mousedown', handleClick);
    return () => document.removeEventListener('mousedown', handleClick);
  }, [open]);

  return (
    <div ref={ref} className="relative flex-shrink-0">
      <button
        type="button"
        onClick={(e) => { e.stopPropagation(); setOpen(p => !p); }}
        title={t('drift.badgeTitle')}
        className="px-1.5 py-0.5 text-[10px] font-semibold rounded bg-amber-100 dark:bg-amber-500/15 text-amber-800 dark:text-amber-300 hover:bg-amber-200 dark:hover:bg-amber-500/25 transition-colors leading-none border border-amber-300 dark:border-amber-500/30"
      >
        updated
      </button>

      {open && (
        <div className="absolute left-0 top-full mt-1 z-50 w-52 rounded-md border border-hairline bg-canvas shadow-lg p-2.5 text-2xs text-slate-700">
          <div className="font-semibold text-slate-800 mb-1.5">{t('drift.title')}</div>
          <div className="space-y-1">
            <div className="flex items-center justify-between gap-2">
              <span className="text-slate-500">{t('drift.forkedAt')}</span>
              <code className="font-mono text-[10px] bg-slate-100 px-1 rounded">
                {shortSha(drift.forkedFromCommit)}
              </code>
            </div>
            <div className="flex items-center justify-between gap-2">
              <span className="text-slate-500">{t('drift.currentBuiltin')}</span>
              <code className="font-mono text-[10px] bg-amber-50 dark:bg-amber-500/15 px-1 rounded text-amber-800 dark:text-amber-300">
                {shortSha(drift.latestCommit)}
              </code>
            </div>
          </div>
          <p className="mt-2 text-[10px] text-slate-400 leading-snug">
            {t('drift.reviewHint')}
          </p>
        </div>
      )}
    </div>
  );
}

type ShowToast = (message: string, variant?: 'success' | 'error') => void;

interface PieceRowProps {
  p: PieceSummary;
  activeKey?: SelectionKey;
  isBuiltin: boolean;
  isAdmin: boolean;
  onSelectPiece: (name: string, source: PieceSource) => void;
  startDuplicate: (name: string, source: PieceSource) => void;
}

function PieceRow({ p, activeKey, isBuiltin, isAdmin, onSelectPiece, startDuplicate }: PieceRowProps) {
  const { t } = useTranslation('pieces');
  // For Default pieces:
  //   - admins: show Duplicate (hover)
  //   - non-admins: show Duplicate always (it's their only action)
  // For Custom pieces: existing behavior (Duplicate on hover)
  const duplicateAlwaysVisible = isBuiltin && !isAdmin;
  const src = (p.source ?? (isBuiltin ? 'builtin' : 'user-custom')) as PieceSource;
  const thisKey = makeKey(p.name, src);

  return (
    <div key={thisKey} className="group flex items-center mb-0.5 gap-1 pr-1">
      <button onClick={() => onSelectPiece(p.name, src)}
        className={`flex-1 text-left px-2 py-1 rounded text-xs transition-colors min-w-0 truncate ${
          activeKey === thisKey
            ? 'bg-accent-soft text-accent font-semibold'
            : 'text-slate-700 hover:bg-surface'
        }`}>
        {p.name}
      </button>
      {p.drift?.drifted && <DriftBadge drift={p.drift} />}
      <button
        onClick={(e) => { e.stopPropagation(); startDuplicate(p.name, src); }}
        className={`text-slate-400 hover:text-slate-700 text-xs px-1.5 transition-opacity flex-shrink-0 ${
          duplicateAlwaysVisible ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
        }`}
        title={t('row.duplicateTitle')}
      >
        &#x2398;
      </button>
    </div>
  );
}

function PiecesSidebar({
  activeKey,
  onSelectPiece,
  isAdmin,
  showToast,
}: {
  activeKey?: SelectionKey;
  onSelectPiece: (name: string, source: PieceSource) => void;
  isAdmin: boolean;
  showToast?: ShowToast;
}) {
  const { t } = useTranslation('pieces');
  const { data: pieces } = usePieceList();
  const queryClient = useQueryClient();
  const [isCreating, setIsCreating] = useState(false);
  const [newName, setNewName] = useState('');
  const [creating, setCreating] = useState(false);

  // Inline duplicate dialog state — replaces window.prompt.
  // Track both the name and source of the piece being duplicated.
  const [duplicateTarget, setDuplicateTarget] = useState<{ name: string; source: PieceSource } | null>(null);
  const [duplicateName, setDuplicateName] = useState('');
  const [duplicateError, setDuplicateError] = useState<string | null>(null);
  const [duplicating, setDuplicating] = useState(false);
  const duplicateBackdrop = useBackdropClose(() => cancelDuplicate());

  const notifyError = (label: string, err: unknown) => {
    const msg = `${label}: ${err instanceof Error ? err.message : String(err)}`;
    if (showToast) showToast(msg, 'error');
    else console.error(msg);
  };

  const handleCreate = async () => {
    const name = newName.trim();
    if (!name || creating) return;
    const defaultPiece: PieceDef = {
      name,
      description: '',
      max_movements: 25,
      initial_movement: 'execute',
      movements: [{
        name: 'execute',
        edit: true,
        persona: 'worker',
        instruction: '',
        allowed_tools: ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep'],
        default_next: 'COMPLETE',
        rules: [{ condition: '完了', next: 'COMPLETE' }],
      }],
    };
    try {
      setCreating(true);
      const { source } = await createPiece(defaultPiece);
      await queryClient.invalidateQueries({ queryKey: ['pieces'] });
      setIsCreating(false);
      setNewName('');
      onSelectPiece(name, source);
    } catch (e) {
      notifyError(t('create.failed'), e);
    } finally {
      setCreating(false);
    }
  };

  const startDuplicate = (name: string, source: PieceSource) => {
    setDuplicateTarget({ name, source });
    setDuplicateName(`${name}-copy`);
    setDuplicateError(null);
  };

  const cancelDuplicate = () => {
    setDuplicateTarget(null);
    setDuplicateName('');
    setDuplicateError(null);
  };

  const submitDuplicate = async () => {
    if (!duplicateTarget || duplicating) return;
    const name = duplicateName.trim();
    if (!name) {
      setDuplicateError(t('duplicate.nameRequired'));
      return;
    }
    if (!/^[a-z0-9-]+$/.test(name)) {
      setDuplicateError(t('duplicate.nameInvalid'));
      return;
    }
    try {
      setDuplicating(true);
      setDuplicateError(null);
      // Pass the source so we fetch from the SPECIFIC source (Fix 2).
      const { piece: pieceData } = await fetchPiece(duplicateTarget.name, duplicateTarget.source);
      const { source } = await createPiece({ ...pieceData, name });
      await queryClient.invalidateQueries({ queryKey: ['pieces'] });
      cancelDuplicate();
      onSelectPiece(name, source);
    } catch (e) {
      const msg = e instanceof Error ? e.message : 'Failed to duplicate piece';
      setDuplicateError(msg);
    } finally {
      setDuplicating(false);
    }
  };

  const { defaults, customs } = splitPieces(pieces ?? []);

  return (
    <div className="h-full overflow-y-auto border-r border-hairline bg-canvas p-3">
      {/* Default Pieces section */}
      <div className="flex items-center justify-between mb-2 px-2">
        <span className="section-label">Default Pieces</span>
      </div>
      {defaults.length === 0 && (
        <div className="px-2 text-xs text-slate-400 mb-2">(none)</div>
      )}
      {defaults.map(p => (
        <PieceRow
          key={`builtin-${p.name}`}
          p={p}
          activeKey={activeKey}
          isBuiltin={true}
          isAdmin={isAdmin}
          onSelectPiece={onSelectPiece}
          startDuplicate={startDuplicate}
        />
      ))}

      {/* Custom Pieces section */}
      <div className="flex items-center justify-between mt-3 mb-2 px-2">
        <span className="section-label">Custom Pieces</span>
        <button
          onClick={() => setIsCreating(true)}
          className="w-5 h-5 flex items-center justify-center rounded text-slate-500 hover:bg-surface-2 hover:text-slate-900 text-sm leading-none transition-colors"
          title={t('sidebar.newPieceTitle')}
        >
          +
        </button>
      </div>
      {isCreating && (
        <div className="mb-1 px-2">
          <input
            autoFocus
            value={newName}
            onChange={e => setNewName(e.target.value)}
            onKeyDown={e => {
              if (e.key === 'Enter' && newName.trim()) void handleCreate();
              if (e.key === 'Escape') { setIsCreating(false); setNewName(''); }
            }}
            disabled={creating}
            placeholder="piece-name"
            className="w-full h-7 px-2 text-xs border border-hairline rounded-md focus:outline-none focus:ring-2 focus:ring-accent-ring focus:border-accent transition-shadow"
          />
        </div>
      )}
      {customs.length === 0 && !isCreating && (
        <div className="px-2 text-xs text-slate-400">(none)</div>
      )}
      {customs.map(p => (
        <PieceRow
          key={`custom-${p.name}`}
          p={p}
          activeKey={activeKey}
          isBuiltin={false}
          isAdmin={isAdmin}
          onSelectPiece={onSelectPiece}
          startDuplicate={startDuplicate}
        />
      ))}

      {duplicateTarget && (
        <div
          role="dialog"
          aria-modal="true"
          aria-labelledby="dup-piece-label"
          className="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/30 px-4"
          {...duplicateBackdrop}
        >
          <div
            className="w-full max-w-sm rounded-lg border border-hairline bg-canvas p-4 shadow-xl"
            onClick={e => e.stopPropagation()}
          >
            <div id="dup-piece-label" className="text-[13px] font-semibold text-slate-800 mb-2">
              {t('duplicate.title', { name: duplicateTarget.name })}
            </div>
            <label className="block text-2xs font-medium text-slate-500 mb-1">{t('duplicate.nameLabel')}</label>
            <input
              autoFocus
              value={duplicateName}
              onChange={e => { setDuplicateName(e.target.value); setDuplicateError(null); }}
              onKeyDown={e => {
                if (e.key === 'Enter') void submitDuplicate();
                if (e.key === 'Escape') cancelDuplicate();
              }}
              disabled={duplicating}
              className="w-full h-8 px-2 text-xs border border-hairline rounded-md focus:outline-none focus:ring-2 focus:ring-accent-ring focus:border-accent"
            />
            {duplicateError && (
              <div role="alert" className="mt-2 text-2xs text-red-700 dark:text-red-300">{duplicateError}</div>
            )}
            <div className="mt-3 flex justify-end gap-2">
              <button
                type="button"
                onClick={cancelDuplicate}
                disabled={duplicating}
                className="px-3 py-1 text-xs rounded border border-hairline text-slate-700 hover:bg-surface-2 disabled:opacity-50"
              >
                {t('duplicate.cancel')}
              </button>
              <button
                type="button"
                onClick={() => void submitDuplicate()}
                disabled={duplicating || !duplicateName.trim()}
                className="px-3 py-1 text-xs rounded bg-accent text-white hover:bg-accent-hover disabled:opacity-50"
              >
                {duplicating ? t('duplicate.submitting') : t('duplicate.submit')}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

interface PiecesPageProps {
  showToast?: ShowToast;
  isAdmin?: boolean;
}

export function PiecesPage({ showToast, isAdmin = true }: PiecesPageProps = {}) {
  const { t } = useTranslation('pieces');
  const { urlState, setUrlState } = useUrlState();
  const piece = urlState.piece;
  // pieceSource is persisted in the URL so reload correctly restores both name+source.
  const selectedSource: PieceSource | undefined = urlState.pieceSource;

  // モバイルでは list / detail のどちらかを全幅で表示。
  // URL に piece が指定されていれば detail から、そうでなければ list から。
  const [mobileView, setMobileView] = useState<'list' | 'detail'>(piece ? 'detail' : 'list');

  // 直接 URL から piece が変わった時 (例: 別タブからシェアされた link) は detail に追従。
  useEffect(() => {
    if (piece) setMobileView('detail');
  }, [piece]);

  const handleSelectPiece = (name: string, source: PieceSource) => {
    setUrlState(prev => ({ ...prev, piece: name, pieceSource: source }));
    setMobileView('detail');
  };

  // Composite key for sidebar highlight.
  const activeKey: SelectionKey | undefined =
    piece && selectedSource ? makeKey(piece, selectedSource) : undefined;

  return (
    <div className="flex h-full">
      <div
        className={`${mobileView === 'list' ? 'block' : 'hidden'} md:block w-full md:w-52 flex-shrink-0`}
      >
        <PiecesSidebar
          activeKey={activeKey}
          onSelectPiece={handleSelectPiece}
          isAdmin={isAdmin}
          showToast={showToast}
        />
      </div>
      <div
        className={`${mobileView === 'detail' ? 'flex' : 'hidden'} md:flex flex-1 flex-col overflow-y-auto`}
      >
        {piece && (
          <button
            type="button"
            onClick={() => setMobileView('list')}
            className="md:hidden flex items-center gap-1 px-4 py-2 text-xs text-slate-600 hover:text-slate-900 border-b border-hairline"
          >
            <span aria-hidden>←</span>
            <span>{t('page.backToList')}</span>
          </button>
        )}
        <div className="flex-1 p-6">
          {piece ? (
            <PieceEditor name={piece} isAdmin={isAdmin} source={selectedSource} />
          ) : (
            <div className="text-sm text-slate-400">{t('page.selectPrompt')}</div>
          )}
        </div>
      </div>
    </div>
  );
}
