import React, { useEffect, useRef, useState } from 'react';
import { api } from '../api.js';
import {
  connectionAction,
  copyText,
  formatConnectionDuration,
  localProxyUrls,
  subscriptionDomain,
  subscriptionDaysLeft,
  subscriptionUsage,
} from '../utils/clientControls.js';
import { formatBytes } from '../utils/format.js';

export function ClientOverviewPage({
  state,
  busy,
  error,
  subscriptionUrl,
  setSubscriptionUrl,
  servers,
  pendingTag,
  setPendingTag,
  onFetchSubscription,
  onRefreshSubscriptionInfo,
  onForgetSubscription,
  onApply,
  onRestart,
  onStop,
}) {
  const isGateway = state?.mode === 'gateway';
  const connected = Boolean(state?.singboxRunning);
  const hasSubscription = Boolean(state?.hasSubscription);
  const selectedTag = pendingTag || state?.selectedTag || '';
  const showPower = hasSubscription && Boolean(selectedTag);
  const canStart = Boolean(selectedTag || state?.configExists);
  const [now, setNow] = useState(Date.now());
  const [editingSubscription, setEditingSubscription] = useState(!state?.hasSubscription);
  const [pings, setPings] = useState({});
  const [accessTab, setAccessTab] = useState('gateway');
  const [copyFeedback, setCopyFeedback] = useState(null);
  const [refreshingInfo, setRefreshingInfo] = useState(false);
  const [usageUpdated, setUsageUpdated] = useState(false);
  const [serverRevealVersion, setServerRevealVersion] = useState(0);
  const [serversLeaving, setServersLeaving] = useState(false);
  const subscriptionInputRef = useRef(null);
  const subscriptionRef = useRef(null);
  const copyTimerRef = useRef(null);
  const serverKey = servers.map((server) => `${server.tag}:${server.server}:${server.server_port}`).join('|');
  const gatewayAddress = isGateway ? window.location.hostname : '127.0.0.1';
  const proxyUrls = localProxyUrls(state?.proxyPort, gatewayAddress);
  const usage = subscriptionUsage(state?.userInfo);
  const [displayedUsed, setDisplayedUsed] = useState(usage.used);
  const hasUsage = Boolean(
    state?.userInfo && ['upload', 'download', 'total', 'expire'].some((key) => key in state.userInfo),
  );

  useEffect(() => {
    setNow(Date.now());
    if (!connected || !state?.singboxStartedAt) return undefined;

    const timer = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(timer);
  }, [connected, state?.singboxStartedAt]);

  useEffect(() => {
    if (!servers.length) {
      setPings({});
      return undefined;
    }

    let cancelled = false;
    setPings(Object.fromEntries(servers.map((server) => [server.tag, { checking: true }])));
    api.servers.pingAll()
      .then((data) => {
        if (cancelled) return;
        setPings(Object.fromEntries((data.results || []).map((ping) => [
          String(ping.tag || '').trim(),
          ping,
        ])));
      })
      .catch(() => {
        if (!cancelled) {
          setPings(Object.fromEntries(servers.map((server) => [server.tag, { ok: false }])));
        }
      });

    return () => { cancelled = true; };
  }, [serverKey]);

  useEffect(() => {
    if (editingSubscription) subscriptionInputRef.current?.focus();
  }, [editingSubscription]);

  useEffect(() => {
    if (!hasSubscription) setEditingSubscription(true);
  }, [hasSubscription]);

  useEffect(() => {
    if (!editingSubscription || !state?.hasSubscription || subscriptionUrl) return undefined;
    const timer = setTimeout(() => setEditingSubscription(false), 5000);
    return () => clearTimeout(timer);
  }, [editingSubscription, state?.hasSubscription, subscriptionUrl]);

  useEffect(() => {
    if (!editingSubscription || !hasSubscription) return undefined;
    const closeOnOutsideClick = (event) => {
      if (subscriptionRef.current?.contains(event.target)) return;
      setSubscriptionUrl('');
      setEditingSubscription(false);
    };
    document.addEventListener('pointerdown', closeOnOutsideClick);
    return () => document.removeEventListener('pointerdown', closeOnOutsideClick);
  }, [editingSubscription, hasSubscription, setSubscriptionUrl]);

  useEffect(() => {
    if (!state?.hasSubscription) return undefined;
    const refresh = () => onRefreshSubscriptionInfo().catch(() => {});
    refresh();
    const timer = setInterval(refresh, 60_000);
    return () => clearInterval(timer);
  }, [state?.hasSubscription]);

  useEffect(() => {
    const from = displayedUsed;
    const to = usage.used;
    if (from === to) return undefined;
    const startedAt = performance.now();
    let frame;
    const tick = (now) => {
      const progress = Math.min(1, (now - startedAt) / 900);
      const eased = 1 - Math.pow(1 - progress, 4);
      setDisplayedUsed(from + (to - from) * eased);
      if (progress < 1) frame = requestAnimationFrame(tick);
    };
    frame = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(frame);
  }, [usage.used]);

  useEffect(() => () => clearTimeout(copyTimerRef.current), []);

  async function toggleConnection() {
    const action = connectionAction({ connected, selectedTag, configExists: state?.configExists });
    if (action?.type === 'stop') return onStop();
    if (action?.type === 'apply') return onApply(action.selectedTag);
    if (action?.type === 'restart') return onRestart();
  }

  function selectServer(tag) {
    setPendingTag(tag);
    if (connected && tag) onApply(tag);
  }

  async function submitSubscription(event) {
    event.preventDefault();
    if (!subscriptionUrl.trim()) return;
    await onFetchSubscription();
    setSubscriptionUrl('');
    setEditingSubscription(false);
  }

  async function copyProxy(kind) {
    const value = kind === 'gateway' ? gatewayAddress : proxyUrls[kind];
    clearTimeout(copyTimerRef.current);
    setCopyFeedback({ kind, failed: false });
    copyTimerRef.current = setTimeout(() => setCopyFeedback(null), 800);
    try {
      await copyText(value);
    } catch {
      setCopyFeedback({ kind, failed: true });
    }
  }

  async function refreshInfo() {
    const startedAt = performance.now();
    setRefreshingInfo(true);
    try {
      await onRefreshSubscriptionInfo();
      setUsageUpdated(false);
      requestAnimationFrame(() => setUsageUpdated(true));
      setTimeout(() => setUsageUpdated(false), 900);
      setServersLeaving(true);
      await new Promise((resolve) => setTimeout(resolve, 420 + Math.max(0, servers.length - 1) * 90));
      setServerRevealVersion((version) => version + 1);
      setServersLeaving(false);
    } finally {
      const elapsed = performance.now() - startedAt;
      const completeCyclesAt = Math.max(900, Math.ceil(elapsed / 900) * 900);
      await new Promise((resolve) => setTimeout(resolve, completeCyclesAt - elapsed));
      setRefreshingInfo(false);
    }
  }

  return (
    <div className="client-shell">
      <main className={`client-panel${showPower ? '' : ' is-setup'}`}>
        {showPower && (
        <section className="client-power-section" aria-labelledby="connection-title">
          <button
            className="client-power"
            type="button"
            role="switch"
            aria-checked={connected}
            aria-label={connected ? 'Выключить VPN' : 'Включить VPN'}
            disabled={busy || (!connected && !canStart)}
            onClick={toggleConnection}
          >
            <svg viewBox="0 0 24 24" aria-hidden="true">
              <path d="M12 2v10M5.6 5.6a9 9 0 1 0 12.8 0" />
            </svg>
          </button>
          <div className="client-state-copy" aria-live="polite">
            <h2 key={connected ? 'connected' : 'disconnected'} id="connection-title">
              {connected ? 'VPN включён' : 'VPN выключен'}
            </h2>
            <div className="client-state-detail">
              {connected ? (
                <time key="duration" className="client-duration">
                  {formatConnectionDuration(state?.singboxStartedAt, now)}
                </time>
              ) : (
                <p key="hint">
                  {canStart ? 'Нажмите, чтобы включить' : 'Добавьте ссылку и выберите сервер'}
                </p>
              )}
            </div>
          </div>

          <section className={`client-proxies${isGateway ? ' has-tabs' : ''}`} aria-label={isGateway ? 'Gateway и Local Proxy' : 'Локальный прокси'}>
            {isGateway && (
              <div className="client-access-tabs" role="tablist" aria-label="Способ подключения">
                {[
                  ['gateway', 'Gateway'],
                  ['proxy', 'Local Proxy'],
                ].map(([tab, label]) => (
                  <button
                    className={`client-access-tab${accessTab === tab ? ' is-active' : ''}`}
                    type="button"
                    role="tab"
                    key={tab}
                    aria-selected={accessTab === tab}
                    onClick={() => setAccessTab(tab)}
                  >
                    {label}
                  </button>
                ))}
              </div>
            )}
            {(isGateway ? accessTab === 'gateway' : false) ? (
              <div className="client-access-point" role="tabpanel" key="gateway">
                <strong className="client-proxy-address">{gatewayAddress}</strong>
                <button
                  className={`client-copy-button${copyFeedback?.kind === 'gateway' ? copyFeedback.failed ? ' is-copy-error' : ' is-copied' : ''}`}
                  type="button"
                  aria-label={`Скопировать Gateway: ${gatewayAddress}`}
                  onClick={() => copyProxy('gateway')}
                >
                  <span className="client-copy-label">КОПИРОВАТЬ</span>
                  {copyFeedback?.kind === 'gateway' && <span className="client-copy-feedback">{copyFeedback.failed ? 'Error' : 'Copied'}</span>}
                </button>
              </div>
            ) : (
            <div className="client-access-point" role={isGateway ? 'tabpanel' : undefined} key="proxy">
              {!isGateway && <span className="client-proxy-label">Адрес</span>}
              <strong className="client-proxy-address">
                {proxyUrls.http.replace(/^https?:\/\//, '')}
              </strong>
              <div className="client-proxy-actions">
              {[
                ['socks5', 'SOCKS5'],
                ['http', 'HTTP'],
              ].map(([kind, label]) => (
                <button
                  className={`client-copy-button${copyFeedback?.kind === kind ? copyFeedback.failed ? ' is-copy-error' : ' is-copied' : ''}`}
                  type="button"
                  key={kind}
                  aria-label={`Скопировать ${label}: ${proxyUrls[kind]}`}
                  onClick={() => copyProxy(kind)}
                >
                  <span className="client-copy-label">{label}</span>
                  {copyFeedback?.kind === kind && <span className="client-copy-feedback">{copyFeedback.failed ? 'Error' : 'Copied'}</span>}
                </button>
              ))}
              </div>
            </div>
            )}
          </section>
        </section>
        )}

        {error && <p className="client-error" role="alert">{error}</p>}

        <div className="client-form">
          <div
            ref={subscriptionRef}
            className={`client-subscription ${editingSubscription ? 'is-editing' : ''}${editingSubscription && state?.hasSubscription && !subscriptionUrl ? ' is-timing-out' : ''}`}
          >
            <div
              className="client-subscription-summary"
              aria-hidden={editingSubscription}
              inert={editingSubscription ? true : undefined}
            >
              <div className="client-subscription-heading">
                <span>Ваша подписка</span>
                <button
                  className="client-subscription-refresh"
                  type="button"
                  aria-label="Обновить статистику подписки"
                  title="Обновить статистику"
                  disabled={refreshingInfo}
                  onClick={refreshInfo}
                >
                  <svg viewBox="0 0 24 24" aria-hidden="true">
                    <path d="M21 12a9 9 0 0 0-15.2-6.5L3 8m0-5v5h5M3 12a9 9 0 0 0 15.2 6.5L21 16m0 5v-5h-5" />
                  </svg>
                </button>
                <button
                  className="client-subscription-delete"
                  type="button"
                  aria-label="Удалить подписку"
                  title="Удалить подписку"
                  disabled={busy}
                  onClick={onForgetSubscription}
                >
                  <svg viewBox="0 0 24 24" aria-hidden="true">
                    <path d="M4 7h16M9 7V4h6v3m-9 0 1 13h10l1-13M10 11v5M14 11v5" />
                  </svg>
                </button>
              </div>
              <button
                className="client-subscription-domain-button"
                type="button"
                tabIndex={editingSubscription ? -1 : 0}
                onClick={() => setEditingSubscription(true)}
              >
                <strong>{subscriptionDomain(state?.subscriptionHost)}</strong>
              </button>
            </div>

            <form
              className="client-subscription-edit"
              autoComplete="off"
              aria-hidden={!editingSubscription}
              inert={!editingSubscription ? true : undefined}
              onSubmit={submitSubscription}
            >
              <input
                ref={subscriptionInputRef}
                id="subscription-url"
                type="url"
                inputMode="url"
                autoComplete="off"
                tabIndex={editingSubscription ? 0 : -1}
                aria-label="Ссылка подписки"
                placeholder="Вставьте ссылку подписки"
                className={subscriptionUrl ? 'has-value' : ''}
                value={subscriptionUrl}
                onChange={(event) => setSubscriptionUrl(event.target.value)}
                onKeyDown={(event) => {
                  if (event.key === 'Escape' && state?.hasSubscription) {
                    setSubscriptionUrl('');
                    setEditingSubscription(false);
                  }
                }}
              />
              {subscriptionUrl && (
                <span className="client-subscription-domain">
                  {subscriptionDomain(subscriptionUrl)}
                </span>
              )}
              {subscriptionUrl.trim() && (
                <button type="submit" aria-label="Сохранить подписку" disabled={busy}>✓</button>
              )}
            </form>
          </div>

          {hasSubscription && hasUsage && (
            <section className={`client-usage${usageUpdated ? ' is-updated' : ''}`} aria-label="Статистика подписки">
              <span>Использовано</span>
              <strong>
                {formatBytes(displayedUsed)}
                <small> / {usage.total ? formatBytes(usage.total) : 'без лимита'}</small>
              </strong>
              {usage.percent !== null && (
                <div
                  className="client-usage-bar"
                  role="progressbar"
                  aria-label="Использованный трафик"
                  aria-valuemin="0"
                  aria-valuemax="100"
                  aria-valuenow={Math.round(usage.percent)}
                >
                  <i style={{ width: `${usage.percent}%` }} />
                </div>
              )}
              <div className="client-usage-details">
                {usage.expiresAt && !Number.isNaN(usage.expiresAt.getTime()) && (
                  <span>
                    до {usage.expiresAt.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long' })}
                    {' · '}{subscriptionDaysLeft(usage.expiresAt)}
                  </span>
                )}
              </div>
            </section>
          )}

          {hasSubscription && (
          <section className="client-servers" aria-label="Выберите сервер">
            {!showPower && <span className="client-server-prompt">Выберите сервер</span>}
            <div
              className={`client-server-grid${serversLeaving ? ' is-leaving' : ''}`}
              key={`${serverKey}:${serverRevealVersion}`}
            >
              {servers.map((server, index) => {
                const ping = pings[server.tag];
                const selected = server.tag === selectedTag;
                const pingText = ping?.checking
                  ? 'Проверка…'
                  : ping?.ok ? `${ping.latency} ms` : 'Недоступен';
                const pingClass = ping?.ok
                  ? ping.latency < 100 ? 'good' : ping.latency < 250 ? 'medium' : 'slow'
                  : '';

                return (
                  <button
                    className={`client-server ${selected ? 'is-selected' : ''}`}
                    type="button"
                    key={server.tag}
                    disabled={busy}
                    aria-pressed={selected}
                    style={{ '--server-index': index }}
                    onClick={() => selectServer(server.tag)}
                  >
                    <strong>{server.tag}</strong>
                    <small className={pingClass}>{pingText}</small>
                  </button>
                );
              })}
            </div>
          </section>
          )}
        </div>
      </main>

    </div>
  );
}
