"use client";

import { useCallback, useEffect, useState } from "react";

import Modal from "@/components/admin/Modal";
import TemporaryPasswordDialog from "@/components/admin/TemporaryPasswordDialog";
import {
  Button,
  Card,
  Empty,
  ErrorNote,
  Field,
  Loading,
  formatDateTime,
  inputClass,
} from "@/components/admin/ui";
import { api, type AdminRole, type AuthMethod, type TemporaryPassword } from "@/lib/api/browser";
import { toAdminUserView, type AdminUserView } from "@/lib/view-models";

const ROLE_LABEL: Record<string, string> = {
  super_admin: "Super admin",
  host: "Host",
};

function StateBadge({ user }: { user: AdminUserView }) {
  const styles: Record<string, string> = {
    pending: "bg-sky-100 text-sky-900",
    active: "bg-emerald-100 text-emerald-900",
    withdrawn: "bg-stone-200 text-stone-600",
  };
  return (
    <span
      className={`inline-block rounded-full px-2 py-0.5 text-xs font-medium ${
        styles[user.status] ?? "bg-stone-200 text-stone-700"
      }`}
    >
      {user.status}
    </span>
  );
}

/**
 * The admin roster (spec admin-user-management).
 *
 * Pending accounts are listed **above** the active ones and separately, so a new arrival is
 * visible without anybody searching for it — a queue nobody notices is the same as no queue.
 *
 * Every control here is hidden or shown as a courtesy. The API enforces each rule again:
 * the last-super-admin guard, the owned-events guard, the refusal to put a password on a
 * Google account. When one of them fires, the message it returns is what gets rendered
 * rather than something invented here, because the server knows why.
 */
