/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

import React from "react";

function getDirection(changePercent) {
  const value = parseFloat(
    String(changePercent ?? "").replace(/[^0-9.-]/g, "")
  );
  if (!Number.isFinite(value) || value === 0) {
    return "flat";
  }
  return value > 0 ? "up" : "down";
}

// The feed hands us Merino's preformatted values: last_price like "$559.44 USD"
// and todays_change_perc like "+0.2" (US formatting, no percent sign). Parse the
// number back out and reformat it for the viewer's locale, so the decimal
// separator, currency symbol, and percent sign follow local conventions.
function parseAmount(raw) {
  const value = parseFloat(String(raw ?? "").replace(/[^0-9.-]/g, ""));
  return Number.isFinite(value) ? value : null;
}

// How many decimal places the source value carries, so the formatted output
// keeps the same precision Merino sent (e.g. "0.2" stays 1 place, "0.00" stays 2).
function decimalPlaces(raw) {
  const decimals = String(raw ?? "").match(/\.(\d+)/);
  return decimals ? decimals[1].length : 0;
}

function formatPrice(price, locale) {
  const value = parseAmount(price);
  if (value === null) {
    return String(price ?? "");
  }
  return new Intl.NumberFormat(locale, {
    style: "currency",
    // For now the feed only returns US stocks and ETFs, which are all priced
    // in USD, so the currency is fixed here.
    currency: "USD",
    currencyDisplay: "narrowSymbol",
  }).format(value);
}

function formatChange(change, locale) {
  const value = parseAmount(change);
  if (value === null) {
    return String(change ?? "");
  }
  const places = decimalPlaces(change);
  // Merino sends the value already in percent units ("0.2" means 0.2%), but the
  // percent style expects a ratio, so divide by 100.
  return new Intl.NumberFormat(locale, {
    style: "percent",
    signDisplay: "exceptZero",
    minimumFractionDigits: places,
    maximumFractionDigits: places,
  }).format(value / 100);
}

const TICKER_STATUS_L10N_ID = {
  up: "newtab-stocks-ticker-status-up",
  down: "newtab-stocks-ticker-status-down",
  flat: "newtab-stocks-ticker-status-flat",
};

// A single read-only ticker card. The visible rows are hidden from screen
// readers; the spoken label comes from the localized `.stock-ticker-sr` span
// instead (one of the newtab-stocks-ticker-status-* messages, by direction).
function StockTicker({
  loading,
  size = "medium",
  name: stockName,
  ticker,
  price,
  changePercent,
}) {
  const direction = getDirection(changePercent);
  const locale =
    typeof navigator !== "undefined" ? navigator.language : undefined;
  const displayPrice = formatPrice(price, locale);
  const displayChange = formatChange(changePercent, locale);
  // Flat is a sideways arrow, so it mirrors for right-to-left locales. The URL
  // is built in JS like the other newtab arrows, not as a CSS url(): a static
  // reference to shaft-arrow-right.svg makes browser_all_files_referenced.js
  // flag the icon.
  const isRTL = typeof document !== "undefined" && document.dir === "rtl";
  const indicatorStyle =
    direction === "flat"
      ? {
          backgroundImage: `url("chrome://global/skin/icons/shaft-arrow-${
            isRTL ? "left" : "right"
          }.svg")`,
        }
      : undefined;
  const changeText = (
    <span className={`stock-ticker-change stock-ticker-change--${direction}`}>
      {displayChange}
    </span>
  );
  return (
    <li
      className={`stock-ticker stock-ticker--${size}`}
      aria-hidden={loading ? "true" : undefined}
    >
      {!loading && (
        <span
          className="stock-ticker-sr"
          data-l10n-id={TICKER_STATUS_L10N_ID[direction]}
          data-l10n-args={JSON.stringify({
            name: stockName || ticker,
            change: displayChange,
            price: displayPrice,
          })}
        />
      )}
      <span
        className={`stock-indicator stock-indicator--${direction}`}
        style={indicatorStyle}
        aria-hidden="true"
      />
      <span className="stock-ticker-label" aria-hidden="true">
        {size === "large" ? (
          <>
            <span className="stock-ticker-line">
              <span className="stock-ticker-name">{stockName}</span>
              <span className="stock-ticker-dot" />
              <span className="stock-ticker-symbol">{ticker}</span>
            </span>
            <span className="stock-ticker-line">
              {changeText}
              <span className="stock-ticker-dot" />
              <span className="stock-ticker-price">{displayPrice}</span>
            </span>
          </>
        ) : (
          <>
            <span className="stock-ticker-line">
              <span className="stock-ticker-symbol">{ticker}</span>
              <span className="stock-ticker-dot" />
              <span className="stock-ticker-price">{displayPrice}</span>
            </span>
            {changeText}
          </>
        )}
      </span>
    </li>
  );
}

export { StockTicker, getDirection, formatPrice, formatChange };
