"use client";

/* eslint no-console: "off" */
import type { AriaProgressBarProps } from "@react-types/progress";
import classNames from "classnames/dedupe";
import type { ReactNode, RefAttributes } from "react";
import { useId } from "react";
import type { DOMProps } from "../../types/dom";
import { Label } from "../Label";
import { VisuallyHidden } from "../VisuallyHidden";
import "./Progress.css";

export type ProgressElementType = HTMLDivElement;

export interface ProgressProps
  extends
    Omit<AriaProgressBarProps, "label">,
    DOMProps,
    RefAttributes<ProgressElementType> {
  label: ReactNode;
  children?: ReactNode;
  /** Custom class name for setting specific CSS */
  id?: string;
  className?: string;
  showLabel?: boolean;
  showValueLabel?: boolean;
  variant?: "primary" | "secondary" | "success" | "danger" | "warning" | "info";
  value?: number;
  minValue?: number;
  maxValue?: number;
  valueFormatter?: (
    value: number,
    minValue: number,
    maxValue: number,
  ) => string;
}

function warnAboutInvalidValues(
  value: number,
  minValue: number,
  maxValue: number,
) {
  if (minValue > maxValue) {
    console.warn("minValue is greater than maxValue");
  }
  if (value < minValue) {
    console.warn("Progress value is less than minValue");
  }
  if (value > maxValue) {
    console.warn("Progress value is greater than maxValue");
  }
}

const sanitizedValue = (
  value: number | undefined,
  defaultValue: number,
): number => {
  if (value === undefined || Number.isNaN(value)) {
    return defaultValue;
  }
  return value;
};

const getLabelComponent = (
  label: ReactNode,
  progressLabelId: string,
  showLabel: boolean,
) => {
  if (showLabel) {
    return (
      <Label
        id={progressLabelId}
        className="mobius-progress__label"
        elementType="span"
      >
        {label}
      </Label>
    );
  }

  return (
    <VisuallyHidden>
      <Label
        id={progressLabelId}
        className="mobius-progress__label"
        elementType="span"
      >
        {label}
      </Label>
    </VisuallyHidden>
  );
};

const DEFAULT_VALUE = 0;
const DEFAULT_MIN_VALUE = 0;
const DEFAULT_MAX_VALUE = 100;

const Progress = ({ ref, ...props }: ProgressProps) => {
  const progressId = useId();
  const progressLabelId = useId();
  const {
    id,
    label,
    showLabel = true,
    showValueLabel = !!label,
    valueFormatter,
    variant = "primary",
    className,
  } = props;

  let { value, minValue, maxValue } = props;

  value = sanitizedValue(value, DEFAULT_VALUE);
  minValue = sanitizedValue(minValue, DEFAULT_MIN_VALUE);
  maxValue = sanitizedValue(maxValue, DEFAULT_MAX_VALUE);

  // Will never happen but lets appease the typescript gods
  if (value === undefined || minValue === undefined || maxValue === undefined) {
    return null;
  }

  warnAboutInvalidValues(value, minValue, maxValue);

  // Calculate the width of the progress bar as a percentage
  const clamp = (num: number, min: number, max: number): number =>
    Math.min(Math.max(num, min), max);
  const percentage = clamp(
    Math.round(((value - minValue) / (maxValue - minValue)) * 100),
    0,
    100,
  );
  const barWidth = `${percentage}%`;

  const classes = classNames("mobius", "mobius-progress", className, {
    "--is-primary": variant === "primary",
    "--is-secondary": variant === "secondary",
  });

  // Set progress aria values
  const progressBarProps: Record<string, string> = {};
  progressBarProps["aria-valuemin"] = minValue.toString();
  progressBarProps["aria-valuemax"] = maxValue.toString();
  progressBarProps["aria-valuenow"] = value.toString();
  progressBarProps["aria-valuetext"] =
    valueFormatter instanceof Function
      ? valueFormatter(value, minValue, maxValue)
      : barWidth;
  const labelComponent = getLabelComponent(label, progressLabelId, showLabel);

  return (
    <div
      id={id || progressId}
      {...progressBarProps}
      ref={ref}
      className={classes}
      role="progressbar"
      aria-labelledby={progressLabelId}
    >
      {labelComponent}
      <div className="mobius-progress__track">
        <div className="mobius-progress__bar" style={{ width: barWidth }} />
      </div>
      {showValueLabel && (
        <Label data-testid="value-label" elementType="span">
          {progressBarProps["aria-valuetext"]}
        </Label>
      )}
    </div>
  );
};

Progress.displayName = "Progress";
export { Progress };
