import styled from '@emotion/styled';
import React, { useCallback, useEffect, useRef, useState } from 'react';

import useClickDrag from '@/hooks/useClickDrag';
import { useRealtimeValue } from '@/hooks/useRealtimeValue';

import setCursor from '../edit2025/canvasRenderer/setCursor';
import { Tooltip } from '../tooltip/Tooltip';

const KnobContainer = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
`;

const KnobSvg = styled.svg`
  cursor: ns-resize;
  user-select: none;
  margin-bottom: -13px;
`;

const KnobInput = styled.input`
  background: transparent;
  border: 1px solid transparent;
  color: var(--color-foreground-secondary);
  text-align: center;
  font-family: 'Input Sans', monospace;
  font-size: 10px;
  width: 60px;
  padding: 2px 4px;
  margin-bottom: -4px;
  border-radius: 4px;
  outline: none;
  transition: all 0.15s ease;
  opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};

  pointer-events: ${({ disabled }) => (disabled ? 'none' : 'auto')};

  &:hover:not(:focus) {
    background-color: var(--color-background-glass-thin);
    border: 1px solid var(--color-border-primary);
  }

  &:focus {
    background-color: var(--color-background-glass-thick);
    border: 1px solid var(--color-accent-blue);
    color: var(--color-foreground-primary);
  }
`;

const KnobLabel = styled.div<{ disabled: boolean }>`
  font-size: 14px;
  color: var(--color-foreground-primary);
  opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};
