"use client";

import Link from "next/link";
import type { ReactNode } from "react";
import { ChevronRight, CircleX, X } from "lucide-react";
import SafeImage from "@/components/ui/SafeImage";
import { cn } from "@/lib/utils";
import type { EnquiryStage, OrderStage } from "@/lib/chat/types";
import { useChatBlocks } from "../ChatBlockContext";

export const inputClass =
  "w-full rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm text-gray-800 placeholder:text-gray-400 focus:border-[#CC1414] focus:outline-none focus:ring-2 focus:ring-[#CC1414]/20 disabled:bg-gray-50 disabled:text-gray-500";

export const labelClass = "mb-1 block text-[11px] font-semibold uppercase tracking-wide text-gray-500";

export const primaryButtonClass =
  "inline-flex items-center justify-center gap-1.5 rounded-lg bg-[#CC1414] px-3 py-2 text-xs font-semibold text-white transition-colors hover:bg-[#A50E0E] disabled:cursor-not-allowed disabled:opacity-60";

export const secondaryButtonClass =
  "inline-flex items-center justify-center gap-1 rounded-lg border border-red-200 bg-red-50 px-2.5 py-1.5 text-[11px] font-semibold text-[#A50E0E] transition-colors hover:bg-red-100 disabled:cursor-not-allowed disabled:opacity-60";

/**
 * Server hrefs are app-relative paths. Anything else (absolute, protocol-
 * relative, `javascript:`) is dropped rather than rendered as a link, as are
 * control characters and backslashes: browsers strip tab/CR/LF and read `\`
 * as `/`, so "/\t/evil.com" would otherwise become protocol-relative (same
 * rule as the login page's `safeRedirect`).
 */
export function safeHref(href: string | null | undefined): string | null {
  if (typeof href !== "string" || !href.startsWith("/") || href.startsWith("//")) return null;
  if (/[\u0000-\u001F\u007F\\]/.test(href)) return null;
  return href;
}

/** The white card every rich block sits in. */
export function BlockCard({ children, className }: { children: ReactNode; className?: string }) {
  return (
    <div className={cn("w-full rounded-xl bg-white p-3 text-xs text-gray-700 shadow-sm ring-1 ring-gray-100", className)}>
      {children}
    </div>
  );
}

export function Chip({ tone, children }: { tone: string; children: ReactNode }) {
  return (
    <span
      className={cn(
        "inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-[10px] font-semibold ring-1 ring-inset",
        tone
      )}
    >
      {children}
    </span>
  );
}

const ORDER_TONES: Record<OrderStage, string> = {
  placed: "bg-amber-50 text-amber-700 ring-amber-200",
  confirmed: "bg-blue-50 text-blue-700 ring-blue-200",
  loading: "bg-indigo-50 text-indigo-700 ring-indigo-200",
  dispatched: "bg-sky-50 text-sky-700 ring-sky-200",
  delivered: "bg-emerald-50 text-emerald-700 ring-emerald-200",
  completed: "bg-emerald-50 text-emerald-700 ring-emerald-200",
  cancelled: "bg-red-50 text-red-700 ring-red-200",
};

const ENQUIRY_TONES: Record<EnquiryStage, string> = {
  sent: "bg-gray-50 text-gray-700 ring-gray-200",
  bidding: "bg-amber-50 text-amber-700 ring-amber-200",
  best_found: "bg-emerald-50 text-emerald-700 ring-emerald-200",
  offer_ready: "bg-emerald-50 text-emerald-700 ring-emerald-200",
  closed: "bg-gray-50 text-gray-600 ring-gray-200",
  ordered: "bg-blue-50 text-blue-700 ring-blue-200",
  cancelled: "bg-red-50 text-red-700 ring-red-200",
};

const NEUTRAL_TONE = "bg-gray-50 text-gray-700 ring-gray-200";

export function OrderStageChip({ stage, label }: { stage: OrderStage; label?: string }) {
  const { copy } = useChatBlocks();
  return <Chip tone={ORDER_TONES[stage] ?? NEUTRAL_TONE}>{label || copy.orderStages[stage] || stage}</Chip>;
}

export function EnquiryStageChip({ stage }: { stage: EnquiryStage }) {
  const { copy } = useChatBlocks();
  return <Chip tone={ENQUIRY_TONES[stage] ?? NEUTRAL_TONE}>{copy.enquiryStages[stage] ?? stage}</Chip>;
}

/** An in-app link out of the chat; closes the sheet on phones as it navigates. */
export function ChatLink({
  href,
  children,
  className,
  arrow = false,
}: {
  href: string | null | undefined;
  children: ReactNode;
  className?: string;
  arrow?: boolean;
}) {
  const { onNavigate } = useChatBlocks();
  const safe = safeHref(href);
  if (!safe) return null;
  return (
    <Link
      href={safe}
      onClick={onNavigate}
      className={cn("inline-flex items-center gap-0.5 font-semibold text-[#CC1414] hover:underline", className)}
    >
      {children}
      {arrow ? <ChevronRight size={12} aria-hidden /> : null}
    </Link>
  );
}

