/**
 * JoinSpace — 招待リンク（/ui/invite/:token）の参加確認画面。
 *
 * App() が window.location.pathname で検出し、通常アプリの代わりに全画面表示する。
 * プレビュー API の結果で 3 分岐:
 *   - unauthorized: ログイン導線（returnTo に現在の招待 URL を付ける）
 *   - invalid: リンク無効（期限切れ・取り消し・不明）
 *   - ok: 「{spaceTitle} に {role} として参加しますか?」→ 受諾でスペースへ遷移
 *
 * 認証は /api/local 全体の requireAuth が担保する。未ログインで preview は 401 を返す。
 * spec: docs/superpowers/specs/2026-06-19-space-invite-links-design.md
 */
import { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
  fetchInvitePreview,
  acceptSpaceInvite,
  type InvitePreview,
} from '../../api';

type State =
  | { kind: 'loading' }
  | { kind: 'unauthorized' }
  | { kind: 'invalid' }
  | { kind: 'ok'; preview: InvitePreview };

function Shell({ children }: { children: React.ReactNode }) {
  return (
    <div className="h-dvh flex items-center justify-center bg-slate-50 px-4" data-testid="join-space">
      <div className="w-[min(28rem,92vw)] rounded-xl border border-hairline bg-canvas p-6 shadow-sm text-center">
        {children}
      </div>
    </div>
  );
}

export function JoinSpace({ token }: { token: string }) {
  const { t } = useTranslation('spaces');
  const [state, setState] = useState<State>({ kind: 'loading' });
  const [joining, setJoining] = useState(false);
  const [error, setError] = useState('');

  useEffect(() => {
    let cancelled = false;
    setState({ kind: 'loading' });
    fetchInvitePreview(token)
      .then((r) => {
        if (cancelled) return;
        if (r.status === 'ok') setState({ kind: 'ok', preview: r.preview });
        else if (r.status === 'unauthorized') setState({ kind: 'unauthorized' });
        else setState({ kind: 'invalid' });
      })
      .catch(() => { if (!cancelled) setState({ kind: 'invalid' }); });
    return () => { cancelled = true; };
  }, [token]);

  const handleJoin = useCallback(async () => {
    setJoining(true);
    setError('');
    try {
      const { spaceId } = await acceptSpaceInvite(token);
      // 参加先のワークスペースを開く。
      window.location.href = `/ui/?page=spaces&space=${encodeURIComponent(spaceId)}`;
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
      setJoining(false);
    }
  }, [token]);

  if (state.kind === 'loading') {
    return (
      <Shell>
        <div className="mx-auto h-8 w-8 animate-spin rounded-full border-2 border-accent border-t-transparent" />
      </Shell>
    );
  }

  if (state.kind === 'unauthorized') {
    const returnTo = encodeURIComponent(window.location.pathname);
    return (
      <Shell>
        <h1 className="mb-2 text-base font-semibold text-slate-900">{t('joinSpace.title')}</h1>
        <p className="mb-5 text-[13px] leading-relaxed text-slate-600">
          {t('joinSpace.unauthorized.body')}
        </p>
        <a
          href={`/auth/login?returnTo=${returnTo}`}
          data-testid="join-space-login"
          className="inline-block rounded-md bg-accent px-4 py-2 text-sm font-semibold text-accent-fg hover:bg-accent-deep"
        >
          {t('joinSpace.unauthorized.login')}
        </a>
      </Shell>
    );
  }

  if (state.kind === 'invalid') {
    return (
      <Shell>
        <h1 className="mb-2 text-base font-semibold text-slate-900">{t('joinSpace.invalid.title')}</h1>
        <p className="mb-5 text-[13px] leading-relaxed text-slate-600">
          {t('joinSpace.invalid.body')}
        </p>
        <a href="/ui/" className="text-[13px] text-slate-600 underline hover:text-slate-900">
          {t('joinSpace.invalid.home')}
        </a>
      </Shell>
    );
  }

  // ok
  return (
    <Shell>
      <h1 className="mb-1 text-base font-semibold text-slate-900">{t('joinSpace.title')}</h1>
      <p className="mb-5 text-[13px] leading-relaxed text-slate-600">
        <span className="font-semibold text-slate-900">{state.preview.spaceTitle}</span>{t('joinSpace.ok.joinAs.lead')}
        <span className="font-semibold text-slate-900">{t('joinSpace.ok.joinAs.roleQuoted', { role: t(`joinSpace.role.${state.preview.role}`) })}</span>
        {t('joinSpace.ok.joinAs.trail')}
      </p>
      {error && <div className="mb-3 text-[13px] text-red-600">{error}</div>}
      <div className="flex justify-center gap-2">
        <a
          href="/ui/"
          className="rounded-md border border-hairline px-4 py-2 text-sm text-slate-700 hover:bg-surface"
        >
          {t('joinSpace.ok.cancel')}
        </a>
        <button
          type="button"
          data-testid="join-space-accept"
          onClick={handleJoin}
          disabled={joining}
          className="rounded-md bg-accent px-4 py-2 text-sm font-semibold text-accent-fg hover:bg-accent-deep disabled:opacity-50"
        >
          {joining ? t('joinSpace.ok.joining') : t('joinSpace.ok.join')}
        </button>
      </div>
    </Shell>
  );
}