`;

const MIN_ANGLE = -135;
const MAX_ANGLE = 135;
const ANGLE_RANGE = MAX_ANGLE - MIN_ANGLE;

const valueToAngle = (
  value: number,
  min: number,
  max: number,
  scale: 'linear' | 'logarithmic' = 'linear'
) => {
  let normalized: number;
  if (scale === 'logarithmic') {
    const logMin = Math.log(min);
    const logMax = Math.log(max);
    const logValue = Math.log(value);
    normalized = (logValue - logMin) / (logMax - logMin);
  } else {
    normalized = (value - min) / (max - min);
  }
  return MIN_ANGLE + normalized * ANGLE_RANGE;
};

const angleToValue = (
  angle: number,
  min: number,
  max: number,
  scale: 'linear' | 'logarithmic' = 'linear'
) => {
  const normalized = (angle - MIN_ANGLE) / ANGLE_RANGE;
  if (scale === 'logarithmic') {
    const logMin = Math.log(min);
    const logMax = Math.log(max);
    return Math.exp(logMin + normalized * (logMax - logMin));
  } else {
    return min + normalized * (max - min);
  }
};

const polarToCartesian = (
  centerX: number,
  centerY: number,
  radius: number,
  angleInDegrees: number
) => {
  const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;
  return {
    x: centerX + radius * Math.cos(angleInRadians),
    y: centerY + radius * Math.sin(angleInRadians),
  };
};

const describeArc = (
  x: number,
  y: number,
  radius: number,
  startAngle: number,
  endAngle: number
) => {
  const start = polarToCartesian(x, y, radius, endAngle);
  const end = polarToCartesian(x, y, radius, startAngle);
  const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
  return [
    'M',
    start.x,
    start.y,
    'A',
    radius,
    radius,
    0,
    largeArcFlag,
    0,
    end.x,
    end.y,
  ].join(' ');
};

export default function StudioKnob({
  getValue,
  min,
  max,
  anchorValue,
  defaultValue = anchorValue,
  anchorType = 'center',
  scale = 'linear',
  onChange,
  onCommit,
  formatValue,
  parseValue,
  label,
  disabled = false,
  size = 64,
  tooltipContent,
}: {
  getValue: () => number;
  min: number;
  max: number;
  anchorValue?: number;
  anchorType?: 'center' | 'min' | 'max';
  defaultValue?: number;
  scale?: 'linear' | 'logarithmic';
  onChange?: (value: number, isFirst: boolean) => void;
  onCommit?: (value: number) => void;
  formatValue?: (value: number) => string;
  parseValue?: (str: string) => number;
  label?: string;
  disabled?: boolean;
  size?: number;
  tooltipContent?: React.ReactNode[] | React.ReactNode;
}) {
  const svgRef = useRef<SVGSVGElement>(null);
  const [_isDragging, setIsDragging] = useState(false);
  const [isFocused, setIsFocused] = useState(false);
  const [inputValue, setInputValue] = useState('');
  const inputRef = useRef<HTMLInputElement>(null);
  const pendingChangeRef = useRef<{
    value: number;
    isFirst: boolean;
  } | null>(null);
  const rafIdRef = useRef<number | null>(null);
  const [localDragValue, setLocalDragValue] = useState<number | null>(null);

  // Use realtime value hook to watch for changes at screen refresh rate
  const value = useRealtimeValue(getValue, !disabled);

  const effectiveAnchorValue =
    anchorValue ??
    (anchorType === 'min'
      ? min
      : anchorType === 'max'
        ? max
        : scale === 'logarithmic'
          ? Math.sqrt(min * max)
          : (min + max) / 2);

  const effectiveValue = localDragValue !== null ? localDragValue : value;
  const currentAngle = valueToAngle(effectiveValue, min, max, scale);
  const anchorAngle = valueToAngle(effectiveAnchorValue, min, max, scale);

  const center = size / 2;
  const radius = size / 2 - 6;
  const innerRadius = radius - 6;

  const pointerEnd = polarToCartesian(
    center,
    center,
    innerRadius - 4,
    currentAngle
  );

  // Throttle onChange calls to once per animation frame
  const scheduleChange = useCallback(
    (value: number, isFirst: boolean) => {
      pendingChangeRef.current = { value, isFirst };

      if (rafIdRef.current === null) {
        rafIdRef.current = requestAnimationFrame(() => {
          rafIdRef.current = null;
          const pending = pendingChangeRef.current;
          if (pending) {
            onChange?.(pending.value, pending.isFirst);
            pendingChangeRef.current = null;
          }
        });
      }
    },
    [onChange]
  );

  const clickDragRef = useClickDrag(
    useCallback(
      ({ event }) => {
        if (disabled) return;
        event.stopPropagation();
        event.preventDefault();

        const initialValue = getValue();
        const unsetCursor = setCursor('ns-resize');
        let totalMovementY = 0;
        let isDrag = false;
        let wasDrag = false;

        return {
          onMouseMove: ({ event }) => {
            event.stopPropagation();
            event.preventDefault();

            if (event instanceof MouseEvent) {
              if (Math.abs(event.movementY) < 100) {
                totalMovementY += event.movementY / 2;
              }
            }

            if (Math.abs(totalMovementY) > 0 && !isDrag) {
              isDrag = true;
              document.body.requestPointerLock();
              setIsDragging(true);
            }

            if (isDrag) {
              // Mouse down increases value, mouse up decreases value
              const pixelsForFullRange = 200; // 200 pixels of movement = full range
              const initialAngle = valueToAngle(initialValue, min, max, scale);
              const anglePerPixel = ANGLE_RANGE / pixelsForFullRange;
              const newAngle = Math.min(
                MAX_ANGLE,
                Math.max(
                  MIN_ANGLE,
                  initialAngle - totalMovementY * anglePerPixel
                )
              );
              const newValue = angleToValue(newAngle, min, max, scale);
              setLocalDragValue(newValue);
              scheduleChange(newValue, !wasDrag);
              wasDrag = true;
            }
          },
          onMouseUp: ({ event }) => {
            event.stopPropagation();
            event.preventDefault();
            document.exitPointerLock();
            unsetCursor();
            setIsDragging(false);
            setLocalDragValue(null);

            if (!isDrag) return;

            const pixelsForFullRange = 200;
            const initialAngle = valueToAngle(initialValue, min, max, scale);
            const anglePerPixel = ANGLE_RANGE / pixelsForFullRange;
            const newAngle = Math.min(
              MAX_ANGLE,
              Math.max(MIN_ANGLE, initialAngle - totalMovementY * anglePerPixel)
            );
            const newValue = angleToValue(newAngle, min, max, scale);
            onCommit?.(newValue);
          },
        };
      },
      [getValue, min, max, scale, scheduleChange, onCommit, disabled]
    )
  );

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setInputValue(e.target.value);
  };

  const handleInputBlur = () => {
    setIsFocused(false);
    const parsed = parseValue ? parseValue(inputValue) : parseFloat(inputValue);
    if (!isNaN(parsed)) {
      const clampedValue = Math.min(max, Math.max(min, parsed));
      onChange?.(clampedValue, true);
      onCommit?.(clampedValue);
    }
  };

  const handleInputFocus = () => {
    setIsFocused(true);
    const formatted = formatValue
      ? formatValue(effectiveValue)
      : effectiveValue.toFixed(1);
    setInputValue(formatted);
  };

  const handleInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === 'Enter') {
      inputRef.current?.blur();
    } else if (e.key === 'Escape') {
      setIsFocused(false);
      inputRef.current?.blur();
    }
  };

  const inputDisplayValue = isFocused
    ? inputValue
    : formatValue
      ? formatValue(effectiveValue)
      : effectiveValue.toFixed(1);

  useEffect(() => {
    if (isFocused) {
      inputRef.current?.select();
    }
  }, [isFocused]);

  // Cleanup RAF on unmount
  useEffect(() => {
    return () => {
      if (rafIdRef.current !== null) {
        cancelAnimationFrame(rafIdRef.current);
      }
    };
  }, []);

  const arcPath =
    Math.abs(currentAngle - anchorAngle) > 0.1
      ? describeArc(
          center,
          center,
          radius,
          Math.min(anchorAngle, currentAngle),
          Math.max(anchorAngle, currentAngle)
        )
      : '';

  const backgroundArcPath = describeArc(
    center,
    center,
    radius,
    MIN_ANGLE,
    MAX_ANGLE
  );

  return (
    <Tooltip label={tooltipContent} placement='bottom' openDelay={500}>
      <KnobContainer>
        <KnobSvg
          ref={svgRef}
          width={size}
          height={size}
          viewBox={`0 0 ${size} ${size}`}
          style={{ opacity: disabled ? 0.5 : 1 }}
          onDoubleClick={() => {
            if (disabled) return;
            scheduleChange(defaultValue ?? effectiveAnchorValue, true);
            onCommit?.(defaultValue ?? effectiveAnchorValue);
          }}
        >
          {/* Background arc */}
          <path
            d={backgroundArcPath}
            fill='none'
            stroke='var(--color-foreground-tertiary)'
            strokeWidth='4'
            strokeLinecap='round'
            opacity='0.3'
          />

          {/* Active arc */}
          {arcPath && (
            <path
              d={arcPath}
              fill='none'
              stroke='var(--color-accent-blue)'
              strokeWidth='4'
              strokeLinecap='round'
            />
          )}

          {/* Center circle (clickable area) */}
          <circle
            ref={clickDragRef as any}
            cx={center}
            cy={center}
            r={innerRadius}
            fill='var(--color-background-glass-thin)'
            stroke='var(--color-border-primary)'
            strokeWidth='1'
          />

          {/* Pointer line */}
          <line
            x1={center}
            y1={center}
            x2={pointerEnd.x}
            y2={pointerEnd.y}
            stroke={
              disabled
                ? 'var(--color-background-glass-thick)'
                : 'var(--color-accent-blue)'
            }
            strokeWidth='2'
            strokeLinecap='round'
            pointerEvents='none'
          />
        </KnobSvg>

        <KnobInput
          ref={inputRef}
          type='text'
          value={inputDisplayValue}
          onChange={handleInputChange}
          onFocus={handleInputFocus}
          onBlur={handleInputBlur}
          onKeyDown={handleInputKeyDown}
          disabled={disabled}
        />

        {label && <KnobLabel disabled={disabled}>{label}</KnobLabel>}
      </KnobContainer>
    </Tooltip>
  );
}