export function Thumb({ src, alt }: { src: string | null; alt: string }) {
  return (
    <span className="relative inline-flex h-9 w-9 shrink-0 overflow-hidden rounded-lg bg-gray-50 ring-1 ring-gray-100">
      <SafeImage src={src || "/logo.png"} alt={alt} fill sizes="36px" unoptimized className="object-contain" />
    </span>
  );
}

/** Label/value row used by the detail cards. */
export function Row({ label, children }: { label: string; children: ReactNode }) {
  return (
    <div className="flex items-baseline justify-between gap-3">
      <dt className="text-gray-500">{label}</dt>
      <dd className="min-w-0 text-right font-medium text-gray-800">{children}</dd>
    </div>
  );
}

export function FieldError({ id, message }: { id: string; message?: string }) {
  if (!message) return null;
  return (
    <p id={id} className="mt-1 text-[11px] text-red-600">
      {message}
    </p>
  );
}

/** Label + control + error. The control should set `aria-describedby={errorIdFor(id)}` when `error` is set. */
export function Field({
  id,
  label,
  required,
  optional,
  error,
  children,
}: {
  id: string;
  label: string;
  required?: boolean;
  optional?: boolean;
  error?: string;
  children: ReactNode;
}) {
  const { copy } = useChatBlocks();
  return (
    <div>
      <label htmlFor={id} className={labelClass}>
        {label}
        {required ? <span className="text-[#CC1414]"> *</span> : null}
        {optional ? <span className="font-normal normal-case text-gray-400"> ({copy.optional})</span> : null}
      </label>
      {children}
      <FieldError id={errorIdFor(id)} message={error} />
    </div>
  );
}

export const errorIdFor = (id: string) => `${id}-error`;

/** Accessibility props for a control whose `Field` may show an error. */
export function describedBy(id: string, error?: string) {
  return { "aria-invalid": error ? true : undefined, "aria-describedby": error ? errorIdFor(id) : undefined };
}

/** "20 MT" — joins quantity and unit, skipping whichever is missing. */
export function formatQuantity(quantity: number | null, unit: string | null): string | null {
  if (quantity === null || quantity === undefined) return null;
  return [String(quantity), unit].filter(Boolean).join(" ");
}

/** Laravel 422 keys arrive as `action.payload.quantity` (or bare); keep the field name. */
export function fieldErrorsFrom(errors: Record<string, string[]>): Record<string, string> {
  const out: Record<string, string> = {};
  for (const [key, messages] of Object.entries(errors)) {
    const field = key.split(".").pop() ?? key;
    if (messages?.length && !out[field]) out[field] = messages[0];
  }
  return out;
}

/* ------------------------------------------------------------- form close */

/**
 * Closing a form (enquiry / callback) without submitting it. The form
 * collapses to a one-line note (kept across reloads) and the main menu is
 * offered so the conversation doesn't dead-end. Not allowed while that
 * form's own submit is in flight.
 */
export function useFormDismiss(formKey: string) {
  const { copy, busy, pendingFormKey, isFormDismissed, dismissForm, sendAction } = useChatBlocks();
  const canClose = pendingFormKey !== formKey;
  const close = () => {
    if (!canClose) return;
    dismissForm(formKey);
    if (!busy) void sendAction({ type: "menu" }, { echo: copy.cancel });
  };
  return { dismissed: isFormDismissed(formKey), canClose, close };
}

export function FormHeader({ title, onClose, canClose }: { title: string; onClose: () => void; canClose: boolean }) {
  const { copy } = useChatBlocks();
  return (
    <div className="mb-2 flex items-start justify-between gap-2">
      <p className="text-sm font-semibold text-gray-900">{title}</p>
      <button
        type="button"
        onClick={onClose}
        disabled={!canClose}
        className="-mr-1 -mt-1 shrink-0 rounded-md p-1 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-700 disabled:cursor-not-allowed disabled:opacity-40"
        aria-label={copy.closeForm}
        title={copy.closeForm}
      >
        <X size={15} aria-hidden />
      </button>
    </div>
  );
}

export function FormClosedNote() {
  const { copy } = useChatBlocks();
  return (
    <BlockCard>
      <p className="flex items-center gap-1.5 font-medium text-gray-500">
        <CircleX size={14} aria-hidden /> {copy.formClosed}
      </p>
    </BlockCard>
  );
}

export const cancelButtonClass =
  "inline-flex items-center justify-center rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-semibold text-gray-700 transition-colors hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-60";
