// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import { fabric } from 'fabric';
import { customFabricObjectControls } from './util/customFabricObjectControls';
import { moreStyles } from './util/moreStyles';
import { getDateTimeFormatter } from '../util/formatTimestamp';

export enum DigitalClockStickerStyle {
  White = 'White',
  Black = 'Black',
  Light = 'Light',
  Dark = 'Dark',
  Orange = 'Orange',
}

function getTextStyle(style: DigitalClockStickerStyle): {
  fill: string;
  textBackgroundColor: string;
} {
  if (style === DigitalClockStickerStyle.Black) {
    return {
      fill: '#000',
      textBackgroundColor: '',
    };
  }

  if (style === DigitalClockStickerStyle.Light) {
    return {
      fill: '#fff',
      textBackgroundColor: 'rgba(255, 255, 255, 0.4)',
    };
  }

  if (style === DigitalClockStickerStyle.Dark) {
    return {
      fill: '#fff',
      textBackgroundColor: 'rgba(0, 0, 0, 0.4)',
    };
  }

  if (style === DigitalClockStickerStyle.Orange) {
    return {
      fill: '#ff7629',
      textBackgroundColor: 'rgba(0, 0, 0, 0.6)',
    };
  }

  return {
    fill: '#fff',
    textBackgroundColor: '',
  };
}

const TEXT_PROPS = {
  editable: false,
  fontWeight: '400',
  left: 0,
  lockScalingFlip: true,
  originX: 'center',
  originY: 'center',
  textAlign: 'center',
  top: 0,
};

export class MediaEditorFabricDigitalTimeSticker extends fabric.Group {
  static getNextStyle(
    style?: DigitalClockStickerStyle
  ): DigitalClockStickerStyle {
    if (style === DigitalClockStickerStyle.White) {
      return DigitalClockStickerStyle.Black;
    }

    if (style === DigitalClockStickerStyle.Black) {
      return DigitalClockStickerStyle.Light;
    }

    if (style === DigitalClockStickerStyle.Light) {
      return DigitalClockStickerStyle.Dark;
    }

    if (style === DigitalClockStickerStyle.Dark) {
      return DigitalClockStickerStyle.Orange;
    }

    return DigitalClockStickerStyle.White;
  }

  constructor(
    timestamp: number,
    style: DigitalClockStickerStyle = DigitalClockStickerStyle.White,
    options: fabric.IGroupOptions = {}
  ) {
    const parts = getDateTimeFormatter({
      hour: 'numeric',
      minute: 'numeric',
    }).formatToParts(timestamp);
    const { fill } = getTextStyle(style);

    let dayPeriodText = '';
    const timeText = parts.reduce((acc, part) => {
      if (part.type === 'dayPeriod') {
        dayPeriodText = part.value;
        return acc;
      }
      return `${acc}${part.value}`;
    }, '');

    const timeTextNode = new fabric.IText(timeText.trim(), {
      ...TEXT_PROPS,
      fill,
      fontSize: 72,
      fontFamily: '"Hatsuishi Large", Hatsuishi, Inter',
    });

    const dayPeriodTextNode = new fabric.IText(dayPeriodText, {
      ...TEXT_PROPS,
      fill,
      fontSize: 11,
      fontFamily: 'Inter',
    });

    const dayPeriodBounds = dayPeriodTextNode.getBoundingRect();
    const timeBounds = timeTextNode.getBoundingRect();
    const totalWidth = dayPeriodBounds.width + timeBounds.width;

    dayPeriodTextNode.set({
      left: totalWidth / 2 + dayPeriodBounds.width / 2,
      top: timeBounds.height / 2 - dayPeriodBounds.height * 1.66,
    });

    super([timeTextNode, dayPeriodTextNode], {
      ...options,
      data: { stickerStyle: style, timestamp },
    });

    this.set('width', totalWidth * 2);
  }

  static override fromObject(
    // eslint-disable-next-line max-len
    // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
    options: any,
    callback: (_: MediaEditorFabricDigitalTimeSticker) => unknown
  ): MediaEditorFabricDigitalTimeSticker {
    const timestamp = options?.data.timestamp ?? Date.now();

    const result = new MediaEditorFabricDigitalTimeSticker(
      timestamp,
      options.data?.stickerStyle,
      options
    );
    callback(result);
    return result;
  }

  override render(ctx: CanvasRenderingContext2D): void {
    const { textBackgroundColor } = getTextStyle(this.data.stickerStyle);

    if (textBackgroundColor) {
      const bounds = this.getBoundingRect();
      const zoom = this.canvas?.getZoom() || 1;
      const height = bounds.height / zoom;
      const left = bounds.left / zoom;
      const top = bounds.top / zoom;
      const width = bounds.width / zoom;

      ctx.save();

      ctx.fillStyle = textBackgroundColor;
      ctx.beginPath();
      ctx.roundRect(left, top, width, height, 14);
      ctx.closePath();
      ctx.fill();

      ctx.restore();
    }

    super.render(ctx);
  }
}

const moreStylesControl = new fabric.Control({
  ...moreStyles,
  mouseUpHandler: (_eventData, { target }) => {
    const stickerStyle = MediaEditorFabricDigitalTimeSticker.getNextStyle(
      target.data.stickerStyle
    );

    target.setOptions({
      data: {
        ...target.data,
        stickerStyle,
      },
    });

    const styleAttrs = getTextStyle(stickerStyle);

    const group = target as fabric.Group;
    group.getObjects().forEach(textObject => {
      textObject.set({ fill: styleAttrs.fill });
    });

    target.setCoords();
    target.canvas?.requestRenderAll();
    return true;
  },
});

MediaEditorFabricDigitalTimeSticker.prototype.type =
  'MediaEditorFabricDigitalTimeSticker';
MediaEditorFabricDigitalTimeSticker.prototype.controls = {
  ...customFabricObjectControls,
  mb: moreStylesControl,
};
