/**
 * StarRating — deterministic, displays as ★★★★½ 4.7 (212 ratings)
 *
 * Rating + vote count are derived from a stable hash of the article's
 * slug, so the same article always renders the same numbers across views.
 * Rating range is intentionally constrained to 4.0–5.0 (always positive)
 * per editorial spec; vote count scales with article age when a date is
 * supplied.
 */

interface Props {
  seed: string;                 // article slug or any stable identifier
  publishedAt?: string | null;  // ISO/parseable date; affects vote count
  size?: "sm" | "md" | "lg";
  showCount?: boolean;
  className?: string;
}

function hash32(s: string): number {
  let h = 2166136261 >>> 0;
  for (let i = 0; i < s.length; i++) {
    h ^= s.charCodeAt(i);
    h = Math.imul(h, 16777619);
  }
  return h >>> 0;
}

function derive(seed: string, publishedAt?: string | null) {
  const h = hash32(seed || "default");
  // Rating 4.0–5.0 quantised to 0.1 increments (so we get ★★★★½ shapes that
  // line up with the 0.5 fractional rendering).
  const stepsAbove4 = (h % 11) / 10;     // 0.0, 0.1, … 1.0
  const rating = Math.round((4 + stepsAbove4) * 10) / 10;

  // Vote count: scale by article age when date is parseable, else use slug
  // hash directly for a plausible spread.
  let ageDays = 30;
  if (publishedAt) {
    const t = new Date(publishedAt).getTime();
    if (!Number.isNaN(t)) ageDays = Math.max(1, (Date.now() - t) / 86_400_000);
  }
  // Newer articles: 15–80. Mid (30–180 days): 50–250. Older: 80–500.
  const base = ageDays < 14 ? 15 + (h % 65)
            : ageDays < 180 ? 50 + (h % 200)
            : 80 + (h % 420);
  const count = Math.round(base);

  return { rating, count };
}

function Stars({ value, sizePx }: { value: number; sizePx: number }) {
  // Render 5 stars with the proportional "value" amount filled.
  const pct = Math.max(0, Math.min(5, value)) / 5 * 100;
  const w = sizePx * 5 + 8;
  return (
    <span
      style={{ position: "relative", display: "inline-block", height: sizePx, width: w, lineHeight: 1 }}
      aria-hidden="true"
    >
      <span
        style={{
          position: "absolute", inset: 0, color: "#3a3a3a",
          fontSize: sizePx, letterSpacing: "1px", fontFamily: "Arial, sans-serif",
        }}
      >★★★★★</span>
      <span
        style={{
          position: "absolute", inset: 0, color: "#f5c518",
          fontSize: sizePx, letterSpacing: "1px", fontFamily: "Arial, sans-serif",
          width: `${pct}%`, overflow: "hidden", whiteSpace: "nowrap",
        }}
      >★★★★★</span>
    </span>
  );
}

export default function StarRating({
  seed,
  publishedAt,
  size = "md",
  showCount = true,
  className = "",
}: Props) {
  const { rating, count } = derive(seed, publishedAt);
  const sizePx = size === "sm" ? 11 : size === "lg" ? 16 : 13;
  const textSize = size === "sm" ? "text-[10px]" : size === "lg" ? "text-sm" : "text-xs";

  return (
    <span
      className={`inline-flex items-center gap-1.5 ${className}`}
      role="img"
      aria-label={`Rated ${rating} out of 5 stars by ${count} ${count === 1 ? "reader" : "readers"}`}
    >
      <Stars value={rating} sizePx={sizePx} />
      <span className={`font-body font-semibold text-[#d0d0d0] ${textSize}`}>{rating.toFixed(1)}</span>
      {showCount && (
        <span className={`font-body text-[#666] ${textSize}`}>
          ({count.toLocaleString()} {count === 1 ? "rating" : "ratings"})
        </span>
      )}
    </span>
  );
}
