import { FormEvent, KeyboardEvent, useEffect, useMemo, useState } from 'react';
import './app.css';
import { fallbackDashboardSnapshot, panelAuth } from './data/mockDashboard';
import {
  buildProxyLink,
  formatBytes,
  formatQuotaState,
  formatTrafficShare,
  getServiceTone,
  isQuotaExceeded,
} from './lib/3proxy';
import type {
  CreateUserInput,
  DashboardSnapshot,
  ProxyServiceRecord,
  ProxyUserRecord,
} from './shared/contracts';

type TabId = 'dashboard' | 'users' | 'system';

const tabs: Array<{ id: TabId; label: string }> = [
  { id: 'dashboard', label: 'Dashboard' },
  { id: 'users', label: 'Users' },
  { id: 'system', label: 'System' },
];

function LoginGate({ onUnlock }: { onUnlock: () => void }) {
  const [login, setLogin] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();

    if (login === panelAuth.login && password === panelAuth.password) {
      setError('');
      onUnlock();
      return;
    }

    setError('Wrong panel credentials. Check the hardcoded startup values.');
  };

  return (
    <main className="login-shell">
      <section className="login-card">
        <div className="login-copy">
          <h1>3proxy UI</h1>
          <p>Sign in to the control panel.</p>
        </div>
        <form className="login-form" onSubmit={handleSubmit}>
          <label>
            Login
            <input
              autoComplete="username"
              name="login"
              value={login}
              onChange={(event) => setLogin(event.target.value)}
            />
          </label>
          <label>
            Password
            <input
              autoComplete="current-password"
              name="password"
              type="password"
              value={password}
              onChange={(event) => setPassword(event.target.value)}
            />
          </label>
          <button type="submit">Open panel</button>
          {error ? <p className="form-error">{error}</p> : null}
        </form>
      </section>
    </main>
  );
}

function AddUserModal({
  host,
  services,
  onClose,
  onCreate,
}: {
  host: string;
  services: ProxyServiceRecord[];
  onClose: () => void;
  onCreate: (input: CreateUserInput) => Promise<void>;
}) {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [serviceId, setServiceId] = useState(services[0]?.id ?? '');
  const [quotaMb, setQuotaMb] = useState('');
  const [error, setError] = useState('');

  useEffect(() => {
    const handleKeyDown = (event: globalThis.KeyboardEvent) => {
      if (event.key === 'Escape') {
        onClose();
      }
    };

    window.addEventListener('keydown', handleKeyDown);

    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [onClose]);

  const stopPropagation = (event: KeyboardEvent<HTMLDivElement>) => {
    event.stopPropagation();
  };

  const selectedService = services.find((service) => service.id === serviceId);

  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();

    try {
      await onCreate({
        username,
        password,
        serviceId,
        quotaMb: quotaMb.trim() ? Number(quotaMb) : null,
      });
      onClose();
    } catch (submitError) {
      setError(submitError instanceof Error ? submitError.message : 'Unable to create user.');
    }
  };

  return (
    <div className="modal-backdrop" role="presentation" onClick={onClose}>
      <section
        aria-labelledby="add-user-title"
        aria-modal="true"
        className="modal-card"
        role="dialog"
        onClick={stopPropagation}
      >
        <div className="modal-header">
          <h2 id="add-user-title">Add user</h2>
          <button type="button" className="button-secondary" onClick={onClose}>
            Close
          </button>
        </div>
        <form className="modal-form" onSubmit={handleSubmit}>
          <label>
            Username
            <input autoFocus placeholder="night-shift-01" value={username} onChange={(event) => setUsername(event.target.value)} />
          </label>
          <label>
            Password
            <input placeholder="generated-secret" value={password} onChange={(event) => setPassword(event.target.value)} />
          </label>
          <label>
            Service
            <select value={serviceId} onChange={(event) => setServiceId(event.target.value)}>
              {services.map((service) => (
                <option key={service.id} value={service.id}>
                  {service.name}
                </option>
              ))}
            </select>
          </label>
          <label>
            Quota (MB)
            <input placeholder="Optional" value={quotaMb} onChange={(event) => setQuotaMb(event.target.value)} />
          </label>
          <div className="modal-preview">
            <span>Endpoint</span>
            <strong>{selectedService ? `${host}:${selectedService.port}` : 'Unavailable'}</strong>
          </div>
          <div className="modal-preview">
            <span>Protocol</span>
            <strong>{selectedService ? selectedService.protocol : 'Unavailable'}</strong>
          </div>
          {error ? <p className="form-error modal-error">{error}</p> : null}
          <div className="modal-actions">
            <button type="button" className="button-secondary" onClick={onClose}>
              Cancel
            </button>
            <button type="submit">Create user</button>
          </div>
        </form>
      </section>
    </div>
  );
}