export default function UsersView({ currentEmail }: { currentEmail: string }) {
  const [users, setUsers] = useState<AdminUserView[] | null>(null);
  const [error, setError] = useState<unknown>(null);
  const [busyId, setBusyId] = useState<string | null>(null);
  const [creating, setCreating] = useState(false);
  const [issued, setIssued] = useState<{ email: string; secret: TemporaryPassword } | null>(
    null,
  );

  const load = useCallback(async () => {
    try {
      setError(null);
      const rows = await api.adminUsers();
      setUsers(rows.map(toAdminUserView));
    } catch (err) {
      setError(err);
      setUsers([]);
    }
  }, []);

  useEffect(() => {
    void load();
  }, [load]);

  const act = useCallback(
    async (id: string, run: () => Promise<unknown>) => {
      setBusyId(id);
      setError(null);
      try {
        await run();
        await load();
      } catch (err) {
        setError(err);
      } finally {
        setBusyId(null);
      }
    },
    [load],
  );

  if (users === null) return <Loading label="Loading users…" />;

  const pending = users.filter((u) => u.status === "pending");
  const settled = users.filter((u) => u.status !== "pending");

  return (
    <div className="space-y-4">
      <div className="flex flex-wrap items-center justify-between gap-3">
        <h1 className="text-lg font-semibold tracking-tight text-stone-900">Users</h1>
        <Button onClick={() => setCreating(true)}>Add user</Button>
      </div>

      <ErrorNote error={error} />

      {pending.length > 0 && (
        <Card
          title={`Awaiting approval (${pending.length})`}
          actions={
            <span className="text-xs text-stone-500">
              Created by a first Google sign-in. They can do nothing until approved.
            </span>
          }
        >
          <ul className="divide-y divide-stone-100">
            {pending.map((user) => (
              <li key={user.id} className="flex flex-wrap items-center gap-3 py-3">
                <div className="min-w-0 flex-1">
                  <p className="truncate text-sm font-medium text-stone-900">{user.email}</p>
                  <p className="text-xs text-stone-500">
                    {user.name ?? "No name"} · first seen{" "}
                    {user.firstSeenAt ? formatDateTime(user.firstSeenAt) : "—"}
                  </p>
                </div>
                <div className="flex flex-wrap gap-2">
                  <Button
                    disabled={busyId === user.id}
                    onClick={() => act(user.id, () => api.activateAdminUser(user.id, "host"))}
                  >
                    Approve as host
                  </Button>
                  <Button
                    variant="secondary"
                    disabled={busyId === user.id}
                    onClick={() =>
                      act(user.id, () => api.activateAdminUser(user.id, "super_admin"))
                    }
                  >
                    Approve as super admin
                  </Button>
                  <Button
                    variant="ghost"
                    disabled={busyId === user.id}
                    onClick={() => act(user.id, () => api.rejectAdminUser(user.id))}
                  >
                    Reject
                  </Button>
                </div>
              </li>
            ))}
          </ul>
        </Card>
      )}

      <Card title="Accounts">
        {settled.length === 0 ? (
          <Empty>No user accounts yet.</Empty>
        ) : (
          <div className="overflow-x-auto">
            <table className="w-full min-w-[52rem] text-left text-sm">
              <thead className="text-xs uppercase tracking-wide text-stone-500">
                <tr>
                  <th className="py-2 pr-3 font-medium">Account</th>
                  <th className="py-2 pr-3 font-medium">Sign-in</th>
                  <th className="py-2 pr-3 font-medium">Role</th>
                  <th className="py-2 pr-3 font-medium">State</th>
                  <th className="py-2 pr-3 font-medium">Events</th>
                  <th className="py-2 pr-3 font-medium">Last sign-in</th>
                  <th className="py-2 font-medium">
                    <span className="sr-only">Actions</span>
                  </th>
                </tr>
              </thead>
              <tbody className="divide-y divide-stone-100">
                {settled.map((user) => (
                  <tr key={user.id}>
                    <td className="py-3 pr-3">
                      <p className="font-medium text-stone-900">{user.email}</p>
                      <p className="text-xs text-stone-500">
                        {user.name ?? "No name"}
                        {user.email === currentEmail && " · you"}
                      </p>
                    </td>
                    <td className="py-3 pr-3 text-stone-700">
                      {user.authMethod === "google" ? "Google" : `Password (${user.username})`}
                      {user.temporaryPasswordOutstanding && (
                        <span className="ml-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs text-amber-900">
                          temporary
                        </span>
                      )}
                      {user.locked && (
                        <span className="ml-1 rounded-full bg-red-100 px-2 py-0.5 text-xs text-red-900">
                          locked
                        </span>
                      )}
                    </td>
                    <td className="py-3 pr-3">
                      <select
                        aria-label={`Role for ${user.email}`}
                        value={user.role}
                        disabled={busyId === user.id}
                        onChange={(e) =>
                          act(user.id, () =>
                            api.updateAdminUser(user.id, {
                              role: e.target.value as AdminRole,
                            }),
                          )
                        }
                        className="min-h-9 rounded-md border border-stone-300 bg-white px-2 text-sm"
                      >
                        <option value="host">{ROLE_LABEL.host}</option>
                        <option value="super_admin">{ROLE_LABEL.super_admin}</option>
                      </select>
                    </td>
                    <td className="py-3 pr-3">
                      <StateBadge user={user} />
                    </td>
                    <td className="py-3 pr-3 tabular-nums text-stone-700">
                      {user.ownedEventCount}
                    </td>
                    <td className="py-3 pr-3 text-xs text-stone-500">
                      {user.lastLoginAt ? formatDateTime(user.lastLoginAt) : "never"}
                    </td>
                    <td className="py-3">
                      <div className="flex justify-end gap-2">
                        {user.authMethod === "password" && (
                          <Button
                            variant="secondary"
                            disabled={busyId === user.id}
                            onClick={async () => {
                              setBusyId(user.id);
                              setError(null);
                              try {
                                const secret = await api.issueTemporaryPassword(user.id);
                                setIssued({ email: user.email, secret });
                                await load();
                              } catch (err) {
                                setError(err);
                              } finally {
                                setBusyId(null);
                              }
                            }}
                          >
                            Reset password
                          </Button>
                        )}
                        {user.status === "active" ? (
                          <Button
                            variant="danger"
                            disabled={busyId === user.id}
                            onClick={() => act(user.id, () => api.withdrawAdminUser(user.id))}
                          >
                            Withdraw
                          </Button>
                        ) : (
                          <Button
                            variant="secondary"
                            disabled={busyId === user.id}
                            onClick={() =>
                              act(user.id, () =>
                                api.activateAdminUser(user.id, user.role as AdminRole),
                              )
                            }
                          >
                            Restore
                          </Button>
                        )}
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
        <p className="mt-4 text-xs text-stone-500">
          Withdrawing takes effect on the account&rsquo;s next request, not at their next
          sign-in. Events they own keep working and stay assigned to them until transferred.
        </p>
      </Card>

      {creating && (
        <CreateAdminModal
          onClose={() => setCreating(false)}
          onCreated={async (secret, email) => {
            setCreating(false);
            if (secret) setIssued({ email, secret });
            await load();
          }}
        />
      )}

      {issued && (
        <TemporaryPasswordDialog
          email={issued.email}
          password={issued.secret.password}
          expiresAt={issued.secret.expires_at}
          onClose={() => setIssued(null)}
        />
      )}
    </div>
  );
}

/**
 * Creating an account. The kind is chosen here and never again — it cannot be edited later,
 * because converting between them would silently change what evidence is accepted for that
 * identity (design D16). The form says so rather than letting it be discovered.
 */
function CreateAdminModal({
  onClose,
  onCreated,
}: {
  onClose: () => void;
  onCreated: (secret: TemporaryPassword | null, email: string) => void | Promise<void>;
}) {
  const [authMethod, setAuthMethod] = useState<AuthMethod>("google");
  const [email, setEmail] = useState("");
  const [name, setName] = useState("");
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [role, setRole] = useState<AdminRole>("host");
  const [error, setError] = useState<unknown>(null);
  const [busy, setBusy] = useState(false);

  async function submit() {
    setBusy(true);
    setError(null);
    try {
      await api.createAdminUser({
        email,
        name: name || null,
        role,
        auth_method: authMethod,
        username: authMethod === "password" ? username : null,
        temporary_password: authMethod === "password" ? password : null,
      });
      // The password was typed here, so it is already known — no need to echo it back.
      await onCreated(null, email);
    } catch (err) {
      setError(err);
      setBusy(false);
    }
  }

  return (
    <Modal
      title="Add user"
      onClose={onClose}
      footer={
        <div className="flex justify-end gap-2">
          <Button variant="ghost" onClick={onClose}>
            Cancel
          </Button>
          <Button onClick={submit} disabled={busy}>
            {busy ? "Creating…" : "Create"}
          </Button>
        </div>
      }
    >
      <div className="space-y-3">
        <Field
          label="How they sign in"
          hint="Fixed once created. To move somebody between the two, create a new account and withdraw the old one."
        >
          <select
            value={authMethod}
            onChange={(e) => setAuthMethod(e.target.value as AuthMethod)}
            className={inputClass}
          >
            <option value="google">Google account</option>
            <option value="password">Username and password</option>
          </select>
        </Field>

        <Field label="Email">
          <input
            type="email"
            required
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            className={inputClass}
          />
        </Field>

        <Field label="Name">
          <input value={name} onChange={(e) => setName(e.target.value)} className={inputClass} />
        </Field>

        <Field label="Role">
          <select
            value={role}
            onChange={(e) => setRole(e.target.value as AdminRole)}
            className={inputClass}
          >
            <option value="host">Host — only the events they own</option>
            <option value="super_admin">Super admin — everything</option>
          </select>
        </Field>

        {authMethod === "password" && (
          <>
            <Field label="Username" hint="Compared without regard to capitalisation.">
              <input
                value={username}
                onChange={(e) => setUsername(e.target.value)}
                className={inputClass}
              />
            </Field>
            <Field
              label="Temporary password"
              hint="They must change it before they can do anything. At least 12 characters."
            >
              <input
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                className={inputClass}
              />
            </Field>
          </>
        )}

        <ErrorNote error={error} />
      </div>
    </Modal>
  );
}
