import { useCallback, useEffect, useState } from 'react';
import { useLocalStorageState } from '../../hooks/useLocalStorageState';
import {
  isNotificationSupported,
  getNotificationPermission,
  requestNotificationPermission,
  createNotification,
  buildNotificationOptions,
  DEFAULT_NOTIFY_EVENTS,
  type NotifyEventType,
  type NotifyEventSettings,
} from '../../lib/notifications';
import {
  isPushSupported,
  isStandalonePWA,
  isIOS,
  subscribePush,
  unsubscribePush,
  getCurrentPushSubscription,
} from '../../lib/push-subscribe';
import {
  fetchVapidPublicKey,
  listPushSubscriptions,
  postPushSubscription,
  deletePushSubscription as apiDeletePushSubscription,
  fetchNotificationPrefs,
  updateNotificationPrefs,
  migrateLocalStoragePrefs,
  postTestNotification,
  type PushSubscriptionPublic,
  type NotificationPrefsDTO,
} from '../../api';
import { HelpText } from './HelpText';

const EVENT_LABELS: Array<{ key: NotifyEventType; label: string }> = [
  { key: 'running', label: 'タスク開始 (running)' },
  { key: 'succeeded', label: 'タスク完了 (succeeded)' },
  { key: 'failed', label: 'タスク失敗 (failed / aborted)' },
  { key: 'waiting_human', label: 'ユーザー回答待ち (waiting_human)' },
];

type PushAvailability =
  | { kind: 'supported' }
  | { kind: 'needs-pwa-ios' }
  | { kind: 'unsupported'; reason: string };

function evaluatePushAvailability(): PushAvailability {
  if (!isPushSupported()) {
    return { kind: 'unsupported', reason: 'お使いのブラウザは Web Push API に対応していません' };
  }
  if (isIOS() && !isStandalonePWA()) {
    return { kind: 'needs-pwa-ios' };
  }
  return { kind: 'supported' };
}

