"use client";

import { ApiError } from "@/lib/api/browser";

/**
 * Shared admin primitives.
 *
 * Small on purpose. The admin area is a working tool used under time pressure — its job is
 * to be legible on a phone at 11pm, not to be a design system.
 */

export function Card({
  title,
  children,
  actions,
}: {
  title?: string;
  children: React.ReactNode;
  actions?: React.ReactNode;
}) {
  return (
    <section className="rounded-xl border border-stone-200 bg-white p-4 sm:p-5">
      {(title || actions) && (
        <header className="mb-4 flex items-center justify-between gap-3">
          {title && <h2 className="text-sm font-semibold text-stone-900">{title}</h2>}
          {actions}
        </header>
      )}
      {children}
    </section>
  );
}

export function Button({
  variant = "primary",
  className = "",
  // A bare <button> is type="submit". Inside a form that means an action button meant to
  // open a modal also submits the form — which reloads the page and discards the state the
  // click just set, so the button looks like it did nothing at all. Submitting is the rare
  // case and it can ask for itself with type="submit".
  type = "button",
  ...props
}: React.ButtonHTMLAttributes<HTMLButtonElement> & {
  variant?: "primary" | "secondary" | "danger" | "ghost";
}) {
  const styles = {
    primary: "bg-stone-900 text-white hover:bg-stone-800",
    secondary: "border border-stone-300 bg-white text-stone-800 hover:bg-stone-50",
    danger: "border border-red-300 bg-white text-red-700 hover:bg-red-50",
    ghost: "text-stone-600 hover:bg-stone-100 hover:text-stone-900",
  }[variant];

  return (
    <button
      {...props}
      type={type}
      // 44px minimum target: this is used on a phone, often one-handed (PRD §9.4).
      className={`inline-flex min-h-11 items-center justify-center gap-2 rounded-md px-4 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${styles} ${className}`}
    />
  );
}

export function Field({
  label,
  hint,
  error,
  children,
}: {
  label: string;
  hint?: string;
  /** A refusal that belongs to this field. Replaces the hint — showing both would leave the
   * advice that produced the error sitting directly under the error. */
  error?: string | null;
  children: React.ReactNode;
}) {
  return (
    <label className="block">
      <span className="mb-1 block text-xs font-medium text-stone-600">{label}</span>
      {children}
      {error ? (
        <span className="mt-1 block text-xs text-red-700">{error}</span>
      ) : (
        hint && <span className="mt-1 block text-xs text-stone-500">{hint}</span>
      )}
    </label>
  );
}

export const inputClass =
  "w-full min-h-11 rounded-md border border-stone-300 bg-white px-3 text-sm text-stone-900 focus:border-stone-500 focus:outline-none focus:ring-2 focus:ring-stone-200";

const STATUS_STYLES: Record<string, string> = {
  accepted: "bg-emerald-100 text-emerald-900",
  declined: "bg-stone-200 text-stone-700",
  cancelled: "bg-red-100 text-red-900",
  pending: "bg-amber-100 text-amber-900",
  opened: "bg-sky-100 text-sky-900",
  expired: "bg-stone-200 text-stone-600",
  queued: "bg-amber-100 text-amber-900",
  sending: "bg-sky-100 text-sky-900",
  sent: "bg-emerald-100 text-emerald-900",
  delivered: "bg-emerald-100 text-emerald-900",
  read: "bg-emerald-100 text-emerald-900",
  failed: "bg-red-100 text-red-900",
  skipped: "bg-stone-200 text-stone-600",
};

export function StatusBadge({ status }: { status: string }) {
  return (
    <span
      className={`inline-block whitespace-nowrap rounded-full px-2 py-0.5 text-xs font-medium ${
        STATUS_STYLES[status] ?? "bg-stone-200 text-stone-700"
      }`}
    >
      {status.replace(/_/g, " ")}
    </span>
  );
}

/**
 * Renders an error the way its cause deserves.
 *
 * A 403 is not a failure the user can retry out of — telling someone to "try again" when
 * their role simply does not permit the action wastes their time and hides the real answer.
 */
export function ErrorNote({ error }: { error: unknown }): React.ReactElement | null {
  if (!error) return null;

  if (error instanceof ApiError && error.isForbidden) {
    return (
      <p role="alert" className="rounded-lg bg-amber-50 px-3 py-2 text-sm text-amber-900">
        Your role does not allow this. Ask a super admin if you need it.
      </p>
    );
  }
  if (error instanceof ApiError && error.isUnauthenticated) {
    return (
      <p role="alert" className="rounded-lg bg-amber-50 px-3 py-2 text-sm text-amber-900">
        Your session expired.{" "}
        <a href="/admin/signin" className="font-medium underline">
          Sign in again
        </a>
        .
      </p>
    );
  }
  return (
    <p role="alert" className="rounded-lg bg-red-50 px-3 py-2 text-sm text-red-700">
      {error instanceof Error ? error.message : "Something went wrong."}
    </p>
  );
}

export function Empty({ children }: { children: React.ReactNode }) {
  return <p className="py-8 text-center text-sm text-stone-500">{children}</p>;
}

/** aria-busy plus visible text: a screen reader announces the wait, not just a spinner. */
export function Loading({ label = "Loading…" }: { label?: string }) {
  return (
    <p aria-busy="true" className="py-8 text-center text-sm text-stone-500">
      {label}
    </p>
  );
}

export function formatDateTime(iso: string, timeZone = "Asia/Dhaka"): string {
  return new Date(iso).toLocaleString("en-GB", {
    timeZone,
    day: "numeric",
    month: "short",
    year: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  });
}

export function formatDate(iso: string, timeZone = "Asia/Dhaka"): string {
  return new Date(iso).toLocaleDateString("en-GB", {
    timeZone,
    weekday: "short",
    day: "numeric",
    month: "short",
    year: "numeric",
  });
}
