import { useCallback, useEffect, useState } from 'react';
import { ArrowLeft, BellOff, BellRing, Link, Pencil, Plus, Trash2 } from 'lucide-react';
import { request } from '../api/client.js';
import { ConfirmDialog } from '../components/ConfirmDialog.jsx';
import { Field } from '../components/Field.jsx';
import { useToast } from '../components/Toast.jsx';

function validateWebhookForm(form) {
  if (!form.alias.trim()) {
    throw new Error('エイリアス名を入力してください');
  }
  if (form.alias.trim().length > 120) {
    throw new Error('エイリアス名は120文字以内で入力してください');
  }
  if (!form.url.trim()) {
    throw new Error('Webhook URL を入力してください');
  }
  const url = new URL(form.url.trim());
  if (url.protocol !== 'https:') {
    throw new Error('Webhook URL は HTTPS で入力してください');
  }
}

function urlBase64ToUint8Array(base64String) {
  const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
  const base64 = `${base64String}${padding}`.replaceAll('-', '+').replaceAll('_', '/');
  const rawData = window.atob(base64);
  return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)));
}

function permissionText(permission) {
  if (permission === 'unsupported') return '非対応';
  if (permission === 'granted') return '許可済み';
  if (permission === 'denied') return '拒否されています';
  return '未設定';
}

function pushStatusText(status) {
  if (status === 'checking') return '確認中';
  if (status === 'registered') return 'このブラウザは登録済みです';
  if (status === 'unregistered') return 'このブラウザは未登録です';
  if (status === 'no-subscription') return 'このブラウザは未登録です';
  if (status === 'permission-denied') return 'ブラウザ通知が拒否されています';
  if (status === 'unconfigured') return 'VAPID public key が未設定です';
  if (status === 'unsupported') return 'このブラウザは非対応です';
  if (status === 'error') return '状態を確認できませんでした';
  return '未確認';
}

async function getPushRegistration() {
  return navigator.serviceWorker.register('/push-sw.js');
}

