import { Box, useIsScreenReaderEnabled, useStdout } from "src/ink";
import React, { useMemo } from "react";
import { useMeasuredSize } from "../../hooks/useMeasuredSize.js";
import usePosition from "../../hooks/usePosition.js";
import useProtocol from "../../hooks/useProtocol.js";
import type { GetVisibility, Visibility } from "../../hooks/useVisibility.js";
import { useVisibility } from "../../hooks/useVisibility.js";
import { useTerminalInfo } from "../../InkPictureProvider.js";
import AsciiImage from "./Ascii.js";
import BrailleImage from "./Braille.js";
import HalfBlockImage from "./HalfBlock.js";
import ITerm2Image from "./ITerm2.js";
import KittyImage from "./Kitty.js";
import type { ImageProps } from "./protocol.js";
import SixelImage from "./Sixel.js";

const imageProtocols = {
  ascii: AsciiImage,
  braille: BrailleImage,
  halfBlock: HalfBlockImage,
  iterm2: ITerm2Image,
  kitty: KittyImage,
  sixel: SixelImage,
};

export type ImageProtocolName = keyof typeof imageProtocols;

export type ImageProtocolHint = Partial<Record<Visibility, ImageProtocolName>>;

const ImageRenderer = (props: ImageProps & { protocol: ImageProtocolName }) => {
  const ProtocolComponent = imageProtocols[props.protocol];
  return <ProtocolComponent {...props} />;
};

type ImageComponentProps = Omit<ImageProps, "width" | "height"> & {
  width?: number | string;
  height?: number | string;
  protocol?: ImageProtocolName | ImageProtocolHint;
  getVisibility?: GetVisibility;
};

function Image({
  protocol: specifiedProtocol,
  getVisibility,
  width = "100%",
  height = "100%",
  pixelWidth,
  pixelHeight,
  ...props
}: ImageComponentProps) {
  const isScreenReaderEnabled = useIsScreenReaderEnabled();
  const terminalInfo = useTerminalInfo();
  const { stdout } = useStdout();
  const protocol = useProtocol(
    typeof specifiedProtocol === "string" ? specifiedProtocol : undefined,
  );

  const { containerRef, resolvedWidth, resolvedHeight } = useMeasuredSize(
    width,
    height,
  );

  const position = usePosition(containerRef);
  const visibility = useVisibility(
    position,
    stdout.rows,
    stdout.columns,
    getVisibility,
  );

  const effectiveProtocol = useMemo((): ImageProtocolName => {
    if (typeof specifiedProtocol === "string") return specifiedProtocol;

    const hints: ImageProtocolHint = specifiedProtocol ?? {};
    const hint = hints[visibility];
    if (hint) return hint;

    if (visibility === "full") return protocol;

    if (
      protocol === "ascii" ||
      protocol === "braille" ||
      protocol === "halfBlock"
    )
      return protocol;

    if (terminalInfo.supportsUnicode && terminalInfo.supportsColor)
      return "halfBlock";
    if (terminalInfo.supportsUnicode) return "braille";
    return "ascii";
  }, [
    visibility,
    protocol,
    specifiedProtocol,
    terminalInfo.supportsUnicode,
    terminalInfo.supportsColor,
  ]);

  if (isScreenReaderEnabled) {
    const { src, alt } = props;
    const label = `image: ${alt || (typeof src === "string" ? src : "binary image data")}`;

    return (
      <Box
        ref={containerRef}
        width={width}
        height={height}
        aria-label={label}
        flexDirection="column"
      />
    );
  }

  return (
    <Box
      ref={containerRef}
      width={width}
      height={height}
      flexDirection="column"
    >
      <ImageRenderer
        protocol={effectiveProtocol}
        key={effectiveProtocol}
        width={resolvedWidth}
        height={resolvedHeight}
        pixelWidth={pixelWidth}
        pixelHeight={pixelHeight}
        {...props}
      />
    </Box>
  );
}

export default Image;
