import type {
  Canvas as SkiaCanvas,
  CanvasRenderingContext2D as SkiaCanvasRenderingContext2D,
} from 'skia-canvas';

/**
 * Returns builtin Node.js modules or throws an error saying that the method is only implemented in Node.js.
 * @param methodName - Name of the method that calls this function
 * @returns - The `fs`, `path` and `url` Node.js modules.
 */
export function getNodeApiOrThrow(methodName: string) {
  if (!isNode()) {
    throw new Error(`${methodName} is only implemented for Node.js`);
  }
  return {
    fs: process.getBuiltinModule('node:fs'),
    path: process.getBuiltinModule('node:path'),
    url: process.getBuiltinModule('node:url'),
  };
}

let CanvasCtorBrowser: typeof OffscreenCanvas;
let CanvasCtorNode: typeof SkiaCanvas;

/**
 * Returns a 2D canvas context for rendering on the browser or Node.js.
 * On Node.js this requires the optional `skia-canvas` package to be installed.
 * @param width - Width of the canvas.
 * @param height - Height of the canvas.
 * @returns The initialised canvas context.
 */
export function getCanvasContext(
  width: number,
  height: number,
): OffscreenCanvasRenderingContext2D | SkiaCanvasRenderingContext2D {
  if (isNode()) {
    if (!CanvasCtorNode) {
      try {
        CanvasCtorNode = getRequireFn()('skia-canvas').Canvas;
      } catch (error) {
        throw new Error(
          'drawText on Node.js requires the optional "skia-canvas" package. Install it with: npm install skia-canvas',
          { cause: error },
        );
      }
    }
    return new CanvasCtorNode(width, height).getContext('2d');
  } else {
    CanvasCtorBrowser ??= globalThis.OffscreenCanvas;
    const context = new CanvasCtorBrowser(width, height).getContext('2d');
    if (!context) {
      throw new Error('Failed to create canvas context');
    }
    return context;
  }
}

function isNode() {
  return (
    typeof process !== 'undefined' &&
    typeof process.getBuiltinModule === 'function'
  );
}

let requireFn: NodeJS.Require;
function getRequireFn() {
  requireFn ??= process
    .getBuiltinModule('node:module')
    .createRequire(import.meta.url);
  return requireFn;
}