export function NotificationMethodsView({ onBack }) {
  const [webhooks, setWebhooks] = useState([]);
  const [vapidPublicKey, setVapidPublicKey] = useState('');
  const [currentPushStatus, setCurrentPushStatus] = useState('unchecked');
  const [form, setForm] = useState({ alias: '', url: '' });
  const [editingId, setEditingId] = useState('');
  const [permission, setPermission] = useState(
    typeof Notification === 'undefined' ? 'unsupported' : Notification.permission,
  );
  const [busy, setBusy] = useState(false);
  const { showToast } = useToast();

  const refreshCurrentPushStatus = useCallback(
    async (publicKey = vapidPublicKey) => {
      if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
        setCurrentPushStatus('unsupported');
        return;
      }
      if (!publicKey) {
        setCurrentPushStatus('unconfigured');
        return;
      }
      if (typeof Notification === 'undefined') {
        setCurrentPushStatus('unsupported');
        return;
      }
      setPermission(Notification.permission);
      if (Notification.permission === 'denied') {
        setCurrentPushStatus('permission-denied');
        return;
      }

      setCurrentPushStatus('checking');
      try {
        const registration = await getPushRegistration();
        const subscription = await registration.pushManager.getSubscription();
        if (!subscription) {
          setCurrentPushStatus('no-subscription');
          return;
        }

        const data = await request('/api/notification-methods/push-subscription-status', {
          method: 'POST',
          body: JSON.stringify({ endpoint: subscription.endpoint }),
        });
        setCurrentPushStatus(data.registered ? 'registered' : 'unregistered');
      } catch (err) {
        setCurrentPushStatus('error');
        showToast({ type: 'error', message: err.message });
      }
    },
    [showToast, vapidPublicKey],
  );

  const loadMethods = useCallback(async () => {
    const data = await request('/api/notification-methods');
    setWebhooks(data.webhooks);
    setVapidPublicKey(data.vapidPublicKey);
    await refreshCurrentPushStatus(data.vapidPublicKey);
  }, [refreshCurrentPushStatus]);

  useEffect(() => {
    loadMethods().catch((err) => showToast({ type: 'error', message: err.message }));
  }, [loadMethods, showToast]);

  async function submitWebhook(event) {
    event.preventDefault();
    setBusy(true);
    try {
      validateWebhookForm(form);
      const endpoint = editingId
        ? `/api/notification-methods/webhooks/${editingId}`
        : '/api/notification-methods/webhooks';
      await request(endpoint, {
        method: editingId ? 'PATCH' : 'POST',
        body: JSON.stringify({ alias: form.alias.trim(), url: form.url.trim() }),
      });
      setForm({ alias: '', url: '' });
      setEditingId('');
      showToast({
        type: 'success',
        message: editingId ? 'Webhookを更新しました' : 'Webhookを登録しました',
      });
      await loadMethods();
    } catch (err) {
      showToast({ type: 'error', message: err.message });
    } finally {
      setBusy(false);
    }
  }

  function startEdit(webhook) {
    setEditingId(webhook.notificationMethodId);
    setForm({ alias: webhook.alias, url: webhook.url });
  }

  async function deleteWebhook(methodId) {
    setBusy(true);
    try {
      await request(`/api/notification-methods/webhooks/${methodId}`, { method: 'DELETE' });
      showToast({ type: 'success', message: 'Webhookを削除しました' });
      await loadMethods();
    } catch (err) {
      showToast({ type: 'error', message: err.message });
    } finally {
      setBusy(false);
    }
  }

  async function subscribePush() {
    setBusy(true);
    try {
      if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
        throw new Error('このブラウザはプッシュ通知に対応していません');
      }
      if (!vapidPublicKey) {
        throw new Error('VAPID public key が設定されていません');
      }
      if (typeof Notification === 'undefined') {
        throw new Error('このブラウザはプッシュ通知に対応していません');
      }

      const nextPermission = await Notification.requestPermission();
      setPermission(nextPermission);
      if (nextPermission !== 'granted') {
        throw new Error('ブラウザ通知が許可されませんでした');
      }

      const registration = await getPushRegistration();
      const existingSubscription = await registration.pushManager.getSubscription();
      const subscription =
        existingSubscription ??
        (await registration.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey: urlBase64ToUint8Array(vapidPublicKey),
        }));

      await request('/api/notification-methods/push-subscriptions', {
        method: 'POST',
        body: JSON.stringify(subscription.toJSON()),
      });
      showToast({ type: 'success', message: 'プッシュ通知を登録しました' });
      await refreshCurrentPushStatus();
    } catch (err) {
      showToast({ type: 'error', message: err.message });
    } finally {
      setBusy(false);
    }
  }

  async function unsubscribePush() {
    setBusy(true);
    try {
      if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
        throw new Error('このブラウザはプッシュ通知に対応していません');
      }

      const registration = await getPushRegistration();
      const subscription = await registration.pushManager.getSubscription();
      if (!subscription) {
        await refreshCurrentPushStatus();
        throw new Error('解除対象のプッシュ通知登録が見つかりません');
      }

      await request('/api/notification-methods/push-subscriptions', {
        method: 'DELETE',
        body: JSON.stringify({ endpoint: subscription.endpoint }),
      });
      await subscription.unsubscribe();
      showToast({ type: 'success', message: 'プッシュ通知を解除しました' });
      await refreshCurrentPushStatus();
    } catch (err) {
      showToast({ type: 'error', message: err.message });
    } finally {
      setBusy(false);
    }
  }

  return (
    <main className="app-shell">
      <header className="topbar">
        <button className="back-button" onClick={onBack}>
          <ArrowLeft aria-hidden="true" size={18} />
          サイト一覧
        </button>
        <div className="settings-title">
          <span className="eyebrow">通知方法</span>
          <h1>通知方法の管理</h1>
        </div>
      </header>

      <section className="workspace notification-layout">
        <form className="panel" onSubmit={submitWebhook}>
          <div className="panel-heading">
            <Link aria-hidden="true" size={20} />
            <div>
              <h2>Webhook</h2>
              <p>Slack互換Webhookとして送信します。</p>
            </div>
          </div>

          <div className="webhook-form">
            <Field label="エイリアス名">
              <input
                value={form.alias}
                onChange={(event) => setForm({ ...form, alias: event.target.value })}
                placeholder="Slack 通知"
                maxLength="120"
                required
              />
            </Field>
            <Field label="URL">
              <input
                value={form.url}
                onChange={(event) => setForm({ ...form, url: event.target.value })}
                placeholder="https://hooks.slack.com/services/..."
                maxLength="2048"
                required
              />
            </Field>
            <button className="primary" disabled={busy}>
              <Plus aria-hidden="true" size={18} />
              {editingId ? '更新' : '登録'}
            </button>
          </div>
        </form>

        <section className="panel">
          <h2>登録済みWebhook</h2>
          <div className="method-list">
            {webhooks.length === 0 ? (
              <div className="empty">Webhookはまだ登録されていません。</div>
            ) : (
              webhooks.map((webhook) => (
                <article className="method-row" key={webhook.notificationMethodId}>
                  <div>
                    <strong>{webhook.alias}</strong>
                    <span>{webhook.url}</span>
                  </div>
                  <div className="site-actions">
                    <button
                      className="icon-button"
                      type="button"
                      onClick={() => startEdit(webhook)}
                      aria-label={`${webhook.alias}を編集`}
                      title="編集"
                    >
                      <Pencil aria-hidden="true" size={18} />
                    </button>
                    <ConfirmDialog
                      title="Webhookを削除"
                      description={`${webhook.alias}を通知方法から削除します。`}
                      onConfirm={() => deleteWebhook(webhook.notificationMethodId)}
                      disabled={busy}
                      trigger={
                        <button
                          className="icon-button danger"
                          type="button"
                          aria-label={`${webhook.alias}を削除`}
                          title="削除"
                        >
                          <Trash2 aria-hidden="true" size={18} />
                        </button>
                      }
                    />
                  </div>
                </article>
              ))
            )}
          </div>
        </section>

        <section className="panel">
          <div className="panel-heading">
            <BellRing aria-hidden="true" size={20} />
            <div>
              <h2>プッシュ通知</h2>
              <p>ブラウザ許可状態: {permissionText(permission)}</p>
            </div>
          </div>

          <div className="method-list">
            <article className="method-row">
              <div>
                <strong>現在のブラウザ</strong>
                <span>{pushStatusText(currentPushStatus)}</span>
              </div>
            </article>
          </div>

          <div className="push-actions">
            {currentPushStatus === 'registered' ? (
              <ConfirmDialog
                title="プッシュ通知を解除"
                description="現在のブラウザをプッシュ通知の登録から解除します。"
                confirmLabel="解除"
                onConfirm={unsubscribePush}
                disabled={busy}
                trigger={
                  <button className="secondary danger-text" type="button" disabled={busy}>
                    <BellOff aria-hidden="true" size={18} />
                    このブラウザを解除
                  </button>
                }
              />
            ) : (
              <button
                className="secondary"
                type="button"
                onClick={subscribePush}
                disabled={busy || !vapidPublicKey}
              >
                <BellRing aria-hidden="true" size={18} />
                このブラウザを登録
              </button>
            )}
            {!vapidPublicKey ? (
              <p className="muted">VAPID public key を設定すると登録できます。</p>
            ) : null}
          </div>
        </section>
      </section>
    </main>
  );
}
