"use client";

import {
  indicatorAnnotationAppearance,
  type ChartAppearanceSettings,
} from "@/lib/chart/chart-appearance";
import type { CoordinateBridge } from "@/lib/chart/drawings/canvas-render";
import {
  drawIndicatorAnnotations,
  flattenBoxOutputs,
  flattenLabelOutputs,
  type FlatIndicatorBox,
  type FlatIndicatorLabel,
} from "@/lib/chart/indicator-annotations/canvas-render";
import type { OverlayAnnotationState } from "@/lib/indicators/sync-chart-outputs";
import {
  forwardRef,
  useCallback,
  useEffect,
  useImperativeHandle,
  useRef,
} from "react";

type LwcChart = {
  timeScale: () => {
    timeToCoordinate: (time: number) => number | null;
    subscribeVisibleLogicalRangeChange: (cb: () => void) => void;
    unsubscribeVisibleLogicalRangeChange: (cb: () => void) => void;
  };
  subscribeCrosshairMove: (cb: () => void) => void;
  unsubscribeCrosshairMove: (cb: () => void) => void;
};

type LwcSeries = {
  priceToCoordinate: (price: number) => number | null;
};

export type IndicatorAnnotationLayerHandle = {
  notifyAnnotationsChanged: () => void;
};

type IndicatorAnnotationLayerProps = {
  chart: LwcChart | null;
  series: LwcSeries | null;
  annotationsRef: { current: OverlayAnnotationState | null };
  appearance: ChartAppearanceSettings;
  width: number;
  height: number;
};

export const IndicatorAnnotationLayer = forwardRef<
  IndicatorAnnotationLayerHandle,
  IndicatorAnnotationLayerProps
>(function IndicatorAnnotationLayer(
  { chart, series, annotationsRef, appearance, width, height },
  ref,
) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const revisionRef = useRef(0);

  const snapshotAnnotations = useCallback((): {
    labels: FlatIndicatorLabel[];
    boxes: FlatIndicatorBox[];
  } => {
    const state = annotationsRef.current ?? { labels: [], boxes: [] };
    return {
      labels: flattenLabelOutputs(state.labels),
      boxes: flattenBoxOutputs(state.boxes),
    };
  }, [annotationsRef]);

  const buildBridge = useCallback((): CoordinateBridge | null => {
    if (!chart || !series) {
      return null;
    }
    return {
      timeToX: (timeSec) => chart.timeScale().timeToCoordinate(timeSec),
      priceToY: (price) => series.priceToCoordinate(price),
    };
  }, [chart, series]);

  const redraw = useCallback(() => {
    const canvas = canvasRef.current;
    const bridge = buildBridge();
    if (!canvas || !bridge || width <= 0 || height <= 0) {
      return;
    }
    const dpr = window.devicePixelRatio || 1;
    canvas.width = Math.floor(width * dpr);
    canvas.height = Math.floor(height * dpr);
    canvas.style.width = `${width}px`;
    canvas.style.height = `${height}px`;
    const ctx = canvas.getContext("2d");
    if (!ctx) {
      return;
    }
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    const { labels, boxes } = snapshotAnnotations();
    drawIndicatorAnnotations(
      ctx,
      bridge,
      labels,
      boxes,
      indicatorAnnotationAppearance(appearance),
      width,
      height,
    );
  }, [appearance, buildBridge, height, snapshotAnnotations, width]);

  useImperativeHandle(ref, () => ({
    notifyAnnotationsChanged: () => {
      revisionRef.current += 1;
      redraw();
    },
  }));

  useEffect(() => {
    redraw();
  }, [redraw, appearance.themeId]);

  useEffect(() => {
    if (!chart) {
      return;
    }
    const cb = () => redraw();
    chart.timeScale().subscribeVisibleLogicalRangeChange(cb);
    chart.subscribeCrosshairMove(cb);
    return () => {
      chart.timeScale().unsubscribeVisibleLogicalRangeChange(cb);
      chart.unsubscribeCrosshairMove(cb);
    };
  }, [chart, redraw]);

  return (
    <canvas
      ref={canvasRef}
      className="chart-indicator-annotation-layer"
      aria-hidden
      data-annotation-revision={revisionRef.current}
    />
  );
});
