import { componentInterface, includeComponent } from '../../factory';
import { getCommonPixels } from '../../utils/commonPixels';
import { hash } from '../../utils/hash';
import { getBrowser } from '../system/browser';

const browser = getBrowser();
const name = browser.name.toLowerCase();
const ver = browser.version.split('.')[0] || '0';
const majorVer = parseInt(ver, 10);

const _RUNS = 3;

/**
 * A simple canvas finger printing function
 *
 * @returns a CanvasInfo JSON object
 */

const _WIDTH = 280;
const _HEIGHT = 20;

export default function generateCanvasFingerprint(): Promise<componentInterface> {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');

  return new Promise((resolve) => {
    /**
     * Since some browsers fudge with the canvas pixels to prevent fingerprinting, the following
     * creates the canvas three times and getCommonPixels picks the most common byte for each
     * channel of each pixel.
     */
    const imageDatas: ImageData[] = Array.from({ length: _RUNS }, () => generateCanvasImageData());
    const commonImageData = getCommonPixels(imageDatas, _WIDTH, _HEIGHT);

    resolve({
      commonImageDataHash: hash(commonImageData.data.toString()).toString(),
    });
  });
}

function generateCanvasImageData(): ImageData {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');

  if (!ctx) {
    return new ImageData(1, 1);
  }

  // Set canvas dimensions
  canvas.width = _WIDTH;
  canvas.height = _HEIGHT;

  // Create rainbow gradient for the background rectangle
  const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
  gradient.addColorStop(0, 'red');
  gradient.addColorStop(1 / 6, 'orange');
  gradient.addColorStop(2 / 6, 'yellow');
  gradient.addColorStop(3 / 6, 'green');
  gradient.addColorStop(4 / 6, 'blue');
  gradient.addColorStop(5 / 6, 'indigo');
  gradient.addColorStop(1, 'violet');

  // Draw background rectangle with the rainbow gradient
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // Draw some random text
  const randomText = 'Random Text WMwmil10Oo';
  ctx.font = '23.123px Arial';
  ctx.fillStyle = 'black';
  ctx.fillText(randomText, -5, 15);

  // Draw the same text with an offset, different color, and slight transparency
  ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
  ctx.fillText(randomText, -3.3, 17.7);

  // Draw a line crossing the image at an arbitrary angle
  ctx.beginPath();
  ctx.moveTo(0, 0);
  ctx.lineTo((canvas.width * 2) / 7, canvas.height);
  ctx.strokeStyle = 'white';
  ctx.lineWidth = 2;
  ctx.stroke();

  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  // Return data URL of the canvas
  return imageData;
}

// In Safari from version 17 in private and normal modes canvas differs
if (name !== 'firefox' && !(name === 'safari' && majorVer === 17)) {
  includeComponent('canvas', generateCanvasFingerprint);
}
