import { memo, useMemo, useCallback, Fragment } from 'react'
import type { ReactNode } from 'react'
import { motion } from 'framer-motion'
import { cn } from '../../lib/utils'
import { useReasoningStepContext } from './context'
import type {
  ReasoningStepHeaderProps,
  ReasoningStepDetailsProps,
  ReasoningStepDetailRowProps,
  ReasoningStepDetail,
} from './types'

// ===== 样式常量 =====

const iconSizeStyle = { width: 'var(--font-size-xs)', height: 'var(--font-size-xs)' }

const FILE_ICON_COLOR_MAP: Record<string, string> = {
  ts: 'text-info', tsx: 'text-info', js: 'text-info', jsx: 'text-info',
  py: 'text-teal',
  md: 'text-text-secondary', mdx: 'text-text-secondary',
}

function getFileIconColorClass(fileType?: string, filename?: string): string {
  const ext = (fileType ?? filename?.split('.').pop() ?? '').toLowerCase()
  return FILE_ICON_COLOR_MAP[ext] ?? 'text-text-tertiary'
}

// ===== 辅助组件 =====

const FileIconPlaceholder = memo(function FileIconPlaceholder({ className }: { className?: string }) {
  return (
    <span
      className={cn('inline-flex items-center justify-center text-xs leading-xs font-mono', className)}
      style={iconSizeStyle}
      aria-hidden
    >
      ⎔
    </span>
  )
})

const InlineRow = memo(function InlineRow({
  prefix,
  primary,
  secondary,
}: {
  prefix?: ReactNode
  primary: string
  secondary?: string
}) {
  return (
    <div className="flex flex-row items-center gap-1.5 min-w-0 flex-1">
      {prefix ? <div className="flex items-center justify-center shrink-0">{prefix}</div> : null}
      <div className="flex flex-row items-center gap-1 min-w-0 overflow-hidden flex-1">
        <span className="text-xs leading-xs font-normal text-text-tertiary truncate" title={primary}>
          {primary}
        </span>
        {secondary ? (
          <span className="text-xs leading-xs font-normal text-text-quaternary truncate" title={secondary}>
            {secondary}
          </span>
        ) : null}
      </div>
    </div>
  )
})

// ===== ReasoningStep.Header =====

export function ReasoningStepHeader({ className }: ReasoningStepHeaderProps) {
  const ctx = useReasoningStepContext()

  const dynamicSpread = useMemo(() => {
    if (typeof ctx.text === 'string') return ctx.text.length * ctx.spread
    return 10 * ctx.spread
  }, [ctx.text, ctx.spread])

  const getIcon = () => (ctx.icon ? ctx.icon : ctx.eyeIcon)

  const renderPrimaryText = () => {
    if (typeof ctx.text !== 'string') return ctx.text
    if (ctx.status === 'in-progress') {
      return (
        <motion.span
          className="text-xs leading-xs font-normal relative inline-block bg-clip-text text-transparent bg-[length:250%_100%,auto] bg-no-repeat truncate"
          style={{
            backgroundImage: `linear-gradient(90deg, transparent calc(50% - ${dynamicSpread}px), var(--color-text), transparent calc(50% + ${dynamicSpread}px)), linear-gradient(var(--color-text-tertiary), var(--color-text-tertiary))`,
          }}
          initial={{ backgroundPosition: '100% center' }}
          animate={{ backgroundPosition: '0% center' }}
          transition={{ repeat: Infinity, duration: ctx.duration, ease: 'linear' }}
        >
          {ctx.text}
        </motion.span>
      )
    }
    return (
      <span className="text-xs leading-xs font-normal text-text-tertiary truncate">
        {ctx.text}
      </span>
    )
  }

  const renderDescription = () => {
    if (!ctx.description) return null
    return (
      <span className="text-xs leading-xs font-normal text-text-quaternary truncate">
        {ctx.description}
      </span>
    )
  }

  return (
    <div
      className={cn('flex flex-row items-center gap-1.5', ctx.hasExpandableContent && 'cursor-pointer', className)}
      onClick={ctx.toggleExpanded}
    >
      {ctx.effectiveShowIcon && (
        <div className="flex items-center justify-center shrink-0" style={iconSizeStyle}>
          {getIcon()}
        </div>
      )}
      <div className="flex flex-row items-center gap-1 flex-1 min-w-0">
        {renderPrimaryText()}
        {ctx.description ? renderDescription() : null}
        {ctx.hasExpandableContent && (
          <div
            className={cn(
              'flex items-center justify-center shrink-0 text-text-tertiary transition-opacity duration-200 ease-in-out',
              ctx.effectiveExpanded ? 'opacity-100' : ctx.isHovered ? 'opacity-100' : 'opacity-0',
              ctx.effectiveExpanded ? 'rotate-90' : 'rotate-0'
            )}
            style={iconSizeStyle}
          >
            {ctx.arrowRightIcon}
          </div>
        )}
      </div>
    </div>
  )
}

ReasoningStepHeader.displayName = 'ReasoningStepHeader'

// ===== ReasoningStep.DetailRow（内部递归组件）=====

interface DetailRowInternalProps {
  detail: ReasoningStepDetail
  depth: number
  pathKey: string
  detailIndent: string
  disableExpandAnimation: boolean
  expandedNestedKeys: Set<string>
  onToggleNested: (pathKey: string) => void
  arrowRightIcon: ReactNode
}

