import { useMemo } from 'react'
import { useTmdbPerson } from '../../hooks/use-tmdb'
import { mapTmdbToJf } from '../../lib/tmdb-mapping'
import ContentRow from '../ui/ContentRow'
import type { TmdbCombinedCreditCrew, TmdbCombinedCreditCast } from '../../api/tmdb'

type Role = 'director' | 'writer' | 'composer' | 'actor'

interface Props {
  personId: number
  personName: string
  role: Role
  /** TMDB id of the current item so it gets filtered out of the row. */
  excludeTmdbId?: number | string | null
  /** Filter the credit list to the same media kind as the current page. */
  kind: 'movie' | 'tv'
  libraryMap?: Map<string, { id: string; name: string; type: string }> | undefined
}

const ROLE_LABELS: Record<Role, string> = {
  director: 'Director',
  writer: 'Writer',
  composer: 'Composer',
  actor: 'Top-billed cast',
}

/**
 * Row of other titles by a named person (director, writer, composer, or
 * actor). Hides itself when there's too little material to be useful -
 * one or two credits feels like a leak, four-plus reads as intentional.
 *
 * Filters to the same media kind (movie or tv) as the current detail
 * page so a series page doesn't get drowned in director's movie work.
 */
export default function FromSameRow({
  personId,
  personName,
  role,
  excludeTmdbId,
  kind,
  libraryMap,
}: Props) {
  const personQuery = useTmdbPerson(personId)
  const items = useMemo(() => {
    const credits = personQuery.data?.combined_credits
    if (!credits) return []

    const excludeStr = excludeTmdbId != null ? String(excludeTmdbId) : null
    const source = role === 'actor' ? credits.cast : credits.crew

    const matched = (source as Array<TmdbCombinedCreditCast | TmdbCombinedCreditCrew>).filter(c => {
      if (c.media_type !== kind) return false
      if (excludeStr && String(c.id) === excludeStr) return false
      if (!c.poster_path) return false
      if (role === 'actor') return true
      const crew = c as TmdbCombinedCreditCrew
      if (role === 'director') return crew.job === 'Director'
      if (role === 'writer') return crew.department === 'Writing'
      if (role === 'composer') {
        return (
          crew.job === 'Original Music Composer' ||
          crew.job === 'Music' ||
          crew.job === 'Composer'
        )
      }
      return false
    })

    // De-dup by tmdb id (writers especially get credited multiple times
    // on the same title under different jobs).
    const seen = new Set<string>()
    const deduped = matched.filter(c => {
      const k = String(c.id)
      if (seen.has(k)) return false
      seen.add(k)
      return true
    })

    return deduped
      .sort((a, b) => (b.vote_average || 0) * (b.popularity || 0) - (a.vote_average || 0) * (a.popularity || 0))
      .slice(0, 20)
  }, [personQuery.data, role, excludeTmdbId, kind])

  // Skip rows that would look anemic - fewer than 3 entries doesn't
  // earn its keep on the page.
  if (items.length < 3) return null

  const mapped = mapTmdbToJf(
    items.map(it => ({
      ...it,
      adult: false,
      // mapTmdbToJf reads media_type to pick kind, which is already set.
    })),
    libraryMap,
  )

  const title = role === 'actor' ? `More with ${personName}` : `From ${personName}`
  const subtitle = `${ROLE_LABELS[role]} on this title - other ${kind === 'movie' ? 'films' : 'shows'} they've worked on`

  return (
    <ContentRow
      title={title}
      subtitle={subtitle}
      items={mapped}
      layoutKey={`from_same_${role}_${personId}`}
    />
  )
}
