/**
 * ProgressBar — determinate or indeterminate progress with optional chrome.
 * Uses Tamagui stacks/tokens only (no raw CSS gradients) for consistent theming and RN compatibility.
 *
 * Slots (all optional): `header`, `footer` (e.g. wrap your own ScrollView for history), `leftSlot` / `rightSlot`
 * for inline actions, `label` centered above the track in the main column.
 *
 * @typedef {'determinate' | 'indeterminate'} ProgressMode
 * @typedef {'inline' | 'fixedTop'} ProgressVariant
 */

import React, { useEffect, useMemo, useState } from 'react';
import { Text, XStack, YStack } from 'tamagui';
import { scheduleInterval, scheduleTimeout } from '../../platform/compat.js';

function clamp01(n) {
  const x = Number(n);
  if (!Number.isFinite(x)) {
    return 0;
  }
  return Math.max(0, Math.min(100, x));
}

function renderNode(node) {
  if (node === null || node === undefined || node === false) {
    return null;
  }
  return node;
}

function hasSlot(node) {
  return node !== null && node !== undefined && node !== false;
}

export function ProgressBar({
  mode = 'indeterminate',
  value = 0,
  active = false,
  /** Delay before unmount after `active` becomes false (smooth fade-out). Inline only; `fixedTop` hides immediately to avoid layout jump (label removed) during opacity animation. */
  retainAfterInactiveMs = 420,
  label,
  header,
  footer,
  leftSlot,
  rightSlot,
  variant = 'inline',
  trackHeight = 4,
  zIndex = 20000
}) {
  const [offset, setOffset] = useState(-45);
  const [mounted, setMounted] = useState(active);
  /** `fixedTop` follows `active` only — no delayed unmount, so the track cannot re-anchor to the top when the parent clears `label` on the same tick as `active`. */
  const useRetainAfterInactive = variant !== 'fixedTop';
  const visible = useRetainAfterInactive ? mounted : active;

  const determinateWidth = useMemo(() => `${clamp01(value)}%`, [value]);

  useEffect(() => {
    if (active) {
      setMounted(true);
    }
  }, [active]);

  useEffect(() => {
    if (!visible || mode !== 'indeterminate') {
      return undefined;
    }

    return scheduleInterval(() => {
      setOffset((v) => (v >= 100 ? -45 : v + 4));
    }, 80);
  }, [visible, mode]);

  useEffect(() => {
    if (!useRetainAfterInactive || active || !mounted) {
      return undefined;
    }

    return scheduleTimeout(() => {
      setMounted(false);
    }, retainAfterInactiveMs);
  }, [active, mounted, retainAfterInactiveMs, useRetainAfterInactive]);

  if (!visible) {
    return null;
  }

  const track = (
    <YStack
      position="relative"
      width="100%"
      height={trackHeight}
      borderRadius="$1"
      backgroundColor="$borderColor"
      opacity={0.55}
      overflow="hidden"
    >
      {mode === 'determinate' ? (
        <YStack
          position="absolute"
          top={0}
          left={0}
          height="100%"
          width={determinateWidth}
          borderRadius="$1"
          backgroundColor="$accentColor"
        />
      ) : (
        <YStack
          position="absolute"
          top={0}
          height="100%"
          width="42%"
          borderRadius="$1"
          backgroundColor="$accentColor"
          left={`${offset}%`}
        />
      )}
    </YStack>
  );

  const centerBlock = (
    <YStack flex={1} minWidth={0} alignItems="center" gap="$1">
      {label !== null && label !== undefined && label !== '' ? (
        typeof label === 'string' ? (
          <Text fontSize="$2" color="$colorSecondary" numberOfLines={1} textAlign="center" width="100%">
            {label}
          </Text>
        ) : (
          label
        )
      ) : null}
      {track}
    </YStack>
  );

  const body = (
    <YStack
      width="100%"
      pointerEvents="box-none"
      opacity={useRetainAfterInactive ? (active ? 1 : 0) : 1}
      animation={useRetainAfterInactive ? 'medium' : undefined}
      gap="$2"
    >
      {hasSlot(header) ? <YStack width="100%">{renderNode(header)}</YStack> : null}

      <XStack width="100%" alignItems="center" justifyContent="center" gap="$2" pointerEvents="box-none">
        {hasSlot(leftSlot) ? (
          <XStack flexShrink={0} alignItems="center" pointerEvents="auto">
            {renderNode(leftSlot)}
          </XStack>
        ) : null}
        {centerBlock}
        {hasSlot(rightSlot) ? (
          <XStack flexShrink={0} alignItems="center" pointerEvents="auto">
            {renderNode(rightSlot)}
          </XStack>
        ) : null}
      </XStack>

      {hasSlot(footer) ? (
        <YStack width="100%" flexShrink={0}>
          {renderNode(footer)}
        </YStack>
      ) : null}
    </YStack>
  );

  if (variant === 'fixedTop') {
    return (
      <YStack
        position="fixed"
        top={0}
        left={0}
        right={0}
        zIndex={zIndex}
        paddingHorizontal="$1"
        paddingTop="$1"
        pointerEvents="box-none"
        backgroundColor="transparent"
      >
        {body}
      </YStack>
    );
  }

  return <YStack width="100%" pointerEvents="box-none">{body}</YStack>;
}

export default ProgressBar;
