import { useState, useEffect, useMemo } from 'react'
import { useRoutePokemon } from '../hooks/useGames'
import { useNameSuggestions } from '../hooks/useRuns'
import { EncounterMethodBadge, getMethodLabel, METHOD_ORDER } from './EncounterMethodBadge'
import type { Route, EncounterDetail, EncounterStatus, RouteEncounterDetail } from '../types'

interface EncounterModalProps {
  route: Route
  gameId: number
  runId: number
  namingScheme?: string | null | undefined
  isGenlocke?: boolean | undefined
  existing?: EncounterDetail | undefined
  dupedPokemonIds?: Set<number> | undefined
  retiredPokemonIds?: Set<number> | undefined
  onSubmit: (data: {
    routeId: number
    pokemonId: number
    nickname?: string | undefined
    status: EncounterStatus
    catchLevel?: number | undefined
  }) => void
  onUpdate?:
    | ((data: {
        id: number
        data: {
          nickname?: string | undefined
          status?: EncounterStatus | undefined
          faintLevel?: number | undefined
          deathCause?: string | undefined
        }
      }) => void)
    | undefined
  onClose: () => void
  isPending: boolean
}

const statusOptions: {
  value: EncounterStatus
  label: string
  color: string
}[] = [
  {
    value: 'caught',
    label: 'Caught',
    color:
      'bg-green-100 text-green-800 border-green-300 dark:bg-green-900/40 dark:text-green-300 dark:border-green-700',
  },
  {
    value: 'fainted',
    label: 'Fainted',
    color:
      'bg-red-100 text-red-800 border-red-300 dark:bg-red-900/40 dark:text-red-300 dark:border-red-700',
  },
  {
    value: 'missed',
    label: 'Missed / Ran',
    color: 'bg-surface-2 text-text-primary border-border-default',
  },
]

const SPECIAL_METHODS = ['starter', 'gift', 'fossil', 'trade']

interface GroupedEncounter {
  encounter: RouteEncounterDetail
  conditions: string[]
  displayRate: number | null
}

function getUniqueConditions(pokemon: RouteEncounterDetail[]): string[] {
  const conditions = new Set<string>()
  for (const rp of pokemon) {
    if (rp.condition) conditions.add(rp.condition)
  }
  return [...conditions].sort()
}

function groupByMethod(
  pokemon: RouteEncounterDetail[],
  selectedCondition: string | null
): { method: string; pokemon: GroupedEncounter[] }[] {
  const groups = new Map<string, Map<number, GroupedEncounter>>()

  // Build a lookup: pokemonId+method -> condition -> rate
  const rateByCondition = new Map<string, Map<string, number>>()
  for (const rp of pokemon) {
    if (rp.condition) {
      const key = `${rp.pokemonId}:${rp.encounterMethod}`
      let condMap = rateByCondition.get(key)
      if (!condMap) {
        condMap = new Map()
        rateByCondition.set(key, condMap)
      }
      condMap.set(rp.condition, rp.encounterRate)
    }
  }

  for (const rp of pokemon) {
    // When a specific condition is selected, skip pokemon with 0% under that condition
    if (selectedCondition) {
      const key = `${rp.pokemonId}:${rp.encounterMethod}`
      const condMap = rateByCondition.get(key)
      if (condMap) {
        const rate = condMap.get(selectedCondition)
        if (rate === 0) continue
        // Skip entries for other conditions (we only want one entry per pokemon)
        if (rp.condition && rp.condition !== selectedCondition) continue
      }
    } else {
      // "All" mode: skip 0% entries
      if (rp.encounterRate === 0 && rp.condition) continue
    }

    let methodGroup = groups.get(rp.encounterMethod)
    if (!methodGroup) {
      methodGroup = new Map()
      groups.set(rp.encounterMethod, methodGroup)
    }

    const existing = methodGroup.get(rp.pokemonId)
    if (existing) {
      if (rp.condition) existing.conditions.push(rp.condition)
    } else {
      // Determine the display rate
      let displayRate: number | null = null
      const isSpecial = SPECIAL_METHODS.includes(rp.encounterMethod)
      if (!isSpecial) {
        if (selectedCondition) {
          const key = `${rp.pokemonId}:${rp.encounterMethod}`
          const condMap = rateByCondition.get(key)
          if (condMap) {
            displayRate = condMap.get(selectedCondition) ?? null
          } else {
            displayRate = rp.encounterRate
          }
        } else if (!rp.condition) {
          // "All" mode: show the base rate for non-condition entries
          displayRate = rp.encounterRate
        }
      }

      methodGroup.set(rp.pokemonId, {
        encounter: rp,
        conditions: rp.condition ? [rp.condition] : [],
        displayRate,
      })
    }
  }
  return [...groups.entries()]
    .sort(([a], [b]) => {
      const ai = METHOD_ORDER.indexOf(a)
      const bi = METHOD_ORDER.indexOf(b)
      return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi)
    })
    .map(([method, pokemonMap]) => ({
      method,
      pokemon: [...pokemonMap.values()].sort((a, b) => (b.displayRate ?? 0) - (a.displayRate ?? 0)),
    }))
}

