"use client";

import { useMemo, useOptimistic, useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import type { User } from "@/lib/types";
import { deleteUser, updateUser } from "@/app/actions";
import EditableCell from "./editable-cell";
import ConfirmDialog from "./confirm-dialog";
import CreateUserDialog from "./create-user-dialog";
import Toast, { type ToastMessage } from "./toast";

type Props = { initial: User[]; prefixPattern: string };
type SortDir = "asc" | "desc";
type SortKey = "f_username" | "last_update_time";
type OptimisticPatch = {
  f_username: string;
  field: keyof Pick<User, "f_password" | "t_username" | "t_password">;
  value: string;
};

function timeOf(t: string | null) {
  if (!t) return 0;
  const ms = Date.parse(t);
  return Number.isNaN(ms) ? 0 : ms;
}

function sortUsers(rows: User[], key: SortKey, dir: SortDir, prefix: string): User[] {
  return [...rows].sort((a, b) => {
    const ap = a.f_username.startsWith(prefix);
    const bp = b.f_username.startsWith(prefix);
    if (ap && !bp) return -1;
    if (!ap && bp) return 1;
    if (key === "f_username") {
      return dir === "asc"
        ? a.f_username.localeCompare(b.f_username)
        : b.f_username.localeCompare(a.f_username);
    }
    return dir === "asc"
      ? timeOf(a.last_update_time) - timeOf(b.last_update_time)
      : timeOf(b.last_update_time) - timeOf(a.last_update_time);
  });
}

function formatTime(t: string | null) {
  if (!t) return <em className="not-italic text-zinc-400">—</em>;
  const d = new Date(t);
  if (Number.isNaN(d.getTime())) return t;
  return d.toLocaleString(undefined, {
    month: "short",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
  });
}

function DeleteButton({
  label,
  onClick,
}: {
  label: string;
  onClick: () => void;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      aria-label={`Delete ${label}`}
      className="inline-flex h-7 w-7 items-center justify-center rounded-md text-zinc-400 transition-colors hover:bg-red-50 hover:text-red-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-400"
    >
      <span aria-hidden="true" className="text-base leading-none">
        ×
      </span>
    </button>
  );
}

export default function UsersTable({ initial, prefixPattern }: Props) {
  const router = useRouter();
  const [sortKey, setSortKey] = useState<SortKey>("last_update_time");
  const [sortDir, setSortDir] = useState<SortDir>("desc");
  const [editingKey, setEditingKey] = useState<string | null>(null);
  const [, startTransition] = useTransition();
  const [refreshing, setRefreshing] = useState(false);
  const [deleteTarget, setDeleteTarget] = useState<string | null>(null);
  const [deleting, setDeleting] = useState(false);
  const [deleteError, setDeleteError] = useState<string | null>(null);
  const [createOpen, setCreateOpen] = useState(false);
  const [toast, setToast] = useState<ToastMessage | null>(null);

  const [optimistic, applyOptimistic] = useOptimistic<User[], OptimisticPatch>(
    initial,
    (state, patch) =>
      state.map((row) =>
        row.f_username === patch.f_username ? { ...row, [patch.field]: patch.value } : row,
      ),
  );

  const sorted = useMemo(
    () => sortUsers(optimistic, sortKey, sortDir, prefixPattern),
    [optimistic, sortKey, sortDir, prefixPattern],
  );

  function saveCell(
    f_username: string,
    field: OptimisticPatch["field"],
    value: string,
  ) {
    return new Promise<{ ok: boolean; error?: string }>((resolve) => {
      startTransition(async () => {
        applyOptimistic({ f_username, field, value });
        const row = initial.find((r) => r.f_username === f_username);
        if (!row) return resolve({ ok: false, error: "row not found" });
        const next = {
          f_username: row.f_username,
          f_password: row.f_password,
          t_username: row.t_username,
          t_password: row.t_password,
          [field]: value,
        };
        const result = await updateUser(next);
        resolve(result.ok ? { ok: true } : { ok: false, error: result.error });
      });
    });
  }

  function refresh() {
    setRefreshing(true);
    startTransition(() => {
      router.refresh();
      setTimeout(() => setRefreshing(false), 400);
    });
  }

  function toggleSort(k: SortKey) {
    if (sortKey === k) setSortDir((d) => (d === "asc" ? "desc" : "asc"));
    else {
      setSortKey(k);
      setSortDir("desc");
    }
  }

  async function confirmDelete() {
    if (!deleteTarget) return;
    setDeleting(true);
    setDeleteError(null);
    const result = await deleteUser(deleteTarget);
    setDeleting(false);
    if (result.ok) {
      const deleted = deleteTarget;
      setDeleteTarget(null);
      setToast({ type: "success", message: `User ${deleted} deleted` });
    } else {
      setDeleteError(result.error);
    }
  }

  function HeaderTh({ k, label }: { k: SortKey; label: string }) {
    const active = sortKey === k;
    return (
      <button
        type="button"
        onClick={() => toggleSort(k)}
        className={`inline-flex items-center gap-1 text-[11px] font-medium uppercase tracking-wider transition-colors ${
          active ? "text-zinc-900" : "text-zinc-500 hover:text-zinc-900"
        }`}
      >
        {label}
        <span aria-hidden="true">{active ? (sortDir === "asc" ? "↑" : "↓") : "↕"}</span>
      </button>
    );
  }

  if (initial.length === 0) {
    return (
      <div>
        <PageHead
          count={0}
          onRefresh={refresh}
          refreshing={refreshing}
          onAdd={() => setCreateOpen(true)}
        />
        <div className="mt-6 rounded-2xl bg-white px-8 py-16 text-center ring-1 ring-zinc-200/60">
          <p className="text-sm text-zinc-500">
            No users yet. Click <span className="font-medium text-zinc-700">Add</span> to create one manually.
          </p>
        </div>
        <CreateUserDialog
        open={createOpen}
        onClose={() => setCreateOpen(false)}
        onSuccess={(name) =>
          setToast({ type: "success", message: `User ${name} created` })
        }
      />

      <Toast toast={toast} onDismiss={() => setToast(null)} />
      </div>
    );
  }

  return (
    <div>
      <PageHead
        count={optimistic.length}
        onRefresh={refresh}
        refreshing={refreshing}
        onAdd={() => setCreateOpen(true)}
      />

      <div className="mt-6 hidden overflow-hidden rounded-2xl bg-white ring-1 ring-zinc-200/60 sm:block">
        <table className="w-full table-fixed border-collapse">
          <thead>
            <tr className="bg-zinc-50/60">
              <th className="w-[18%] px-5 py-3 text-left">
                <HeaderTh k="f_username" label="From username" />
              </th>
              <th className="w-[18%] px-5 py-3 text-left text-[11px] font-medium uppercase tracking-wider text-zinc-500">
                From password
              </th>
              <th className="w-[18%] px-5 py-3 text-left text-[11px] font-medium uppercase tracking-wider text-zinc-500">
                To username
              </th>
              <th className="w-[18%] px-5 py-3 text-left text-[11px] font-medium uppercase tracking-wider text-zinc-500">
                To password
              </th>
              <th className="px-5 py-3 text-left">
                <HeaderTh k="last_update_time" label="Last update" />
              </th>
              <th className="w-12 px-3 py-3" aria-hidden="true" />
            </tr>
          </thead>
          <tbody className="divide-y divide-zinc-100">
            {sorted.map((row) => {
              const k = (f: string) => `${row.f_username}::${f}`;
              return (
                <tr key={row.f_username} className="transition-colors hover:bg-zinc-50/60">
                  <td className="px-5 py-3 align-middle font-mono text-[13px] font-semibold text-zinc-900">
                    {row.f_username}
                  </td>
                  <td className="px-5 py-3 align-middle">
                    <EditableCell
                      value={row.f_password}
                      label={`from password for ${row.f_username}`}
                      isCurrentlyEditing={editingKey === k("f_password")}
                      onEditStart={() => setEditingKey(k("f_password"))}
                      onEditEnd={() => setEditingKey(null)}
                      onSave={(v) => saveCell(row.f_username, "f_password", v)}
                    />
                  </td>
                  <td className="px-5 py-3 align-middle">
                    <EditableCell
                      value={row.t_username}
                      label={`to username for ${row.f_username}`}
                      isCurrentlyEditing={editingKey === k("t_username")}
                      onEditStart={() => setEditingKey(k("t_username"))}
                      onEditEnd={() => setEditingKey(null)}
                      onSave={(v) => saveCell(row.f_username, "t_username", v)}
                    />
                  </td>
                  <td className="px-5 py-3 align-middle">
                    <EditableCell
                      value={row.t_password}
                      label={`to password for ${row.f_username}`}
                      isCurrentlyEditing={editingKey === k("t_password")}
                      onEditStart={() => setEditingKey(k("t_password"))}
                      onEditEnd={() => setEditingKey(null)}
                      onSave={(v) => saveCell(row.f_username, "t_password", v)}
                    />
                  </td>
                  <td className="px-5 py-3 align-middle text-xs text-zinc-500">
                    {formatTime(row.last_update_time)}
                  </td>
                  <td className="px-3 py-3 text-right align-middle">
                    <DeleteButton
                      label={row.f_username}
                      onClick={() => {
                        setDeleteError(null);
                        setDeleteTarget(row.f_username);
                      }}
                    />
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>

      <div className="mt-6 space-y-3 sm:hidden">
        {sorted.map((row) => {
          const k = (f: string) => `${row.f_username}::${f}`;
          return (
            <div key={row.f_username} className="rounded-2xl bg-white p-5 ring-1 ring-zinc-200/60">
              <div className="flex items-center justify-between gap-2">
                <span className="font-mono text-base font-semibold text-zinc-900">
                  {row.f_username}
                </span>
                <div className="flex items-center gap-2">
                  <span className="text-[11px] text-zinc-500">
                    {formatTime(row.last_update_time)}
                  </span>
                  <DeleteButton
                    label={row.f_username}
                    onClick={() => {
                      setDeleteError(null);
                      setDeleteTarget(row.f_username);
                    }}
                  />
                </div>
              </div>
              <dl className="mt-4 space-y-3 border-t border-zinc-100 pt-4">
                <MobileRow label="From PW">
                  <EditableCell
                    value={row.f_password}
                    label={`from password for ${row.f_username}`}
                    isCurrentlyEditing={editingKey === k("f_password")}
                    onEditStart={() => setEditingKey(k("f_password"))}
                    onEditEnd={() => setEditingKey(null)}
                    onSave={(v) => saveCell(row.f_username, "f_password", v)}
                  />
                </MobileRow>
                <MobileRow label="To User">
                  <EditableCell
                    value={row.t_username}
                    label={`to username for ${row.f_username}`}
                    isCurrentlyEditing={editingKey === k("t_username")}
                    onEditStart={() => setEditingKey(k("t_username"))}
                    onEditEnd={() => setEditingKey(null)}
                    onSave={(v) => saveCell(row.f_username, "t_username", v)}
                  />
                </MobileRow>
                <MobileRow label="To PW">
                  <EditableCell
                    value={row.t_password}
                    label={`to password for ${row.f_username}`}
                    isCurrentlyEditing={editingKey === k("t_password")}
                    onEditStart={() => setEditingKey(k("t_password"))}
                    onEditEnd={() => setEditingKey(null)}
                    onSave={(v) => saveCell(row.f_username, "t_password", v)}
                  />
                </MobileRow>
              </dl>
            </div>
          );
        })}
      </div>

      <CreateUserDialog
        open={createOpen}
        onClose={() => setCreateOpen(false)}
        onSuccess={(name) =>
          setToast({ type: "success", message: `User ${name} created` })
        }
      />

      <Toast toast={toast} onDismiss={() => setToast(null)} />

      <ConfirmDialog
        open={!!deleteTarget}
        onCancel={() => {
          if (!deleting) setDeleteTarget(null);
        }}
        onConfirm={confirmDelete}
        title={`Delete ${deleteTarget ?? ""}?`}
        message={
          <>
            <p>
              Permanently remove the user pairing for{" "}
              <code className="rounded bg-zinc-100 px-1.5 py-0.5 font-mono text-xs text-zinc-700">
                {deleteTarget}
              </code>{" "}
              from the users table. This action cannot be undone and only
              removes the pairing — the underlying account row stays.
            </p>
            {deleteError && (
              <p className="mt-3 font-mono text-[11px] text-red-600" role="alert">
                {deleteError}
              </p>
            )}
          </>
        }
        confirmLabel="Delete"
        destructive
        pending={deleting}
      />
    </div>
  );
}

function MobileRow({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <div className="grid grid-cols-[max-content_1fr] items-baseline gap-x-4">
      <dt className="text-[11px] font-medium uppercase tracking-wider text-zinc-500">{label}</dt>
      <dd className="min-w-0">{children}</dd>
    </div>
  );
}

function PageHead({
  count,
  onRefresh,
  refreshing,
  onAdd,
}: {
  count: number;
  onRefresh: () => void;
  refreshing: boolean;
  onAdd: () => void;
}) {
  return (
    <div className="flex flex-wrap items-end justify-between gap-4">
      <div>
        <p className="text-[11px] font-medium uppercase tracking-wider text-zinc-500">Table</p>
        <h1 className="mt-1 text-2xl font-semibold tracking-tight text-zinc-900 sm:text-3xl">
          Users
          <span className="ml-2 align-middle text-base font-medium text-zinc-400">{count}</span>
        </h1>
      </div>
      <div className="flex items-center gap-2">
        <button
          type="button"
          onClick={onRefresh}
          disabled={refreshing}
          className="inline-flex items-center gap-2 rounded-full bg-white px-4 py-2 text-xs font-medium text-zinc-700 shadow-sm ring-1 ring-zinc-200 transition-colors hover:bg-zinc-50 hover:text-zinc-900 disabled:opacity-60"
        >
          <span aria-hidden="true" className={refreshing ? "inline-block animate-spin" : ""}>
            ⟳
          </span>
          Refresh
        </button>
        <button
          type="button"
          onClick={onAdd}
          className="inline-flex items-center gap-1.5 rounded-full bg-zinc-900 px-4 py-2 text-xs font-medium text-white shadow-sm transition-colors hover:bg-zinc-700"
        >
          <span aria-hidden="true">+</span>
          Add
        </button>
      </div>
    </div>
  );
}