export function NotificationsForm() {
  const supported = isNotificationSupported();
  const [permission, setPermission] = useState<NotificationPermission | 'unsupported'>(
    getNotificationPermission(),
  );
  // V1 localStorage (legacy fallback; server prefs override once loaded).
  const [v1Enabled, setV1Enabled] = useLocalStorageState<boolean>('notify.enabled', true);
  const [v1Events, setV1Events] = useLocalStorageState<NotifyEventSettings>(
    'notify.events',
    DEFAULT_NOTIFY_EVENTS,
  );

  // V2 server-side state — null until first fetch / not configured.
  const [serverPrefs, setServerPrefs] = useState<NotificationPrefsDTO | null>(null);
  const [subscriptions, setSubscriptions] = useState<PushSubscriptionPublic[]>([]);
  const [pushAvailable] = useState<PushAvailability>(() => evaluatePushAvailability());
  const [hasLocalSubscription, setHasLocalSubscription] = useState<boolean>(false);
  const [pushFatal, setPushFatal] = useState<string | null>(null);
  const [busy, setBusy] = useState<boolean>(false);

  // Effective prefs come from the server when available; otherwise fall back
  // to localStorage so V1-only deployments keep working unchanged.
  const enabled = serverPrefs?.enabled ?? v1Enabled;
  const events: NotifyEventSettings = serverPrefs
    ? serverPrefs.events
    : v1Events;
  const includeDetails = serverPrefs?.includeDetails ?? false;

  const setEnabled = useCallback(
    async (next: boolean) => {
      setV1Enabled(next);
      if (serverPrefs) {
        const updated = await updateNotificationPrefs({ enabled: next });
        setServerPrefs(updated);
      }
    },
    [serverPrefs, setV1Enabled],
  );

  const toggleEvent = useCallback(
    async (key: NotifyEventType) => {
      const nextValue = !events[key];
      setV1Events(prev => ({ ...prev, [key]: nextValue }));
      if (serverPrefs) {
        const updated = await updateNotificationPrefs({ events: { [key]: nextValue } });
        setServerPrefs(updated);
      }
    },
    [events, serverPrefs, setV1Events],
  );

  const setIncludeDetails = useCallback(
    async (next: boolean) => {
      if (!serverPrefs) return;
      const updated = await updateNotificationPrefs({ includeDetails: next });
      setServerPrefs(updated);
    },
    [serverPrefs],
  );

  // First-load: hydrate server prefs, migrate from localStorage if needed.
  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const prefs = await fetchNotificationPrefs();
        if (cancelled) return;
        if (!prefs.v1Migrated) {
          // One-shot import from localStorage. 409 means another tab beat us;
          // in that case just adopt the server state.
          const result = await migrateLocalStoragePrefs({
            enabled: v1Enabled,
            events: v1Events,
            includeDetails: false,
          });
          if (cancelled) return;
          if ('alreadyMigrated' in result) {
            setServerPrefs(prefs);
          } else {
            setServerPrefs(result.prefs);
          }
        } else {
          setServerPrefs(prefs);
        }
      } catch (err) {
        // /api/notifications/preferences not reachable → keep V1 fallback.
        // Logged for diagnostics; the UI remains usable.
        console.warn('[notifications] failed to load server prefs', err);
      }
    })();
    return () => { cancelled = true; };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Subscriptions list — re-fetch on mount and after subscribe/unsubscribe.
  const refreshSubscriptions = useCallback(async () => {
    try {
      const list = await listPushSubscriptions();
      setSubscriptions(list);
    } catch (err) {
      console.warn('[notifications] failed to load subscriptions', err);
    }
    try {
      const local = await getCurrentPushSubscription();
      setHasLocalSubscription(local !== null);
    } catch {
      setHasLocalSubscription(false);
    }
  }, []);

  useEffect(() => {
    if (pushAvailable.kind !== 'supported') return;
    refreshSubscriptions();
  }, [pushAvailable.kind, refreshSubscriptions]);

  useEffect(() => {
    const refresh = () => setPermission(getNotificationPermission());
    window.addEventListener('focus', refresh);
    return () => window.removeEventListener('focus', refresh);
  }, []);

  if (!supported) {
    return (
      <div className="space-y-4">
        <section>
          <h3 className="text-sm font-bold text-slate-900">ブラウザ通知</h3>
          <HelpText>お使いのブラウザは Notification API に未対応です。</HelpText>
        </section>
      </div>
    );
  }

  const handleEnable = async () => {
    const result = await requestNotificationPermission();
    setPermission(result === 'unsupported' ? 'unsupported' : result);
    if (result === 'granted') void setEnabled(true);
    else void setEnabled(false);
  };

  const handleTestV1 = () => {
    const opts = buildNotificationOptions(
      { id: 0, title: 'テスト通知', pieceName: 'ブラウザ通知は正常に動作しています' },
      'succeeded',
    );
    createNotification(opts, () => { /* no-op */ });
  };

  const handleSubscribe = async () => {
    if (pushAvailable.kind !== 'supported') return;
    setBusy(true);
    setPushFatal(null);
    try {
      const { publicKey } = await fetchVapidPublicKey();
      const dto = await subscribePush(publicKey);
      await postPushSubscription(dto);
      await refreshSubscriptions();
    } catch (err) {
      setPushFatal(err instanceof Error ? err.message : String(err));
    } finally {
      setBusy(false);
    }
  };

  const handleUnsubscribeLocal = async () => {
    setBusy(true);
    setPushFatal(null);
    try {
      const local = await getCurrentPushSubscription();
      // Match the server-side row by endpoint host (server returns only the host).
      const targetHost = local ? (() => {
        try { return new URL(local.endpoint).host; } catch { return null; }
      })() : null;
      const serverRow = subscriptions.find(s => targetHost && s.endpointHost === targetHost);
      await unsubscribePush();
      if (serverRow) await apiDeletePushSubscription(serverRow.id);
      await refreshSubscriptions();
    } catch (err) {
      setPushFatal(err instanceof Error ? err.message : String(err));
    } finally {
      setBusy(false);
    }
  };

  const handleDeleteRemote = async (id: string) => {
    setBusy(true);
    setPushFatal(null);
    try {
      await apiDeletePushSubscription(id);
      await refreshSubscriptions();
    } catch (err) {
      setPushFatal(err instanceof Error ? err.message : String(err));
    } finally {
      setBusy(false);
    }
  };

  const handleTestV2 = async () => {
    setBusy(true);
    setPushFatal(null);
    try {
      await postTestNotification();
    } catch (err) {
      setPushFatal(err instanceof Error ? err.message : String(err));
    } finally {
      setBusy(false);
    }
  };

  const v1StatusBadge = (() => {
    if (permission === 'granted' && enabled) return '✅ 有効化済み';
    if (permission === 'granted' && !enabled) return '⏸ 一時停止中';
    if (permission === 'denied') return '🚫 ブラウザで拒否';
    return '❌ 未許可';
  })();

  return (
    <div className="space-y-6">
      {/* ── V1: 前面通知 ── */}
      <section>
        <h3 className="text-sm font-bold text-slate-900">ブラウザ通知 (V1: 前面表示)</h3>
        <p className="mt-1 text-[13px] text-slate-700">状態: {v1StatusBadge}</p>

        {permission === 'default' && (
          <button
            onClick={handleEnable}
            className="mt-2 px-3 py-1.5 rounded bg-accent text-accent-fg text-[13px]"
          >
            ブラウザ通知を有効化
          </button>
        )}

        {permission === 'denied' && (
          <HelpText>
            ブラウザのアドレスバー左の設定アイコンから「通知」を許可に変更してください。
          </HelpText>
        )}

        {permission === 'granted' && (
          <label className="mt-2 flex items-center gap-2 text-[13px]">
            <input
              type="checkbox"
              checked={enabled}
              onChange={e => void setEnabled(e.target.checked)}
            />
            通知を受け取る (マスター ON/OFF)
          </label>
        )}

        {permission === 'granted' && (
          <button
            onClick={handleTestV1}
            disabled={!enabled}
            className="mt-2 px-3 py-1.5 rounded border border-slate-300 text-[13px]"
          >
            テスト通知 (ページ内)
          </button>
        )}
      </section>

      {/* ── V2: モバイル / バックグラウンド通知 ── */}
      <section>
        <h3 className="text-sm font-bold text-slate-900">📱 モバイル / バックグラウンド通知 (V2)</h3>

        {pushAvailable.kind === 'unsupported' && (
          <HelpText>{pushAvailable.reason}</HelpText>
        )}

        {pushAvailable.kind === 'needs-pwa-ios' && (
          <HelpText>
            iOS Safari では「共有 → ホーム画面に追加」でアプリとしてインストールしてから、
            ホーム画面のアイコンから開いた状態で通知を有効化できます。
          </HelpText>
        )}

        {pushAvailable.kind === 'supported' && (
          <div className="mt-2 space-y-2">
            <p className="text-[13px] text-slate-700">
              状態: {hasLocalSubscription ? '✅ このデバイスで購読中' : '❌ このデバイスは未購読'}
              {subscriptions.length > 0 && ` (合計 ${subscriptions.length} デバイス)`}
            </p>
            <div className="flex gap-2">
              <button
                onClick={handleSubscribe}
                disabled={busy || hasLocalSubscription || !enabled}
                className="px-3 py-1.5 rounded bg-accent text-accent-fg text-[13px] disabled:opacity-50"
              >
                このデバイスで購読
              </button>
              {hasLocalSubscription && (
                <button
                  onClick={handleUnsubscribeLocal}
                  disabled={busy}
                  className="px-3 py-1.5 rounded border border-slate-300 text-[13px]"
                >
                  購読を解除
                </button>
              )}
              <button
                onClick={handleTestV2}
                disabled={busy || subscriptions.length === 0}
                className="px-3 py-1.5 rounded border border-slate-300 text-[13px]"
              >
                テスト通知 (サーバー経由)
              </button>
            </div>

            {subscriptions.length > 0 && (
              <div className="mt-2 border border-slate-200 rounded">
                <p className="px-3 py-1 text-[12px] text-slate-600 border-b border-slate-200">
                  購読デバイス一覧
                </p>
                {subscriptions.map(s => (
                  <div key={s.id} className="flex items-center justify-between px-3 py-2 text-[13px] border-b border-slate-100 last:border-b-0">
                    <div>
                      <div className="truncate max-w-md">{s.userAgent ?? '(unknown)'}</div>
                      <div className="text-[11px] text-slate-500">
                        {s.endpointHost} • {new Date(s.createdAt).toLocaleString('ja-JP')}
                        {s.failureCount > 0 && (
                          <span className="ml-2 text-red-600">⚠ {s.failureCount} failures</span>
                        )}
                      </div>
                    </div>
                    <button
                      onClick={() => handleDeleteRemote(s.id)}
                      disabled={busy}
                      className="ml-2 px-2 py-1 text-[11px] text-red-700 hover:bg-red-50 rounded"
                    >
                      解除
                    </button>
                  </div>
                ))}
              </div>
            )}

            {serverPrefs && (
              <label className="mt-2 flex items-center gap-2 text-[13px]">
                <input
                  type="checkbox"
                  checked={includeDetails}
                  onChange={e => void setIncludeDetails(e.target.checked)}
                />
                通知にタスクの詳細（タイトル・piece 名）を含める
                <span className="text-[11px] text-slate-500">
                  (OFF: 「タスク #N 完了」のみ)
                </span>
              </label>
            )}

            {pushFatal && (
              <p className="text-[12px] text-red-700">エラー: {pushFatal}</p>
            )}
          </div>
        )}
      </section>

      {/* ── 通知するイベント (V1 + V2 共通) ── */}
      <section>
        <h3 className="text-sm font-bold text-slate-900">通知するイベント</h3>
        <div className="mt-2 space-y-1">
          {EVENT_LABELS.map(({ key, label }) => (
            <label key={key} className="flex items-center gap-2 text-[13px]">
              <input
                type="checkbox"
                checked={events[key]}
                onChange={() => void toggleEvent(key)}
                disabled={!enabled}
              />
              {label}
            </label>
          ))}
        </div>
      </section>

      <HelpText>
        ⓘ V1 (前面表示) はタブが開いていてフォーカスがある時のみ動作します<br />
        ⓘ V2 (モバイル / バックグラウンド) は HTTPS + PWA インストール時のみ確実に動作します<br />
        ⓘ 自分が owner のタスクのみ通知されます
      </HelpText>
    </div>
  );
}