const DetailRowInternal = memo(function DetailRowInternal({
  detail,
  depth,
  pathKey,
  detailIndent,
  expandedNestedKeys,
  onToggleNested,
  arrowRightIcon,
}: DetailRowInternalProps) {
  const hasNested = detail.details != null && detail.details.length > 0
  const isExpandedNested = expandedNestedKeys.has(pathKey)
  const showIconOnRow = !hasNested
  const primary = detail.type === 'action' ? detail.action : detail.filename
  const secondary =
    detail.type === 'action' ? detail.desc : [detail.lines, detail.path].filter(Boolean).join(' ')
  const prefix =
    showIconOnRow && detail.type === 'file' ? (
      <FileIconPlaceholder className={getFileIconColorClass(detail.fileType, detail.filename)} />
    ) : undefined

  const handleClick = useCallback(() => {
    if (hasNested) onToggleNested(pathKey)
  }, [hasNested, onToggleNested, pathKey])

  const rowContent = (
    <div
      className={cn(
        'flex flex-row items-center gap-1.5 self-stretch min-h-5 min-w-0',
        hasNested && 'cursor-pointer'
      )}
      onClick={hasNested ? handleClick : undefined}
    >
      <InlineRow prefix={prefix} primary={primary} secondary={secondary} />
      {hasNested && (
        <div
          className={cn(
            'flex items-center justify-center shrink-0 text-text-tertiary',
            isExpandedNested ? 'rotate-90' : 'rotate-0'
          )}
          style={iconSizeStyle}
        >
          {arrowRightIcon}
        </div>
      )}
    </div>
  )

  const rowWithOptionalLine = (
    <div
      className="flex flex-row items-center gap-0 self-stretch"
      style={{ marginLeft: detailIndent }}
    >
      <div className="shrink-0 w-[var(--font-size-xs)] h-5 border-l border-border-tertiary" />
      {rowContent}
    </div>
  )

  if (!hasNested) return <Fragment key={pathKey}>{rowWithOptionalLine}</Fragment>

  const isThirdLevelContainer = depth === 1
  const nestedWrapperClass = isThirdLevelContainer
    ? 'rounded-lg border border-border-tertiary bg-bg-base p-2 flex flex-col gap-0'
    : 'flex flex-col'

  return (
    <div key={pathKey} className="flex flex-col self-stretch">
      {rowWithOptionalLine}
      <div
        className={cn(
          'grid transition-[grid-template-rows,opacity] duration-150 ease-out',
          isExpandedNested ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'
        )}
      >
        <div className="overflow-hidden">
          <div className={cn(nestedWrapperClass)}>
            {detail.details?.map((d, i) => (
              <DetailRowInternal
                key={`${pathKey}-${i}`}
                detail={d}
                depth={depth + 1}
                pathKey={`${pathKey}-${i}`}
                detailIndent={detailIndent}
                disableExpandAnimation={false}
                expandedNestedKeys={expandedNestedKeys}
                onToggleNested={onToggleNested}
                arrowRightIcon={arrowRightIcon}
              />
            ))}
          </div>
        </div>
      </div>
    </div>
  )
})

// ===== ReasoningStep.DetailRow（公开 API）=====

export function ReasoningStepDetailRow({
  detail,
  depth = 0,
  pathKey = '0',
}: ReasoningStepDetailRowProps) {
  const ctx = useReasoningStepContext()
  return (
    <DetailRowInternal
      detail={detail}
      depth={depth}
      pathKey={pathKey}
      detailIndent={ctx.detailIndent}
      disableExpandAnimation={ctx.disableExpandAnimation}
      expandedNestedKeys={ctx.expandedNestedKeys}
      onToggleNested={ctx.toggleNested}
      arrowRightIcon={ctx.arrowRightIcon}
    />
  )
}

ReasoningStepDetailRow.displayName = 'ReasoningStepDetailRow'

// ===== ReasoningStep.Details =====

export function ReasoningStepDetails({ className, children }: ReasoningStepDetailsProps) {
  const ctx = useReasoningStepContext()
  const hasDetails = ctx.details != null && ctx.details.length > 0

  return (
    <div
      className={cn(
        'grid transition-[grid-template-rows,opacity] duration-200 ease-out',
        ctx.effectiveExpanded ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0',
        className
      )}
    >
      <div className="overflow-hidden">
        {hasDetails ? (
          <div className="flex flex-col">
            {ctx.details?.map((d, i) => (
              <DetailRowInternal
                key={String(i)}
                detail={d}
                depth={0}
                pathKey={String(i)}
                detailIndent={ctx.detailIndent}
                disableExpandAnimation={ctx.disableExpandAnimation}
                expandedNestedKeys={ctx.expandedNestedKeys}
                onToggleNested={ctx.toggleNested}
                arrowRightIcon={ctx.arrowRightIcon}
              />
            ))}
          </div>
        ) : (
          children ?? ctx.detailsChildren
        )}
      </div>
    </div>
  )
}

ReasoningStepDetails.displayName = 'ReasoningStepDetails'

// ===== ReasoningStep.DefaultLayout =====

export function ReasoningStepDefaultLayout() {
  const ctx = useReasoningStepContext()

  return (
    <div
      data-reasoning-step
      className="flex flex-col gap-1 py-0.5"
      onMouseEnter={() => ctx.setIsHovered(true)}
      onMouseLeave={() => ctx.setIsHovered(false)}
    >
      <ReasoningStepHeader />
      {ctx.hasExpandableContent && <ReasoningStepDetails />}
    </div>
  )
}

ReasoningStepDefaultLayout.displayName = 'ReasoningStepDefaultLayout'
