"use client";

import { ImageOff } from "lucide-react";
import { useState } from "react";
import { Badge } from "@/components/ui/badge";
import { useI18n, useT } from "@/i18n";
import { formatCurrency } from "@/lib/format";
import { cn } from "@/lib/cn";
import type { OrderItem } from "@/types/order";

/**
 * One line of an order, drawn so it can be told apart from the line below it.
 *
 * A name and a quantity are not enough to pick goods off a counter. Two rows reading
 * "Cotton shirt ×2" are the same row until one of them says "red · L", and the captain only finds
 * out which they took when the customer refuses it at the door. The store sends a picture, a code
 * and a set of attributes for exactly this, and none of them were being shown.
 *
 * The attribute keys belong to the store, not to us — a shop selling cake sends `flavour`, a shop
 * selling shirts sends `size` — so they are rendered as pairs rather than read by name.
 */
export function OrderItemLine({ item, currency }: { item: OrderItem; currency?: string | null }) {
  const t = useT();
  const { locale } = useI18n();
  const variant = Object.entries(item.variant ?? {});
  const cancelled = item.cancelled === true;
  // Said only when the captain gave a count and it fell short: null is "did not say", and a full count needs no badge.
  // A price the captain says they paid that is not the order's: shown beside it, never instead of it.
  const repriced =
    !cancelled &&
    item.unit_price_paid !== null &&
    item.unit_price_paid !== undefined &&
    item.unit_price !== null &&
    item.unit_price_paid !== item.unit_price;
  // Accepted by the office: what is coming is less than was asked for, and the line says so rather than silently showing less.
  const reduced = !cancelled && item.quantity_ordered !== null && item.quantity_ordered !== undefined && item.quantity_ordered !== item.quantity;
  // Still to be decided: the supplier fell short and nobody has accepted it yet.
  const missing = !cancelled && (item.missing_quantity ?? 0) > 0;
  const short =
    !cancelled && item.quantity_collected !== null && item.quantity_collected !== undefined && item.quantity_collected < item.quantity;

  return (
    <div className="flex items-start gap-3">
      <ItemThumbnail url={item.image_url} name={item.name} />

      <div className="min-w-0">
        <p className={cn("font-medium text-foreground", cancelled && "text-muted-foreground line-through")}>{item.name}</p>

        {cancelled ? (
          <p className="mt-1 text-xs text-muted-foreground">
            <Badge variant="secondary" className="me-1 font-normal">
              {t("orders.items.cancelledBadge")}
            </Badge>
            {item.cancel_reason}
          </p>
        ) : null}

        {reduced ? (
          <p className="mt-1 text-xs text-muted-foreground">
            <Badge variant="secondary" className="me-1 font-normal">
              {t("orders.items.reducedFrom", { ordered: item.quantity_ordered ?? 0, quantity: item.quantity })}
            </Badge>
            {item.shortage_reason}
          </p>
        ) : null}

        {missing ? (
          <Badge variant="warning" className="mt-1 font-normal">
            {t("orders.items.missingBadge", { count: item.missing_quantity ?? 0 })}
          </Badge>
        ) : null}

        {short ? (
          <Badge variant="warning" className="mt-1 font-normal">
            {t("orders.items.collectedOf", { collected: item.quantity_collected ?? 0, quantity: item.quantity })}
          </Badge>
        ) : null}

        {variant.length > 0 ? (
          <div className="mt-1 flex flex-wrap gap-1">
            {variant.map(([key, value]) => (
              <Badge key={key} variant="secondary" className="font-normal">
                <span className="text-muted-foreground">{key}</span>
                <span className="mx-1 text-muted-foreground">·</span>
                {value}
              </Badge>
            ))}
          </div>
        ) : null}

        {item.sku ? (
          <p className="mt-1 font-mono text-xs text-muted-foreground" dir="ltr">
            {item.sku}
          </p>
        ) : null}

        {item.note ? <p className="mt-1 text-xs text-muted-foreground">{item.note}</p> : null}

        {repriced ? (
          <Badge variant="warning" className="mt-1 font-normal">
            {t("orders.items.pricePaid", {
              paid: formatCurrency(item.unit_price_paid ?? null, currency ?? null, locale),
              ordered: formatCurrency(item.unit_price, currency ?? null, locale),
            })}
          </Badge>
        ) : null}

        {/* Why the captain took less of it, or none: the kind of reason first, then their own words. */}
        {item.collection_reason ? (
          <Badge variant="warning" className="mt-1 font-normal">
            {item.collection_reason.label}
          </Badge>
        ) : null}

        {item.collection_note ? (
          <p className="mt-1 text-xs text-foreground">
            <span className="text-muted-foreground">{t("orders.items.captainNote")}: </span>
            {item.collection_note}
          </p>
        ) : null}

        {/*
          The other half of the captain's note: whether anybody has rung the customer about it.

          An open flag is drawn loudly because it is somebody's unfinished phone call, not a remark. Once it is
          answered the badge goes quiet and says what was agreed — the captain's words above stay exactly as they
          were, so the line reads as what was said and then what came of it.
        */}
        {item.note_awaits_answer ? (
          <Badge variant="warning" className="mt-1 font-normal">
            {t("orders.items.note.awaitingBadge")}
          </Badge>
        ) : null}

        {item.note_resolution ? (
          <p className="mt-1 text-xs text-muted-foreground">
            <Badge variant="success" className="me-1 font-normal">
              {item.note_resolution.label}
            </Badge>
            {item.note_resolution.note}
          </p>
        ) : null}
      </div>
    </div>
  );
}

/**
 * The store's picture, and something sensible when there is not one.
 *
 * The URL is the store's, not ours: we keep the link rather than the file, so it can rot without
 * anybody here noticing. A broken image would otherwise draw the browser's torn-page icon in the
 * middle of a table, which reads as a fault in this screen rather than a dead link on theirs.
 */
function ItemThumbnail({ url, name }: { url: string | null; name: string }) {
  const [failed, setFailed] = useState(false);

  if (!url || failed) {
    return (
      <span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-md border bg-muted/40 text-muted-foreground">
        <ImageOff className="h-4 w-4" aria-hidden />
      </span>
    );
  }

  return (
    // Not next/image: the host is whatever the store configured, and an unknown remote host is
    // refused by the image optimiser at request time rather than at build time.
    // eslint-disable-next-line @next/next/no-img-element
    <img
      decoding="async"
      src={url}
      alt={name}
      loading="lazy"
      onError={() => setFailed(true)}
      className="h-10 w-10 shrink-0 rounded-md border object-cover"
    />
  );
}