function ConfirmDeleteModal({
  username,
  onClose,
  onConfirm,
}: {
  username: string;
  onClose: () => void;
  onConfirm: () => Promise<void>;
}) {
  useEffect(() => {
    const handleKeyDown = (event: globalThis.KeyboardEvent) => {
      if (event.key === 'Escape') {
        onClose();
      }
    };

    window.addEventListener('keydown', handleKeyDown);

    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [onClose]);

  const stopPropagation = (event: KeyboardEvent<HTMLDivElement>) => {
    event.stopPropagation();
  };

  return (
    <div className="modal-backdrop" role="presentation" onClick={onClose}>
      <section
        aria-labelledby="delete-user-title"
        aria-modal="true"
        className="modal-card confirm-card"
        role="dialog"
        onClick={stopPropagation}
      >
        <div className="modal-header">
          <h2 id="delete-user-title">Delete user</h2>
        </div>
        <p className="confirm-copy">
          Remove profile <strong>{username}</strong>? This action will delete the user entry from the
          current panel state.
        </p>
        <div className="modal-actions">
          <button type="button" className="button-secondary" onClick={onClose}>
            Cancel
          </button>
          <button type="button" className="button-danger" onClick={onConfirm}>
            Delete user
          </button>
        </div>
      </section>
    </div>
  );
}

function DashboardTab({
  snapshot,
  onRuntimeAction,
}: {
  snapshot: DashboardSnapshot;
  onRuntimeAction: (action: 'start' | 'restart') => Promise<void>;
}) {
  const serviceTone = getServiceTone(snapshot.service.status);

  return (
    <section className="page-grid">
      <article className="panel-card">
        <div className="card-header">
          <h2>Service</h2>
          <span className={`status-pill ${serviceTone}`}>{snapshot.service.status}</span>
        </div>
        <dl className="kv-list">
          <div>
            <dt>Process</dt>
            <dd>{snapshot.service.pidLabel}</dd>
          </div>
          <div>
            <dt>Version</dt>
            <dd>{snapshot.service.versionLabel}</dd>
          </div>
          <div>
            <dt>Uptime</dt>
            <dd>{snapshot.service.uptimeLabel}</dd>
          </div>
          <div>
            <dt>Last event</dt>
            <dd>{snapshot.service.lastEvent}</dd>
          </div>
        </dl>
        <div className="actions-row">
          <button type="button" onClick={() => onRuntimeAction('start')}>
            Start
          </button>
          <button type="button" className="button-secondary" onClick={() => onRuntimeAction('restart')}>
            Restart
          </button>
        </div>
      </article>

      <article className="panel-card">
        <div className="card-header">
          <h2>Traffic</h2>
        </div>
        <div className="stats-strip">
          <div>
            <span>Total</span>
            <strong>{formatBytes(snapshot.traffic.totalBytes)}</strong>
          </div>
          <div>
            <span>Connections</span>
            <strong>{snapshot.traffic.liveConnections}</strong>
          </div>
          <div>
            <span>Active users</span>
            <strong>{snapshot.traffic.activeUsers}</strong>
          </div>
        </div>
      </article>

      <article className="panel-card">
        <div className="card-header">
          <h2>Daily usage</h2>
        </div>
        <div className="usage-list">
          {snapshot.traffic.daily.map((bucket) => (
            <div key={bucket.day} className="usage-row">
              <span>{bucket.day}</span>
              <div className="usage-bar">
                <div style={{ width: `${bucket.share * 100}%` }} />
              </div>
              <strong>{formatBytes(bucket.bytes)}</strong>
            </div>
          ))}
        </div>
      </article>

      <article className="panel-card">
        <div className="card-header">
          <h2>Attention</h2>
        </div>
        <div className="event-list">
          {snapshot.attention.map((item) => (
            <div key={item.title} className="event-row">
              <span className={`event-marker ${item.level}`} />
              <div>
                <strong>{item.title}</strong>
                <p>{item.message}</p>
              </div>
            </div>
          ))}
        </div>
      </article>
    </section>
  );
}

function UsersTab({
  snapshot,
  onCreateUser,
  onTogglePause,
  onDeleteUser,
}: {
  snapshot: DashboardSnapshot;
  onCreateUser: (input: CreateUserInput) => Promise<void>;
  onTogglePause: (userId: string) => Promise<void>;
  onDeleteUser: (userId: string) => Promise<void>;
}) {
  const [copiedId, setCopiedId] = useState<string | null>(null);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null);

  const servicesById = useMemo(
    () => new Map(snapshot.system.services.map((service) => [service.id, service] as const)),
    [snapshot.system.services],
  );

  const assignableServices = snapshot.system.services.filter((service) => service.enabled && service.assignable);
  const deleteTarget = snapshot.userRecords.find((user) => user.id === deleteTargetId) ?? null;

  const handleCopy = async (userId: string, proxyLink: string) => {
    await navigator.clipboard.writeText(proxyLink);
    setCopiedId(userId);
    window.setTimeout(() => setCopiedId((value) => (value === userId ? null : value)), 1200);
  };

  return (
    <>
      <section className="page-grid single-column">
        <article className="panel-card">
          <div className="table-toolbar">
            <div className="toolbar-title">
              <h2>Users</h2>
              <p>{snapshot.userRecords.length} accounts in current profile</p>
            </div>
            <div className="toolbar-actions">
              <div className="summary-pills">
                <span>{snapshot.users.live} live</span>
                <span>{snapshot.users.nearQuota} near quota</span>
                <span>{snapshot.users.exceeded} exceeded</span>
              </div>
              <button type="button" onClick={() => setIsModalOpen(true)}>
                New user
              </button>
            </div>
          </div>
          <div className="table-wrap">
            <table>
              <thead>
                <tr>
                  <th>User</th>
                  <th>Endpoint</th>
                  <th>Status</th>
                  <th>Used</th>
                  <th>Remaining</th>
                  <th>Share</th>
                  <th>Proxy</th>
                  <th>Actions</th>
                </tr>
              </thead>
              <tbody>
                {snapshot.userRecords.map((user) => {
                  const service = servicesById.get(user.serviceId);
                  const endpoint = service
                    ? `${snapshot.system.publicHost}:${service.port}`
                    : 'service missing';
                  const proxyLink = service
                    ? buildProxyLink(
                        user.username,
                        user.password,
                        snapshot.system.publicHost,
                        service.port,
                        service.protocol,
                      )
                    : '';
                  const exhausted = isQuotaExceeded(user.usedBytes, user.quotaBytes);
                  const displayStatus = user.paused ? 'paused' : user.status;

                  return (
                    <tr key={user.id}>
                      <td>
                        <div className="user-cell">
                          <strong>{user.username}</strong>
                        </div>
                      </td>
                      <td>
                        <div className="endpoint-cell">
                          <strong>{service?.name ?? 'Unknown service'}</strong>
                          <span>{endpoint}</span>
                        </div>
                      </td>
                      <td>
                        <span className={`status-pill ${getServiceTone(displayStatus)}`}>
                          {displayStatus}
                        </span>
                      </td>
                      <td>{formatBytes(user.usedBytes)}</td>
                      <td>{formatQuotaState(user.usedBytes, user.quotaBytes)}</td>
                      <td>{formatTrafficShare(user.usedBytes, snapshot.traffic.totalBytes)}</td>
                      <td>
                        <button
                          type="button"
                          className={exhausted ? 'copy-button danger' : 'copy-button'}
                          onClick={() => handleCopy(user.id, proxyLink)}
                          disabled={!service}
                        >
                          {copiedId === user.id ? 'Copied' : 'Copy'}
                        </button>
                      </td>
                      <td>
                        <div className="row-actions">
                          <button
                            type="button"
                            className="button-secondary button-small"
                            onClick={() => onTogglePause(user.id)}
                          >
                            {user.paused ? 'Resume' : 'Pause'}
                          </button>
                          <button
                            type="button"
                            className="button-danger button-small"
                            onClick={() => setDeleteTargetId(user.id)}
                          >
                            Delete
                          </button>
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </article>
      </section>

      {isModalOpen ? (
        <AddUserModal
          host={snapshot.system.publicHost}
          services={assignableServices}
          onClose={() => setIsModalOpen(false)}
          onCreate={onCreateUser}
        />
      ) : null}

      {deleteTarget ? (
        <ConfirmDeleteModal
          username={deleteTarget.username}
          onClose={() => setDeleteTargetId(null)}
          onConfirm={async () => {
            await onDeleteUser(deleteTarget.id);
            setDeleteTargetId(null);
          }}
        />
      ) : null}
    </>
  );
}

function SystemTab({ snapshot }: { snapshot: DashboardSnapshot }) {
  return (
    <section className="page-grid system-grid">
      <article className="panel-card">
        <div className="card-header">
          <h2>Runtime</h2>
        </div>
        <dl className="kv-list">
          <div>
            <dt>Config mode</dt>
            <dd>{snapshot.system.configMode}</dd>
          </div>
          <div>
            <dt>Reload</dt>
            <dd>{snapshot.system.reloadMode}</dd>
          </div>
          <div>
            <dt>Storage</dt>
            <dd>{snapshot.system.storageMode}</dd>
          </div>
        </dl>
      </article>

      <article className="panel-card">
        <div className="card-header">
          <h2>Services</h2>
        </div>
        <div className="service-list">
          {snapshot.system.services.map((service) => (
            <div key={service.name} className="service-row">
              <div>
                <strong>{service.name}</strong>
                <p>{service.description}</p>
              </div>
              <div className="service-meta">
                <span>{service.port}</span>
                <span className={`status-pill ${service.enabled ? 'live' : 'idle'}`}>
                  {service.enabled ? 'enabled' : 'disabled'}
                </span>
              </div>
            </div>
          ))}
        </div>
      </article>

      <article className="panel-card wide-card">
        <div className="card-header">
          <h2>Generated config</h2>
        </div>
        <pre>{snapshot.system.previewConfig}</pre>
      </article>
    </section>
  );
}

export default function App() {
  const [isAuthed, setIsAuthed] = useState(false);
  const [activeTab, setActiveTab] = useState<TabId>('dashboard');
  const [snapshot, setSnapshot] = useState<DashboardSnapshot>(fallbackDashboardSnapshot);

  useEffect(() => {
    let cancelled = false;

    void fetch('/api/state')
      .then((response) => (response.ok ? response.json() : Promise.reject(new Error('API unavailable'))))
      .then((payload: DashboardSnapshot) => {
        if (!cancelled) {
          setSnapshot(payload);
        }
      })
      .catch(() => {
        // Keep fallback snapshot for local UI and tests when backend is not running.
      });

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

  if (!isAuthed) {
    return <LoginGate onUnlock={() => setIsAuthed(true)} />;
  }

  const mutateSnapshot = async (
    request: () => Promise<Response>,
    fallback: (current: DashboardSnapshot) => DashboardSnapshot,
  ) => {
    try {
      const response = await request();
      if (response.ok) {
        const payload = (await response.json()) as DashboardSnapshot;
        setSnapshot(payload);
        return;
      }
    } catch {
      // Fall back to local optimistic state when the API is unavailable.
    }

    setSnapshot((current) => fallback(current));
  };

  const handleRuntimeAction = async (action: 'start' | 'restart') => {
    await mutateSnapshot(
      () => fetch(`/api/runtime/${action}`, { method: 'POST' }),
      (current) =>
        withDerivedSnapshot({
          ...current,
          service: {
            ...current.service,
            status: 'live',
            lastEvent: action === 'restart' ? 'Runtime restarted from panel' : 'Runtime start requested from panel',
          },
        }),
    );
  };

  const handleCreateUser = async (input: CreateUserInput) => {
    await mutateSnapshot(
      () =>
        fetch('/api/users', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(input),
        }),
      (current) => {
        const nextUser: ProxyUserRecord = {
          id: `u-${Math.random().toString(36).slice(2, 10)}`,
          username: input.username.trim(),
          password: input.password.trim(),
          serviceId: input.serviceId,
          status: 'idle',
          paused: false,
          usedBytes: 0,
          quotaBytes: input.quotaMb === null ? null : input.quotaMb * 1024 * 1024,
        };

        return withDerivedSnapshot({
          ...current,
          service: {
            ...current.service,
            lastEvent: `User ${nextUser.username} created from panel`,
          },
          userRecords: [...current.userRecords, nextUser],
        });
      },
    );
  };

  const handleTogglePause = async (userId: string) => {
    await mutateSnapshot(
      () => fetch(`/api/users/${userId}/pause`, { method: 'POST' }),
      (current) =>
        withDerivedSnapshot({
          ...current,
          userRecords: current.userRecords.map((user) =>
            user.id === userId ? { ...user, paused: !user.paused } : user,
          ),
        }),
    );
  };

  const handleDeleteUser = async (userId: string) => {
    await mutateSnapshot(
      () => fetch(`/api/users/${userId}`, { method: 'DELETE' }),
      (current) =>
        withDerivedSnapshot({
          ...current,
          userRecords: current.userRecords.filter((user) => user.id !== userId),
        }),
    );
  };

  return (
    <main className="shell">
      <header className="shell-header">
        <div className="shell-title">
          <h1>3proxy UI</h1>
          <p>{snapshot.system.publicHost}</p>
        </div>
        <div className="header-meta">
          <div>
            <span>Status</span>
            <strong>{snapshot.service.status}</strong>
          </div>
          <div>
            <span>Version</span>
            <strong>{snapshot.service.versionLabel}</strong>
          </div>
          <div>
            <span>Users</span>
            <strong>{snapshot.users.total}</strong>
          </div>
        </div>
      </header>

      <nav className="tabbar" aria-label="Primary">
        {tabs.map((tab) => (
          <button
            key={tab.id}
            type="button"
            className={activeTab === tab.id ? 'tab-button active' : 'tab-button'}
            onClick={() => setActiveTab(tab.id)}
          >
            {tab.label}
          </button>
        ))}
      </nav>

      {activeTab === 'dashboard' ? <DashboardTab snapshot={snapshot} onRuntimeAction={handleRuntimeAction} /> : null}
      {activeTab === 'users' ? (
        <UsersTab
          snapshot={snapshot}
          onCreateUser={handleCreateUser}
          onTogglePause={handleTogglePause}
          onDeleteUser={handleDeleteUser}
        />
      ) : null}
      {activeTab === 'system' ? <SystemTab snapshot={snapshot} /> : null}
    </main>
  );
}

function withDerivedSnapshot(snapshot: DashboardSnapshot): DashboardSnapshot {
  const live = snapshot.userRecords.filter((user) => !user.paused && user.status === 'live').length;
  const exceeded = snapshot.userRecords.filter(
    (user) => user.quotaBytes !== null && user.usedBytes >= user.quotaBytes,
  ).length;
  const nearQuota = snapshot.userRecords.filter((user) => {
    if (user.paused || user.quotaBytes === null || user.usedBytes >= user.quotaBytes) {
      return false;
    }

    return user.usedBytes / user.quotaBytes >= 0.8;
  }).length;

  return {
    ...snapshot,
    traffic: {
      ...snapshot.traffic,
      activeUsers: snapshot.userRecords.filter((user) => !user.paused).length,
    },
    users: {
      total: snapshot.userRecords.length,
      live,
      nearQuota,
      exceeded,
    },
  };
}
