import type { Metadata } from "next";

const SITE_NAME = "Alqove";
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://alqove.com";

/**
 * The brand card generated by `app/opengraph-image.tsx`. Next only injects
 * the file-convention image into routes that declare no `openGraph` block of
 * their own — and every route built here declares one — so it is referenced
 * explicitly rather than left to be merged in.
 */
const DEFAULT_OG_IMAGE = `${SITE_URL}/opengraph-image`;

export interface MetadataOptions {
  title: string;
  description: string;
  image?: string;
  canonical?: string;
  noindex?: boolean;
}

export function buildMetadata(options: MetadataOptions): Metadata {
  const fullTitle = options.title.includes(SITE_NAME) ? options.title : `${options.title} — ${SITE_NAME}`;
  const image = options.image ?? DEFAULT_OG_IMAGE;

  return {
    title: fullTitle,
    description: options.description,
    alternates: options.canonical ? { canonical: options.canonical } : undefined,
    robots: options.noindex ? { index: false, follow: true } : undefined,
    openGraph: {
      title: fullTitle,
      description: options.description,
      type: "website",
      url: options.canonical ?? SITE_URL,
      siteName: SITE_NAME,
      images: [{ url: image, width: 1200, height: 630 }],
    },
    twitter: {
      card: "summary_large_image",
      title: fullTitle,
      description: options.description,
      images: [image],
    },
  };
}

export type ItemCondition = "NWT" | "NWOT" | "EUC" | "GUC" | "Fair" | "Poor";

export function mapConditionToSchema(condition: ItemCondition): string {
  return condition === "NWT"
    ? "https://schema.org/NewCondition"
    : "https://schema.org/UsedCondition";
}

export function formatPriceForSchema(priceCents: number): string {
  const dollars = priceCents / 100;
  return dollars.toFixed(2);
}

export interface ProductJsonLdInput {
  id: string;
  title: string;
  description: string;
  brand: string | null;
  condition: ItemCondition;
  price: number;
  imageUrl: string | null;
  /** Item lifecycle status; only "active" items are purchasable. */
  status?: string | null;
  /** Store name, surfaced as the schema.org seller. */
  sellerName?: string | null;
}

type Availability =
  | "https://schema.org/InStock"
  | "https://schema.org/OutOfStock";

export interface ProductJsonLd {
  "@context": "https://schema.org";
  "@type": "Product";
  name: string;
  description: string;
  image: string[];
  brand: { "@type": "Brand"; name: string } | undefined;
  offers: {
    "@type": "Offer";
    url: string;
    priceCurrency: "USD";
    price: string;
    availability: Availability;
    itemCondition: string;
    seller: { "@type": "Organization"; name: string } | undefined;
  };
}

function mapAvailability(status?: string | null): Availability {
  // Absent status defaults to available — the public detail endpoint only
  // serves listable items, so a missing value should not read as sold out.
  return status && status !== "active"
    ? "https://schema.org/OutOfStock"
    : "https://schema.org/InStock";
}

export function buildProductJsonLd(input: ProductJsonLdInput, canonicalUrl: string): ProductJsonLd {
  return {
    "@context": "https://schema.org",
    "@type": "Product",
    name: input.title,
    description: input.description,
    image: input.imageUrl ? [input.imageUrl] : [],
    brand: input.brand ? { "@type": "Brand", name: input.brand } : undefined,
    offers: {
      "@type": "Offer",
      url: canonicalUrl,
      priceCurrency: "USD",
      price: formatPriceForSchema(input.price),
      availability: mapAvailability(input.status),
      itemCondition: mapConditionToSchema(input.condition),
      seller: input.sellerName
        ? { "@type": "Organization", name: input.sellerName }
        : undefined,
    },
  };
}
