import { memo, type ElementType, type ReactNode } from 'react'
import type { ControllerIconStyle } from '../input'
import type { PartyMember } from '../game'
import {
  effectiveMaxHealth,
  formatEffectTime,
  memberHotEffects,
} from '../combat/rules'
import { ControllerBindingLabel } from './ControllerIcons'
import { barFillStyle } from './barStyles'

export type FloatingCombatText = {
  id: number
  memberId: string
  value: number
}

type MemberEffectMode = 'full' | 'compact' | 'timed-basic'

const MemberEffects = memo(function MemberEffects({
  member,
  mode,
}: {
  member: PartyMember
  mode: MemberEffectMode
}) {
  if (mode === 'compact') {
    return (
      <div className="member-effects">
        {memberHotEffects(member).map((effect) => (
          <span className="buff" key={effect.id}>{effect.label}</span>
        ))}
        {member.shield > 0 && <span className="buff">Shield {Math.ceil(member.shield)}</span>}
        {member.debuff && <span className="debuff">{member.debuff}</span>}
      </div>
    )
  }

  if (mode === 'timed-basic') {
    return (
      <div className="member-effects">
        {member.hotTicks > 0 && <span className="buff">Renew {formatEffectTime(member.hotTicks)}</span>}
        {member.shield > 0 && <span className="buff">Shield {Math.ceil(member.shield)}</span>}
        {member.debuff && member.debuffTicks && <span className="debuff">{member.debuff} {formatEffectTime(member.debuffTicks)}</span>}
        {(member.poisonStacks ?? 0) > 0 && <span className="debuff">Poison {member.poisonStacks}</span>}
        {(member.maxHealthPenaltyTicks ?? 0) > 0 && <span className="debuff">Max HP -25% {formatEffectTime(member.maxHealthPenaltyTicks ?? 0)}</span>}
        {(member.healingReductionTicks ?? 0) > 0 && <span className="debuff">Healing -25% {formatEffectTime(member.healingReductionTicks ?? 0)}</span>}
      </div>
    )
  }

  return (
    <div className="member-effects">
      {memberHotEffects(member).map((effect) => (
        <span className="buff" key={effect.id}>{effect.label} {formatEffectTime(effect.ticks)}</span>
      ))}
      {member.shield > 0 && <span className="buff">Shield {Math.ceil(member.shield)}</span>}
      {(member.damageReductionTicks ?? 0) > 0 && <span className="buff">Barkskin {formatEffectTime(member.damageReductionTicks ?? 0)}</span>}
      {(member.bounceHeals ?? []).map((effect) => (
        <span className="buff" key={effect.id}>{effect.label} {effect.charges}</span>
      ))}
      {member.debuff && member.debuffTicks && <span className="debuff">{member.debuff} {formatEffectTime(member.debuffTicks)}</span>}
      {(member.poisonStacks ?? 0) > 0 && <span className="debuff">Poison {member.poisonStacks}</span>}
      {(member.maxHealthPenaltyTicks ?? 0) > 0 && <span className="debuff">Max HP -25% {formatEffectTime(member.maxHealthPenaltyTicks ?? 0)}</span>}
      {(member.healingReductionTicks ?? 0) > 0 && <span className="debuff">Healing -25% {formatEffectTime(member.healingReductionTicks ?? 0)}</span>}
    </div>
  )
})

export const PartyMemberFrame = memo(function PartyMemberFrame({
  as,
  member,
  selected = false,
  baseClassName = 'party-member',
  selectedClassName = 'selected',
  deadClassName = 'dead',
  effectMode = 'full',
  floatingTexts = [],
  showTargetMarker = false,
  showHeaderHealth = true,
  showHealthText = false,
  targetBinding,
  controllerIconStyle,
  onSelect,
  children,
}: {
  as?: ElementType
  member: PartyMember
  selected?: boolean
  baseClassName?: string
  selectedClassName?: string
  deadClassName?: string
  effectMode?: MemberEffectMode
  floatingTexts?: FloatingCombatText[]
  showTargetMarker?: boolean
  showHeaderHealth?: boolean
  showHealthText?: boolean
  targetBinding?: string | null
  controllerIconStyle?: ControllerIconStyle
  onSelect?: (id: string) => void
  children?: ReactNode
}) {
  const Component = as ?? (onSelect ? 'button' : 'div')
  const maxHealth = effectiveMaxHealth(member)
  const className = [
    baseClassName,
    selected ? selectedClassName : '',
    member.health <= 0 ? deadClassName : '',
  ].filter(Boolean).join(' ')
  const action = targetBinding ? {
    binding: targetBinding,
    iconStyle: controllerIconStyle ?? 'playstation',
  } : null

  return (
    <Component
      className={className}
      data-party-member-id={member.id}
      onClick={onSelect ? () => onSelect(member.id) : undefined}
      aria-pressed={onSelect ? selected : undefined}
      type={Component === 'button' ? 'button' : undefined}
    >
      {showTargetMarker && (
        <span className="target-marker" aria-hidden="true">
          <i />
          Target
        </span>
      )}
      <div className="member-header">
        <span className={`role role-${member.role.toLowerCase()}`}>{member.role[0]}</span>
        <strong>{member.name}</strong>
        {showHeaderHealth && <small>{Math.ceil(member.health)} / {maxHealth}</small>}
      </div>
      <div className="bar member-health">
        <span style={barFillStyle((member.health / maxHealth) * 100)} />
        {member.shield > 0 && <i style={barFillStyle((member.shield / maxHealth) * 100, 'right')} />}
        {showHealthText && <em className="health-text">{Math.floor(member.health)} / {maxHealth}</em>}
      </div>
      <div className="floating-combat-texts" aria-hidden="true">
        {floatingTexts.map((entry) => <span className="floating-heal" key={entry.id}>+{entry.value}</span>)}
      </div>
      {action && (
        <div className="member-target-key">
          <ControllerBindingLabel
            binding={action.binding}
            iconStyle={action.iconStyle}
          />
        </div>
      )}
      <MemberEffects member={member} mode={effectMode} />
      {children}
    </Component>
  )
})