function pickRandomPokemon(
  pokemon: RouteEncounterDetail[],
  dupedIds?: Set<number>
): RouteEncounterDetail | null {
  // Deduplicate by pokemonId (conditions may create multiple entries)
  const seen = new Set<number>()
  const unique = pokemon.filter((rp) => {
    if (rp.encounterRate === 0) return false
    if (seen.has(rp.pokemonId)) return false
    seen.add(rp.pokemonId)
    return true
  })
  const eligible = dupedIds ? unique.filter((rp) => !dupedIds.has(rp.pokemonId)) : unique
  if (eligible.length === 0) return null
  return eligible[Math.floor(Math.random() * eligible.length)] ?? null
}

export function EncounterModal({
  route,
  gameId,
  runId,
  namingScheme,
  isGenlocke,
  existing,
  dupedPokemonIds,
  retiredPokemonIds,
  onSubmit,
  onUpdate,
  onClose,
  isPending,
}: EncounterModalProps) {
  const { data: routePokemon, isLoading: loadingPokemon } = useRoutePokemon(route.id, gameId)

  const [selectedPokemon, setSelectedPokemon] = useState<RouteEncounterDetail | null>(null)
  const [status, setStatus] = useState<EncounterStatus>(existing?.status ?? 'caught')
  const [nickname, setNickname] = useState(existing?.nickname ?? '')
  const [catchLevel, setCatchLevel] = useState<string>(existing?.catchLevel?.toString() ?? '')
  const [faintLevel, setFaintLevel] = useState<string>('')
  const [deathCause, setDeathCause] = useState('')
  const [search, setSearch] = useState('')
  const [selectedCondition, setSelectedCondition] = useState<string | null>(null)

  const isEditing = !!existing

  const showSuggestions = !!namingScheme && status === 'caught' && !isEditing
  const lineagePokemonId = isGenlocke && selectedPokemon ? selectedPokemon.pokemonId : null
  const {
    data: suggestions,
    refetch: regenerate,
    isFetching: loadingSuggestions,
  } = useNameSuggestions(showSuggestions ? runId : null, lineagePokemonId)

  // Pre-select pokemon when editing
  useEffect(() => {
    if (existing && routePokemon) {
      const match = routePokemon.find((rp) => rp.pokemonId === existing.pokemonId)
      if (match) setSelectedPokemon(match)
    }
  }, [existing, routePokemon])

  const availableConditions = useMemo(
    () => (routePokemon ? getUniqueConditions(routePokemon) : []),
    [routePokemon]
  )

  const filteredPokemon = routePokemon?.filter((rp) =>
    rp.pokemon.name.toLowerCase().includes(search.toLowerCase())
  )

  const groupedPokemon = useMemo(
    () => (filteredPokemon ? groupByMethod(filteredPokemon, selectedCondition) : []),
    [filteredPokemon, selectedCondition]
  )
  const hasMultipleGroups = groupedPokemon.length > 1

  const handleSubmit = () => {
    if (isEditing && onUpdate) {
      onUpdate({
        id: existing.id,
        data: {
          nickname: nickname || undefined,
          status,
          faintLevel: faintLevel ? Number(faintLevel) : undefined,
          deathCause: deathCause || undefined,
        },
      })
    } else if (selectedPokemon) {
      onSubmit({
        routeId: route.id,
        pokemonId: selectedPokemon.pokemonId,
        nickname: nickname || undefined,
        status,
        catchLevel: catchLevel ? Number(catchLevel) : undefined,
      })
    }
  }

  const canSubmit = isEditing || selectedPokemon

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
      <div className="fixed inset-0 bg-black/50" onClick={onClose} />
      <div className="relative bg-surface-1 rounded-xl shadow-xl max-w-lg w-full max-h-[90vh] overflow-y-auto">
        <div className="sticky top-0 bg-surface-1 border-b border-border-default px-6 py-4 rounded-t-xl">
          <div className="flex items-center justify-between">
            <h2 className="text-lg font-semibold text-text-primary">
              {isEditing ? 'Edit Encounter' : 'Log Encounter'}
            </h2>
            <button onClick={onClose} className="text-gray-400 hover:text-text-primary">
              <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M6 18L18 6M6 6l12 12"
                />
              </svg>
            </button>
          </div>
          <p className="text-sm text-text-tertiary mt-1">{route.name}</p>
        </div>

        <div className="px-6 py-4 space-y-4">
          {/* Pokemon Selection (only for new encounters) */}
          {!isEditing && (
            <div>
              <div className="flex items-center justify-between mb-1">
                <label className="block text-sm font-medium text-text-secondary">Pokemon</label>
                {!loadingPokemon && routePokemon && routePokemon.length > 0 && (
                  <button
                    type="button"
                    disabled={
                      loadingPokemon ||
                      !routePokemon ||
                      (dupedPokemonIds
                        ? routePokemon.every((rp) => dupedPokemonIds.has(rp.pokemonId))
                        : false)
                    }
                    onClick={() => {
                      if (routePokemon) {
                        setSelectedPokemon(pickRandomPokemon(routePokemon, dupedPokemonIds))
                      }
                    }}
                    className="px-2.5 py-1 text-xs font-medium rounded-lg border border-purple-300 dark:border-purple-600 text-purple-600 dark:text-purple-400 hover:bg-purple-50 dark:hover:bg-purple-900/20 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
                  >
                    {selectedPokemon ? 'Re-roll' : 'Randomize'}
                  </button>
                )}
              </div>
              {loadingPokemon ? (
                <div className="flex items-center justify-center py-4">
                  <div className="w-10 h-10 border-2 border-blue-600 border-t-transparent rounded-full animate-spin" />
                </div>
              ) : filteredPokemon && filteredPokemon.length > 0 ? (
                <>
                  {(routePokemon?.length ?? 0) > 6 && (
                    <input
                      type="text"
                      placeholder="Search pokemon..."
                      value={search}
                      onChange={(e) => setSearch(e.target.value)}
                      className="w-full px-3 py-1.5 mb-2 rounded-lg border border-border-default bg-surface-2 text-text-primary text-sm focus:outline-none focus:ring-2 focus:ring-accent-400"
                    />
                  )}
                  {availableConditions.length > 0 && (
                    <div className="flex flex-wrap gap-1 mb-2">
                      <button
                        type="button"
                        onClick={() => setSelectedCondition(null)}
                        className={`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors ${
                          selectedCondition === null
                            ? 'bg-purple-100 border-purple-300 text-purple-800 dark:bg-purple-900/40 dark:border-purple-600 dark:text-purple-300'
                            : 'border-border-default text-text-tertiary hover:border-purple-300 dark:hover:border-purple-600'
                        }`}
                      >
                        All
                      </button>
                      {availableConditions.map((cond) => (
                        <button
                          key={cond}
                          type="button"
                          onClick={() => setSelectedCondition(cond)}
                          className={`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors capitalize ${
                            selectedCondition === cond
                              ? 'bg-purple-100 border-purple-300 text-purple-800 dark:bg-purple-900/40 dark:border-purple-600 dark:text-purple-300'
                              : 'border-border-default text-text-tertiary hover:border-purple-300 dark:hover:border-purple-600'
                          }`}
                        >
                          {cond}
                        </button>
                      ))}
                    </div>
                  )}
                  <div className="max-h-64 overflow-y-auto space-y-3">
                    {groupedPokemon.map(({ method, pokemon }, groupIdx) => (
                      <div key={method}>
                        {groupIdx > 0 && <div className="border-t border-border-default mb-3" />}
                        {hasMultipleGroups && (
                          <div className="text-xs font-medium text-text-tertiary mb-1.5">
                            {getMethodLabel(method)}
                          </div>
                        )}
                        <div className="grid grid-cols-3 gap-2">
                          {pokemon.map(({ encounter: rp, conditions, displayRate }) => {
                            const isDuped = dupedPokemonIds?.has(rp.pokemonId) ?? false
                            const isSelected =
                              selectedPokemon?.pokemonId === rp.pokemonId &&
                              selectedPokemon?.encounterMethod === rp.encounterMethod
                            return (
                              <button
                                key={`${rp.encounterMethod}-${rp.pokemonId}`}
                                type="button"
                                onClick={() => !isDuped && setSelectedPokemon(rp)}
                                disabled={isDuped}
                                className={`flex flex-col items-center p-2 rounded-lg border text-center transition-colors ${
                                  isDuped
                                    ? 'opacity-40 cursor-not-allowed border-border-default'
                                    : isSelected
                                      ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/30'
                                      : 'border-border-default hover:border-border-default'
                                }`}
                              >
                                {rp.pokemon.spriteUrl ? (
                                  <img
                                    src={rp.pokemon.spriteUrl}
                                    alt={rp.pokemon.name}
                                    className="w-10 h-10"
                                  />
                                ) : (
                                  <div className="w-10 h-10 rounded-full bg-surface-3 flex items-center justify-center text-xs font-bold">
                                    {rp.pokemon.name[0]?.toUpperCase()}
                                  </div>
                                )}
                                <span className="text-xs text-text-secondary mt-1 capitalize">
                                  {rp.pokemon.name}
                                </span>
                                {isDuped && (
                                  <span className="text-[10px] text-gray-400 italic">
                                    {retiredPokemonIds?.has(rp.pokemonId)
                                      ? 'retired (HoF)'
                                      : 'already caught'}
                                  </span>
                                )}
                                {!isDuped && SPECIAL_METHODS.includes(rp.encounterMethod) && (
                                  <EncounterMethodBadge method={rp.encounterMethod} />
                                )}
                                {!isDuped && displayRate !== null && displayRate !== undefined && (
                                  <span className="text-[10px] text-purple-500 dark:text-purple-400 font-medium">
                                    {displayRate}%
                                  </span>
                                )}
                                {!isDuped &&
                                  selectedCondition === null &&
                                  conditions.length > 0 && (
                                    <span className="text-[10px] text-purple-500 dark:text-purple-400">
                                      {conditions.join(', ')}
                                    </span>
                                  )}
                                {!isDuped && (
                                  <span className="text-[10px] text-gray-400">
                                    Lv. {rp.minLevel}
                                    {rp.maxLevel !== rp.minLevel && `–${rp.maxLevel}`}
                                  </span>
                                )}
                              </button>
                            )
                          })}
                        </div>
                      </div>
                    ))}
                  </div>
                </>
              ) : (
                <p className="text-sm text-text-tertiary py-2">No pokemon data for this route</p>
              )}
            </div>
          )}

          {/* Editing: show pokemon info */}
          {isEditing && existing && (
            <div className="flex items-center gap-3 p-3 bg-surface-0/50 rounded-lg">
              {existing.pokemon.spriteUrl ? (
                <img
                  src={existing.pokemon.spriteUrl}
                  alt={existing.pokemon.name}
                  className="w-12 h-12"
                />
              ) : (
                <div className="w-12 h-12 rounded-full bg-surface-3 flex items-center justify-center text-lg font-bold">
                  {existing.pokemon.name[0]?.toUpperCase()}
                </div>
              )}
              <div>
                <div className="font-medium text-text-primary capitalize">
                  {existing.pokemon.name}
                </div>
                <div className="text-xs text-gray-500">
                  Caught at Lv. {existing.catchLevel ?? '?'}
                </div>
              </div>
            </div>
          )}

          {/* Status */}
          <div>
            <label className="block text-sm font-medium text-text-secondary mb-1">Status</label>
            <div className="flex gap-2">
              {statusOptions.map((opt) => (
                <button
                  key={opt.value}
                  type="button"
                  onClick={() => setStatus(opt.value)}
                  className={`flex-1 px-3 py-2 rounded-lg border text-sm font-medium transition-colors ${
                    status === opt.value
                      ? opt.color
                      : 'border-border-default text-text-tertiary hover:border-gray-300'
                  }`}
                >
                  {opt.label}
                </button>
              ))}
            </div>
          </div>

          {/* Nickname (for caught) */}
          {status === 'caught' && (
            <div>
              <label
                htmlFor="nickname"
                className="block text-sm font-medium text-text-secondary mb-1"
              >
                Nickname
              </label>
              <input
                id="nickname"
                type="text"
                value={nickname}
                onChange={(e) => setNickname(e.target.value)}
                placeholder="Give it a name..."
                className="w-full px-3 py-2 rounded-lg border border-border-default bg-surface-2 text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-400"
              />
              {showSuggestions && suggestions && suggestions.length > 0 && (
                <div className="mt-2">
                  <div className="flex items-center justify-between mb-1.5">
                    <span className="text-xs text-text-tertiary">Suggestions ({namingScheme})</span>
                    <button
                      type="button"
                      onClick={() => regenerate()}
                      disabled={loadingSuggestions}
                      className="text-xs text-text-link hover:text-blue-700 dark:hover:text-blue-300 disabled:opacity-50 transition-colors"
                    >
                      {loadingSuggestions ? 'Loading...' : 'Regenerate'}
                    </button>
                  </div>
                  <div className="flex flex-wrap gap-1.5">
                    {suggestions.map((name) => (
                      <button
                        key={name}
                        type="button"
                        onClick={() => setNickname(name)}
                        className={`px-2.5 py-1 text-xs rounded-full border transition-colors ${
                          nickname === name
                            ? 'bg-blue-100 border-blue-300 text-blue-800 dark:bg-blue-900/40 dark:border-blue-600 dark:text-blue-300'
                            : 'border-border-default text-text-secondary hover:border-blue-300 dark:hover:border-blue-600 hover:bg-blue-50 dark:hover:bg-blue-900/20'
                        }`}
                      >
                        {name}
                      </button>
                    ))}
                  </div>
                </div>
              )}
            </div>
          )}

          {/* Level (for new caught encounters) */}
          {!isEditing && status === 'caught' && (
            <div>
              <label
                htmlFor="catch-level"
                className="block text-sm font-medium text-text-secondary mb-1"
              >
                Catch Level
              </label>
              <input
                id="catch-level"
                type="number"
                min={1}
                max={100}
                value={catchLevel}
                onChange={(e) => setCatchLevel(e.target.value)}
                placeholder={
                  selectedPokemon
                    ? `${selectedPokemon.minLevel}–${selectedPokemon.maxLevel}`
                    : 'Level'
                }
                className="w-24 px-3 py-2 rounded-lg border border-border-default bg-surface-2 text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-400"
              />
            </div>
          )}

          {/* Faint Level + Death Cause (only when editing a caught pokemon to mark dead) */}
          {isEditing && existing?.status === 'caught' && existing?.faintLevel === null && (
            <>
              <div>
                <label
                  htmlFor="faint-level"
                  className="block text-sm font-medium text-text-secondary mb-1"
                >
                  Faint Level <span className="font-normal text-gray-400">(mark as dead)</span>
                </label>
                <input
                  id="faint-level"
                  type="number"
                  min={1}
                  max={100}
                  value={faintLevel}
                  onChange={(e) => setFaintLevel(e.target.value)}
                  placeholder="Leave empty if still alive"
                  className="w-full px-3 py-2 rounded-lg border border-border-default bg-surface-2 text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-400"
                />
              </div>
              <div>
                <label
                  htmlFor="death-cause"
                  className="block text-sm font-medium text-text-secondary mb-1"
                >
                  Cause of Death <span className="font-normal text-gray-400">(optional)</span>
                </label>
                <input
                  id="death-cause"
                  type="text"
                  maxLength={100}
                  value={deathCause}
                  onChange={(e) => setDeathCause(e.target.value)}
                  placeholder="e.g. Crit from rival's Charizard"
                  className="w-full px-3 py-2 rounded-lg border border-border-default bg-surface-2 text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-400"
                />
              </div>
            </>
          )}
        </div>

        <div className="sticky bottom-0 bg-surface-1 border-t border-border-default px-6 py-4 rounded-b-xl flex justify-end gap-3">
          <button
            type="button"
            onClick={onClose}
            className="px-4 py-2 text-text-secondary bg-surface-2 rounded-lg font-medium hover:bg-surface-3 transition-colors"
          >
            Cancel
          </button>
          <button
            type="button"
            disabled={!canSubmit || isPending}
            onClick={handleSubmit}
            className="px-4 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
          >
            {isPending ? 'Saving...' : isEditing ? 'Update' : 'Log Encounter'}
          </button>
        </div>
      </div>
    </div>
  )
}
