/* eslint-disable @typescript-eslint/consistent-type-assertions */
import { useEffect, useState } from 'react';

/**
 * Calculate the width and height of a text element.
 *
 * @param text The text to calculate the width and height of.
 * @param fontSize The font size to apply to the text.
 * @param fontFamily The font family to apply to the text.
 * @returns Returns the width and height of the text.
 */
function calculateTextDimensions(
  text: string,
  fontSize: string,
  fontFamily: string,
): { width: number; height: number } {
  // Reuse old canvas if available.
  let canvas = (calculateTextDimensions as { canvas?: HTMLCanvasElement }).canvas;

  if (!canvas) {
    canvas = document.createElement('canvas');
    (calculateTextDimensions as { canvas?: HTMLCanvasElement }).canvas = canvas;
  }

  const context = canvas.getContext('2d');
  if (!context) {
    return { width: 0, height: 0 };
  }

  context.font = `${fontSize} ${fontFamily}`;
  const metrics = context.measureText(text);

  return {
    width: metrics.width,
    height: metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent,
  };
}

/**
 * Calculate the scaling size to apply to a text to fit it within a container.
 *
 * @param container The container dimensions to fit the text within.
 * @param text The text to fit within the container.
 * @param fontSize The font size to apply to the text.
 * @param fontFamily The font family to apply to the text.
 * @returns Returns a value between 0 and 1 which represents the scaling factor to apply to the text.
 */
export const calculateTextScaleSize = (
  container: { width: number; height: number },
  text: string,
  fontSize: string,
  fontFamily: string,
) => {
  const { width, height } = calculateTextDimensions(text, fontSize, fontFamily);
  return Math.min(container.width / width, container.height / height, 1);
};

/**
 * Given a container and child element, calculate the scaling size to apply to the child.
 */
export function useElementScaleSize(
  container: { width: number; height: number },
  text: string,
  fontSize: number,
  fontFamily: string,
) {
  const [scalingFactor, setScalingFactor] = useState(1);

  useEffect(() => {
    const scaleSize = calculateTextScaleSize(container, text, `${fontSize}px`, fontFamily);

    setScalingFactor(scaleSize);
  }, [text, container, fontFamily, fontSize]);

  return scalingFactor;
}
