'use client';

import { useEffect, useState, useTransition } from 'react';
import {
  MatchDetail,
  MatchQueueFilter,
  RecentMatchSummary,
  getMatchDetail,
  getSnapshotMatches,
} from '@/lib/api';
import { localizeChampionTextWithMap } from '@/lib/champion-labels';
import { getChampionSquareUrl, getItemIconUrl, getSummonerSpellIconUrl } from '@/lib/lol-assets';
import { translateApiErrorMessage } from '@/lib/ui-text';

const FILTER_TABS: Array<{ key: MatchQueueFilter; label: string }> = [
  { key: 'ALL', label: '전체' },
  { key: 'RANKED_SOLO', label: '솔로랭크' },
  { key: 'RANKED_FLEX', label: '자유랭크' },
  { key: 'ARAM', label: '칼바람' },
  { key: 'GAME_TYPES', label: '게임 종류' },
];
const OVERVIEW_TABS = ['개요', 'OP 점수', '팀 분석', '빌드', '기타'];
const PROMPT_CHIPS = [
  '최근 경기 흐름을 분석해줘',
  '현재 실력대를 알려줘',
  '요즘 팀운이 어떤지 알려줘',
  '최근 패턴을 분석해줘',
];
const ROLE_ORDER = ['TOP', 'JUNGLE', 'MID', 'ADC', 'SUP'] as const;
const ROLE_LABELS: Record<(typeof ROLE_ORDER)[number], string> = {
  TOP: '탑',
  JUNGLE: '정글',
  MID: '미드',
  ADC: '원딜',
  SUP: '서포터',
};
const PAGE_SIZE = 20;

type RecentMatch = RecentMatchSummary;

type FilterMatchCache = Record<
  MatchQueueFilter,
  {
    items: RecentMatchSummary[];
    total: number;
    hasMore: boolean;
    initialized: boolean;
  }
>;

type MatchInsightTag = {
  key: string;
  label: string;
  tone: 'blue' | 'gold' | 'green' | 'red' | 'slate';
};

export function MatchHistoryPanel({
  snapshotId,
  initialMatches,
  initialTotalMatchCount,
  championDisplayNames,
}: {
  snapshotId: string;
  initialMatches: RecentMatchSummary[];
  initialTotalMatchCount?: number;
  championDisplayNames: Record<string, string>;
}) {
  const [activeFilter, setActiveFilter] = useState<MatchQueueFilter>('ALL');
  const [filterCache, setFilterCache] = useState<FilterMatchCache>({
    ALL: {
      items: initialMatches,
      total:
        typeof initialTotalMatchCount === 'number'
          ? initialTotalMatchCount
          : initialMatches.length,
      hasMore:
        typeof initialTotalMatchCount === 'number'
          ? initialMatches.length < initialTotalMatchCount
          : initialMatches.length >= PAGE_SIZE,
      initialized: true,
    },
    RANKED_SOLO: { items: [], total: 0, hasMore: false, initialized: false },
    RANKED_FLEX: { items: [], total: 0, hasMore: false, initialized: false },
    ARAM: { items: [], total: 0, hasMore: false, initialized: false },
    GAME_TYPES: { items: [], total: 0, hasMore: false, initialized: false },
  });
  const [panelError, setPanelError] = useState('');
  const [isChangingFilter, startChangingFilter] = useTransition();
  const [isLoadingMore, startLoadingMore] = useTransition();
  const [expandedId, setExpandedId] = useState<string | null>(null);
  const [detailsById, setDetailsById] = useState<Record<string, MatchDetail>>({});
  const [loadingById, setLoadingById] = useState<Record<string, boolean>>({});
  const [errorById, setErrorById] = useState<Record<string, string>>({});

  useEffect(() => {
    setActiveFilter('ALL');
    setFilterCache({
      ALL: {
        items: initialMatches,
        total:
          typeof initialTotalMatchCount === 'number'
            ? initialTotalMatchCount
            : initialMatches.length,
        hasMore:
          typeof initialTotalMatchCount === 'number'
            ? initialMatches.length < initialTotalMatchCount
            : initialMatches.length >= PAGE_SIZE,
        initialized: true,
      },
      RANKED_SOLO: { items: [], total: 0, hasMore: false, initialized: false },
      RANKED_FLEX: { items: [], total: 0, hasMore: false, initialized: false },
      ARAM: { items: [], total: 0, hasMore: false, initialized: false },
      GAME_TYPES: { items: [], total: 0, hasMore: false, initialized: false },
    });
    setExpandedId(null);
    setDetailsById({});
    setLoadingById({});
    setErrorById({});
    setPanelError('');
  }, [initialMatches, initialTotalMatchCount, snapshotId]);

  const activeCache = filterCache[activeFilter];
  const matches = activeCache.items;
  const hasMore = activeCache.hasMore;

  const winCount = matches.filter((match) => match.win).length;
  const lossCount = Math.max(0, matches.length - winCount);
  const averageKills = averageMatchStat(matches, (match) => match.kills);
  const averageDeaths = averageMatchStat(matches, (match) => match.deaths);
  const averageAssists = averageMatchStat(matches, (match) => match.assists);
  const averageKillParticipation = averageKillParticipationRate(matches);
  const topChampions = buildChampionSummary(matches, championDisplayNames);
  const roleBars = buildRoleBars(matches);
  const winRate = matches.length ? Math.round((winCount / matches.length) * 100) : 0;

  async function toggleExpanded(participantMatchId: string) {
    const willExpand = expandedId !== participantMatchId;
    setExpandedId(willExpand ? participantMatchId : null);

    if (!willExpand || detailsById[participantMatchId] || loadingById[participantMatchId]) {
      return;
    }

    setLoadingById((current) => ({ ...current, [participantMatchId]: true }));
    setErrorById((current) => ({ ...current, [participantMatchId]: '' }));

    try {
      const detail = await getMatchDetail(participantMatchId);
      setDetailsById((current) => ({ ...current, [participantMatchId]: detail }));
    } catch (error) {
      setErrorById((current) => ({
        ...current,
        [participantMatchId]: translateApiErrorMessage(
          error instanceof Error ? error.message : 'Failed to load match detail',
        ),
      }));
    } finally {
      setLoadingById((current) => ({ ...current, [participantMatchId]: false }));
    }
  }

  function handleFilterChange(filter: MatchQueueFilter) {
    if (filter === activeFilter) return;

    setExpandedId(null);
    setPanelError('');
    setActiveFilter(filter);

    if (filterCache[filter].initialized) {
      return;
    }

    startChangingFilter(() => {
      void fetchMatchesForFilter(filter, 0, false);
    });
  }

  function loadMoreMatches() {
    if (isLoadingMore || !hasMore) return;

    const filter = activeFilter;
    const currentLength = activeCache.items.length;

    startLoadingMore(() => {
      void fetchMatchesForFilter(filter, currentLength, true);
    });
  }

  async function fetchMatchesForFilter(
    filter: MatchQueueFilter,
    offset: number,
    append: boolean,
  ) {
    try {
      setPanelError('');
      const response = await getSnapshotMatches(snapshotId, offset, PAGE_SIZE, filter);
      setFilterCache((current) => {
        const previous = current[filter];
        const nextItems = append
          ? [
              ...previous.items,
              ...response.items.filter(
                (match) =>
                  !previous.items.some(
                    (existing) => existing.participantMatchId === match.participantMatchId,
                  ),
              ),
            ]
          : response.items;

        return {
          ...current,
          [filter]: {
            items: nextItems,
            total: response.total,
            hasMore: response.hasMore,
            initialized: true,
          },
        };
      });
    } catch (error) {
      setPanelError(
        translateApiErrorMessage(
          error instanceof Error ? error.message : 'Failed to load matches',
        ),
      );
    }
  }

  return (
    <section className="min-w-0 w-full overflow-hidden rounded-[4px] border border-[#31313c] bg-[#2b2f3a]">
      <div className="border-b border-[#31313c] px-4 py-4">
        <div className="flex flex-wrap items-center gap-2 text-[13px] font-semibold sm:text-[14px]">
          {FILTER_TABS.map((tab) => (
            <button
              key={tab.key}
              type="button"
              onClick={() => handleFilterChange(tab.key)}
              disabled={isChangingFilter}
              className={`inline-flex rounded-full px-3 py-2 whitespace-nowrap transition-colors disabled:cursor-not-allowed disabled:opacity-60 ${
                  activeFilter === tab.key
                    ? 'bg-[#283a63] text-white shadow-[inset_0_0_0_1px_rgba(131,165,255,0.25)]'
                    : 'bg-[#23262f] text-[#c7cfdf] shadow-[inset_0_0_0_1px_rgba(255,255,255,0.05)]'
                }`}
              >
                {tab.label}
            </button>
          ))}
        </div>
      </div>

      <div className="grid gap-3 p-3">
        <section className="overflow-hidden rounded-[4px] border border-[#31313c] bg-[#2f3241]">
          <div className="border-b border-[#31313c] px-4 py-3">
            <div className="text-[15px] font-semibold text-white">최근 경기</div>
          </div>

          <div className="grid gap-4 px-4 py-4 lg:grid-cols-[300px_minmax(0,1fr)] 2xl:grid-cols-[300px_minmax(0,1fr)_230px]">
            <div className="flex flex-col items-center gap-4 text-center sm:flex-row sm:text-left">
              <DonutChart winRate={winRate} />
              <div>
                <div className="text-[15px] text-[#9aa4bf]">
                  {matches.length}전 {winCount}승 {lossCount}패
                </div>
                <div className="mt-2 text-[19px] text-[#cfd5e5]">
                  {averageKills.toFixed(1)} / <span className="text-[#e84057]">{averageDeaths.toFixed(1)}</span> / {averageAssists.toFixed(1)}
                </div>
                <div className="text-[32px] font-black tracking-[-0.05em] text-white">
                  {computeKda(averageKills, averageDeaths, averageAssists)} : 1
                </div>
                <div className="text-[14px] font-semibold text-[#e84057]">킬 관여 {averageKillParticipation}%</div>
              </div>
            </div>

            <div>
              <div className="text-[13px] text-[#9aa4bf]">선호 포지션 (랭크)</div>
              <div className="mt-4 flex items-end gap-3 sm:gap-4">
                {roleBars.map((role) => (
                  <div key={role.label} className="flex flex-1 flex-col items-center gap-2">
                    <div className="flex h-[98px] w-full items-end rounded-[3px] bg-[#44495d] p-1">
                      <div className="w-full rounded-[2px] bg-[#6d84f5]" style={{ height: `${role.height}%` }} />
                    </div>
                    <span className="text-[10px] font-semibold text-[#9aa4bf]">{ROLE_LABELS[role.label]}</span>
                  </div>
                ))}
              </div>
            </div>

            <div className="grid gap-3">
              <div className="text-[13px] text-[#9aa4bf]">최근 {matches.length}경기 플레이 챔피언</div>
              {topChampions.slice(0, 2).map((champion) => (
                <div key={champion.name} className="flex items-center gap-3">
                  {champion.icon ? <img src={champion.icon} alt={champion.name} className="h-8 w-8 rounded-full" /> : null}
                  <div className="min-w-0">
                    <div className="text-[14px] font-semibold text-white">{champion.name}</div>
                    <div className="text-[13px] text-[#9aa4bf]">
                      {champion.playRate}% ({champion.wins}승 / {champion.losses}패)
                      <span className="ml-2 font-semibold text-[#1fbba6]">{champion.kda}:1 KDA</span>
                    </div>
                  </div>
                </div>
              ))}
            </div>
          </div>
        </section>

        <div className="flex flex-wrap gap-2 px-1">
          {PROMPT_CHIPS.map((prompt) => (
            <span key={prompt} className="rounded-full bg-[#23262f] px-4 py-3 text-[13px] text-white shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)]">
              {prompt}
            </span>
          ))}
        </div>

        <div className="grid gap-2">
          {matches.map((match) => {
            const expanded = expandedId === match.participantMatchId;
            const detail = detailsById[match.participantMatchId];
            const loading = loadingById[match.participantMatchId];
            const error = errorById[match.participantMatchId];

            return (
              <div key={match.participantMatchId} className="grid gap-2">
                <MatchCard
                  match={match}
                  detail={detail}
                  expanded={expanded}
                  onToggle={() => void toggleExpanded(match.participantMatchId)}
                />
                {expanded ? (
                  <ExpandedMatchDetail
                    match={match}
                    detail={detail}
                    loading={loading}
                    error={error}
                    championDisplayNames={championDisplayNames}
                  />
                ) : null}
              </div>
            );
          })}
        </div>

        {hasMore ? (
          <div className="flex justify-center pt-2">
            <button
              type="button"
              onClick={loadMoreMatches}
              disabled={isLoadingMore}
              className="inline-flex min-w-[188px] items-center justify-center gap-2 rounded-[999px] border border-[#4d5f93] bg-[#232c46] px-5 py-3 text-[14px] font-semibold text-[#dfe7ff] transition-colors hover:bg-[#2a3554] disabled:cursor-not-allowed disabled:opacity-60"
            >
              <span className="text-[16px] leading-none">+</span>
              <span>{isLoadingMore ? '불러오는 중...' : '더보기'}</span>
            </button>
          </div>
        ) : null}

        {panelError ? (
          <div className="px-1 text-center text-[13px] text-[#ff9cac]">{panelError}</div>
        ) : null}
      </div>
    </section>
  );
}

function MatchCard({
  match,
  detail,
  expanded,
  onToggle,
}: {
  match: RecentMatch;
  detail?: MatchDetail;
  expanded: boolean;
  onToggle: () => void;
}) {
  const championIcon = getChampionSquareUrl(match.championName);
  const rosterBlue = detail?.teams.find((team) => team.side === 'BLUE')?.participants ?? [];
  const rosterRed = detail?.teams.find((team) => team.side === 'RED')?.participants ?? [];
  const tags = buildMatchInsightTags(match);
  const level =
    detail?.teams
      .flatMap((team) => team.participants)
      .find((participant) => participant.isPlayer)?.level ??
    clamp(Math.round(11 + match.kdaRatio), 13, 18);
  const perks =
    detail?.teams
      .flatMap((team) => team.participants)
      .find((participant) => participant.isPlayer)?.perks ?? [];
  const toggleLabel = `경기 상세 ${expanded ? '접기' : '펼치기'}: ${match.matchId}`;

  return (
    <div
      className="box-border flex min-w-0 w-full flex-col overflow-hidden rounded-[4px] border-l-[6px] md:flex-row"
      style={{
        borderLeftColor: match.win ? '#5383e8' : '#e84057',
        backgroundColor: match.win ? '#2f436e' : '#5d3942',
      }}
    >
      <div className="flex flex-1 flex-col overflow-hidden">
        <div className="flex flex-1 flex-col text-xs md:flex-row md:items-center md:gap-2 md:px-3 md:py-1">
          <div className="flex flex-row-reverse items-center justify-between gap-2 border-b border-white/10 px-3 py-2 md:w-[132px] md:flex-col md:items-baseline md:justify-normal md:border-none md:px-0 md:py-0">
            <div className="flex items-center gap-2 md:flex-col md:items-baseline md:gap-0">
              <span className={`text-[15px] font-bold ${match.win ? 'text-[#8fb2ff]' : 'text-[#ff7f93]'}`}>{queueLabel(match.queueId)}</span>
              <span className="text-[13px] text-[#d4dbeb]">{formatRelativeDate(match.gameStartAt)}</span>
            </div>
            <div className="hidden md:block">
              <div className={`text-[15px] font-bold ${match.win ? 'text-[#dce8ff]' : 'text-[#ffe3e8]'}`}>{match.win ? '승리' : '패배'}</div>
              <div className="mt-1 text-[13px] text-[#d4dbeb]">{formatDuration(match.gameDurationSec)}</div>
            </div>
          </div>

          <div className="flex min-w-0 flex-1 flex-wrap items-start gap-3 px-3 py-3 md:px-0 md:py-0 xl:flex-nowrap xl:items-center">
            <div className="flex shrink-0 items-center gap-3">
              <div className="relative h-12 w-12 shrink-0">
                {championIcon ? <img alt={match.championName} src={championIcon} className="h-12 w-12 rounded-full" /> : null}
                <span className="absolute bottom-0 right-0 flex h-5 w-5 items-center justify-center rounded-full bg-[#202330] text-[11px] font-bold text-white">
                  {level}
                </span>
              </div>
              <div className="flex gap-[2px]">
                <SpellColumn ids={match.summonerSpellIds} />
                <PerkColumn ids={perks} />
              </div>
            </div>

            <div className="min-w-0 flex-1 md:min-w-[150px]">
              <div className="flex items-center gap-2 text-[16px] font-bold text-white">
                <span>{match.kills}</span>
                <span className="text-[#ff7d8f]">/ {match.deaths} /</span>
                <span>{match.assists}</span>
              </div>
              <div className="mt-1 text-[13px] text-[#d4dbeb]">{match.kdaRatio.toFixed(2)}:1 KDA</div>
              <div className="mt-3 flex gap-[2px] lg:hidden">
                <ItemIcons items={match.items} compact />
              </div>
              <div className="mt-3 grid gap-1 text-[12px] text-[#d4dbeb] xl:hidden">
                <div className="flex flex-wrap items-center gap-1">
                  <span className="text-[#ff7488]">라인전 {match.totalDamageToChampions > 26000 ? '61' : '49'}</span>
                  <strong className="text-white">:</strong>
                  <span>{match.totalDamageToChampions > 26000 ? '39' : '51'}</span>
                </div>
                <div className="flex flex-wrap gap-x-3 gap-y-1">
                  <span>킬 관여 {formatPercent(match.killParticipation)}</span>
                  <span>
                    CS {match.totalMinionsKilled} ({(match.totalMinionsKilled / Math.max(1, match.gameDurationSec / 60)).toFixed(1)})
                  </span>
                  <span>{profileTierFromKda(match.kdaRatio)}</span>
                </div>
                {tags.length ? (
                  <div className="mt-1 flex flex-wrap items-center gap-1 lg:hidden">
                    {tags.map((tag) => (
                      <InsightBadge key={tag.key} tag={tag} compact />
                    ))}
                  </div>
                ) : null}
              </div>
            </div>

            <div className="hidden min-h-[58px] w-[126px] flex-col items-baseline border-l border-white/10 pl-3 text-[12px] text-[#d4dbeb] xl:flex 2xl:w-[146px]">
              <div className="flex items-center gap-1">
                <span className="text-[#ff7488]">라인전 {match.totalDamageToChampions > 26000 ? '61' : '49'}</span>
                <strong className="text-white">:</strong>
                <span>{match.totalDamageToChampions > 26000 ? '39' : '51'}</span>
              </div>
              <div>킬 관여 {formatPercent(match.killParticipation)}</div>
              <div>
                CS {match.totalMinionsKilled} ({(match.totalMinionsKilled / Math.max(1, match.gameDurationSec / 60)).toFixed(1)})
              </div>
              <div>{profileTierFromKda(match.kdaRatio)}</div>
            </div>

            <div className="hidden min-w-[180px] flex-1 flex-col gap-2 lg:flex 2xl:min-w-[220px]">
              <ItemIcons items={match.items} />
              <div className="flex flex-wrap items-center gap-1">
                {tags.map((tag) => (
                  <InsightBadge key={tag.key} tag={tag} />
                ))}
              </div>
            </div>

            <div className="hidden min-w-[196px] grid-cols-2 gap-3 2xl:grid 2xl:min-w-[224px]">
              <RosterColumn participants={rosterBlue} />
              <RosterColumn participants={rosterRed} />
            </div>
          </div>
        </div>
      </div>

      <button
        type="button"
        aria-label={toggleLabel}
        onClick={onToggle}
        className="flex h-11 w-full shrink-0 items-center justify-center border-t border-white/10 bg-black/10 text-[#9eb6ff] transition-colors hover:bg-black/20 md:h-auto md:w-[40px] md:border-l md:border-t-0"
      >
        <img
          src="/icons/chevron-down.svg"
          alt=""
          aria-hidden="true"
          className={`h-4 w-4 transition-transform ${expanded ? 'rotate-180' : ''}`}
        />
      </button>
    </div>
  );
}

function ExpandedMatchDetail({
  match,
  detail,
  loading,
  error,
  championDisplayNames,
}: {
  match: RecentMatch;
  detail?: MatchDetail;
  loading: boolean;
  error?: string;
  championDisplayNames: Record<string, string>;
}) {
  if (loading) {
    return (
      <div className="rounded-[4px] border border-[#31313c] bg-[#2b2f3a] px-4 py-6 text-[14px] text-[#c6cee0]">
        경기 상세 정보를 불러오는 중입니다...
      </div>
    );
  }

  if (error) {
    return (
      <div className="rounded-[4px] border border-[#31313c] bg-[#2b2f3a] px-4 py-6 text-[14px] text-[#ff9cac]">
        {error}
      </div>
    );
  }

  if (!detail) return null;

  const blueTeam = detail.teams.find((team) => team.side === 'BLUE');
  const redTeam = detail.teams.find((team) => team.side === 'RED');
  if (!blueTeam || !redTeam) return null;

  return (
    <div className="grid gap-2">
      <div className="mb-[4px] flex flex-wrap gap-[4px] rounded-[4px] bg-[#3a3d4a] p-[4px]">
        {OVERVIEW_TABS.map((tab, index) => (
          <button
            key={tab}
            className={`min-h-[40px] min-w-[120px] flex-1 rounded-[4px] px-3 text-center text-[14px] transition-colors sm:text-[15px] ${
              index === 0 ? 'bg-[#6a6d7b] font-bold text-white' : 'text-white hover:bg-white/5'
            }`}
          >
            {tab}
          </button>
        ))}
      </div>

      <div className="w-full overflow-x-auto rounded-[4px] border border-[#31313c] bg-[#2b2f3a]">
        <div className="min-w-[880px]">
          <TeamSection team={blueTeam} tone="blue" />
          <TeamSummary blueTeam={blueTeam} redTeam={redTeam} />
          <TeamSection team={redTeam} tone="red" />
        </div>
      </div>

      <div className="rounded-[4px] bg-[#23262f] px-4 py-3 text-[13px] text-[#c6cee0]">
        {localizeChampionTextWithMap(detail.aiDetail || match.aiDetail, championDisplayNames, [match.championName])}
      </div>
    </div>
  );
}

function TeamSection({ team, tone }: { team: MatchDetail['teams'][number]; tone: 'blue' | 'red' }) {
  const toneBg = tone === 'blue' ? '#313b5b' : '#53353d';
  const toneHighlight = tone === 'blue' ? '#3b4670' : '#654148';
  const titleColor = tone === 'blue' ? '#5383e8' : '#e84057';

  const maxDamage = Math.max(...team.participants.map((participant) => participant.totalDamageToChampions), 1);
  const maxTaken = Math.max(...team.participants.map((participant) => participant.totalDamageTaken), 1);

  return (
    <div>
      <div className="grid grid-cols-[minmax(0,1.9fr)_64px_92px_96px_52px_56px_minmax(0,140px)] border-b border-[#31313c] px-3 py-3 text-[13px] text-[#9aa4bf] lg:grid-cols-[minmax(0,2.2fr)_72px_104px_110px_58px_60px_minmax(0,168px)]">
        <div className="font-semibold">
          <span style={{ color: titleColor }}>{translateTeamResult(team.result)}</span> ({translateTeamSide(team.side)})
        </div>
        <div className="text-center">평점</div>
        <div className="text-center">KDA</div>
        <div className="text-center">가한 피해</div>
        <div className="text-center">와드</div>
        <div className="text-center">CS</div>
        <div className="text-center">아이템</div>
      </div>

      {team.participants.map((participant) => (
        <div
          key={`${team.side}-${participant.participantId}`}
          className="grid min-w-0 grid-cols-[minmax(0,1.9fr)_64px_92px_96px_52px_56px_minmax(0,140px)] items-center border-b border-[#31313c] px-3 py-2 text-[12px] text-[#d8dfef] lg:grid-cols-[minmax(0,2.2fr)_72px_104px_110px_58px_60px_minmax(0,168px)]"
          style={{ backgroundColor: participant.isPlayer ? toneHighlight : toneBg }}
        >
          <div className="flex min-w-0 items-center gap-2">
            <div className="flex items-center gap-1">
              <div className="relative h-8 w-8 shrink-0">
                {participant.championName ? <TeamChampionIcon championName={participant.championName} /> : null}
                <span className="absolute bottom-0 right-0 flex h-[15px] w-[15px] items-center justify-center rounded-full bg-[#202330] text-[10px] text-white">
                  {participant.level}
                </span>
              </div>
              <div className="flex gap-[2px]">
                <SpellColumn ids={participant.spells} small />
                <PerkColumn ids={participant.perks} small />
              </div>
            </div>
            <div className="min-w-0">
              <div className={`truncate text-[12px] ${participant.isPlayer ? 'font-bold text-white' : 'text-white'}`}>{participant.summonerName}</div>
              <div className="truncate text-[12px] text-[#9aa4bf]">{participant.tagLine ? `${participant.tagLine}` : '-'}</div>
            </div>
          </div>

          <div className="flex items-center justify-center gap-1">
            <div className="text-[12px] font-bold italic text-white">{participant.kdaRatio.toFixed(1)}</div>
            <span className="rounded-[999px] bg-[#b1b6c8] px-2 py-[2px] text-[10px] font-semibold text-white">
              {participant.isPlayer ? '본인' : `${Math.max(1, participant.participantId)}번`}
            </span>
          </div>

          <div className="text-center">
            <div className="leading-[14px] text-white">
              {participant.kills}/{participant.deaths}/{participant.assists}
              <span className="ml-1 text-[#a9b1c5]">({formatPercent(participant.killParticipation)})</span>
            </div>
            <div className={`font-bold ${participant.isPlayer ? 'text-[#f7b54a]' : 'text-[#cfd5e5]'}`}>{participant.kdaRatio.toFixed(2)}:1</div>
          </div>

          <div className="grid min-w-0 gap-1">
            <DamageBar value={participant.totalDamageToChampions} max={maxDamage} color="#e84057" />
            <DamageBar value={participant.totalDamageTaken} max={maxTaken} color="#9ea5bb" />
          </div>

          <div className="text-center">
            <div>{participant.controlWardsBought}</div>
            <div>{participant.wardsPlaced} / {participant.wardsKilled}</div>
          </div>

          <div className="text-center">
            <div>{participant.cs}</div>
            <div>{participant.csPerMinute}/m</div>
          </div>

          <div className="min-w-0 overflow-hidden">
            <ItemIcons items={participant.items} compact />
          </div>
        </div>
      ))}
    </div>
  );
}

function TeamSummary({
  blueTeam,
  redTeam,
}: {
  blueTeam: MatchDetail['teams'][number];
  redTeam: MatchDetail['teams'][number];
}) {
  return (
    <div className="grid items-center gap-4 border-y border-[#31313c] bg-[#252731] px-4 py-3 md:grid-cols-[140px_minmax(0,1fr)_140px]">
      <ObjectiveList objectives={blueTeam.objectives} tone="blue" />
      <div className="grid min-w-0 gap-1">
        <SummaryBar
          left={blueTeam.totalKills}
          right={redTeam.totalKills}
          label="총 킬"
          leftColor="#5383e8"
          rightColor="#d34d63"
        />
        <SummaryBar
          left={blueTeam.totalGold}
          right={redTeam.totalGold}
          label="총 골드"
          leftColor="#5383e8"
          rightColor="#d34d63"
        />
      </div>
      <ObjectiveList objectives={redTeam.objectives} tone="red" />
    </div>
  );
}

function ObjectiveList({
  objectives,
  tone,
}: {
  objectives: MatchDetail['teams'][number]['objectives'];
  tone: 'blue' | 'red';
}) {
  const color = tone === 'blue' ? '#5383e8' : '#e84057';
  const items = [
    ['바론', objectives.baron],
    ['드래곤', objectives.dragon],
    ['전령', objectives.riftHerald],
    ['공허 유충', objectives.horde],
    ['타워', objectives.tower],
    ['억제기', objectives.inhibitor],
  ] as const;

  return (
    <ul className="flex flex-wrap gap-x-3 gap-y-1 text-[12px] text-[#c3cad7]">
      {items.map(([label, value]) => (
        <li key={label} style={{ color }}>
          {label} {value}
        </li>
      ))}
    </ul>
  );
}

function SummaryBar({
  left,
  right,
  label,
  leftColor,
  rightColor,
}: {
  left: number;
  right: number;
  label: string;
  leftColor: string;
  rightColor: string;
}) {
  const total = Math.max(1, left + right);
  const percent = (left / total) * 100;

  return (
    <div className="relative w-full">
      <div className="h-4 w-full" style={{ backgroundColor: rightColor }}>
        <div className="h-full" style={{ width: `${percent}%`, backgroundColor: leftColor }} />
      </div>
      <div className="absolute left-2 top-0 text-[12px] leading-4 text-white">{compactNumber(left)}</div>
      <div className="absolute left-1/2 top-0 -translate-x-1/2 text-[12px] leading-4 text-white">{label}</div>
      <div className="absolute right-2 top-0 text-[12px] leading-4 text-white">{compactNumber(right)}</div>
    </div>
  );
}

function InsightBadge({ tag, compact = false }: { tag: MatchInsightTag; compact?: boolean }) {
  const palette = getInsightBadgePalette(tag.tone);
  return (
    <span
      className={`inline-flex items-center rounded-[999px] font-semibold text-white ${
        compact ? 'px-2 py-1 text-[11px]' : 'px-3 py-1 text-[11px]'
      }`}
      style={{
        backgroundColor: palette.background,
        boxShadow: `inset 0 0 0 1px ${palette.border}`,
      }}
    >
      {tag.label}
    </span>
  );
}

function DamageBar({ value, max, color }: { value: number; max: number; color: string }) {
  const width = Math.max(6, Math.round((value / Math.max(1, max)) * 52));
  return (
    <div>
      <div className="text-center">{compactNumber(value)}</div>
      <div className="h-[6px] w-[52px] bg-[#1f2029]">
        <div className="h-full" style={{ width, backgroundColor: color }} />
      </div>
    </div>
  );
}

function DonutChart({ winRate }: { winRate: number }) {
  const degree = Math.max(0, Math.min(360, Math.round((winRate / 100) * 360)));
  return (
    <div
      className="flex h-[136px] w-[136px] items-center justify-center rounded-full"
      style={{ background: `conic-gradient(#5383e8 0deg ${degree}deg, #e84057 ${degree}deg 360deg)` }}
    >
      <div className="flex h-[104px] w-[104px] items-center justify-center rounded-full bg-[#2b2f3a] text-[18px] font-black text-white">
        {winRate}%
      </div>
    </div>
  );
}

function SpellColumn({ ids, small = false }: { ids: number[]; small?: boolean }) {
  const size = small ? 'h-4 w-4' : 'h-[22px] w-[22px]';
  return (
    <div className="flex flex-col gap-[2px]">
      {ids.slice(0, 2).map((id, index) => {
        const icon = getSummonerSpellIconUrl(id);
        return icon ? <img key={`${id}-${index}`} src={icon} alt="" className={`${size} rounded-[4px]`} /> : null;
      })}
    </div>
  );
}

function PerkColumn({ ids, small = false }: { ids: number[]; small?: boolean }) {
  const size = small ? 'h-4 w-4' : 'h-[22px] w-[22px]';
  return (
    <div className="flex flex-col gap-[2px]">
      {ids.slice(0, 2).map((id, index) => {
        const icon = getPerkIcon(id);
        return icon ? <img key={`${id}-${index}`} src={icon} alt="" className={`${size} rounded-full bg-black`} /> : null;
      })}
    </div>
  );
}

function ItemIcons({ items, compact = false }: { items: number[]; compact?: boolean }) {
  const size = compact ? 'h-[18px] w-[18px]' : 'h-[22px] w-[22px]';
  const normalized = [...items.slice(0, 8)];
  while (normalized.length < 8) normalized.push(0);

  return (
    <div className="grid grid-cols-8 gap-[2px]">
      {normalized.map((itemId, index) => {
        const icon = itemId ? getItemIconUrl(itemId) : null;
        return (
          <div
            key={`${itemId}-${index}`}
            className={`${size} ${index >= 6 ? 'rounded-full' : 'rounded-[4px]'} bg-[#49506a]`}
          >
            {icon ? <img src={icon} alt="" className={`${size} ${index >= 6 ? 'rounded-full' : 'rounded-[4px]'}`} /> : null}
          </div>
        );
      })}
    </div>
  );
}

function RosterColumn({
  participants,
}: {
  participants: MatchDetail['teams'][number]['participants'];
}) {
  return (
    <div className="flex flex-col gap-[2px]">
      {participants.slice(0, 5).map((participant) => (
        <div key={`${participant.teamId}-${participant.participantId}`} className="flex items-center gap-1">
          {participant.championName ? <RosterChampionIcon championName={participant.championName} /> : null}
          <span className={`block w-[64px] truncate text-[12px] ${participant.isPlayer ? 'font-bold text-white' : 'text-[#d4dbeb]'}`}>
            {participant.summonerName}
          </span>
        </div>
      ))}
    </div>
  );
}

function buildChampionSummary(matches: RecentMatch[], championDisplayNames: Record<string, string>) {
  const map = new Map<
    string,
    { name: string; games: number; wins: number; kills: number; deaths: number; assists: number; icon: string | null }
  >();

  matches.forEach((match) => {
    const current = map.get(match.championName) ?? {
      name: match.championName,
      games: 0,
      wins: 0,
      kills: 0,
      deaths: 0,
      assists: 0,
      icon: getChampionSquareUrl(match.championName),
    };
    current.games += 1;
    current.wins += match.win ? 1 : 0;
    current.kills += match.kills;
    current.deaths += match.deaths;
    current.assists += match.assists;
    map.set(match.championName, current);
  });

  return [...map.values()]
    .sort((a, b) => b.games - a.games)
    .map((entry) => ({
      ...entry,
      name: championDisplayNames[entry.name] ?? entry.name,
      losses: entry.games - entry.wins,
      playRate: Math.round((entry.games / Math.max(1, matches.length)) * 100),
      kda: computeKda(entry.kills / entry.games, entry.deaths / entry.games, entry.assists / entry.games),
    }));
}

function averageMatchStat(matches: RecentMatch[], pick: (match: RecentMatch) => number) {
  if (matches.length === 0) return 0;
  return matches.reduce((sum, match) => sum + pick(match), 0) / matches.length;
}

function averageKillParticipationRate(matches: RecentMatch[]) {
  const values = matches
    .map((match) => match.killParticipation)
    .filter((value): value is number => typeof value === 'number');

  if (values.length === 0) return 0;
  return Math.round((values.reduce((sum, value) => sum + value, 0) / values.length) * 100);
}

function buildRoleBars(matches: RecentMatch[]) {
  const counts = new Map<string, number>();
  ROLE_ORDER.forEach((role) => counts.set(role, 0));

  matches.forEach((match) => {
    const role = normalizeRole(match.lane, match.role);
    counts.set(role, (counts.get(role) ?? 0) + 1);
  });

  const max = Math.max(1, ...ROLE_ORDER.map((role) => counts.get(role) ?? 0));
  return ROLE_ORDER.map((role) => ({
    label: role,
    height: Math.max(16, Math.round(((counts.get(role) ?? 0) / max) * 100)),
  }));
}

function normalizeRole(lane: string | null, role: string | null) {
  if (lane === 'JUNGLE') return 'JUNGLE';
  if (lane === 'MIDDLE') return 'MID';
  if (lane === 'TOP') return 'TOP';
  if (lane === 'BOTTOM' && role === 'DUO_CARRY') return 'ADC';
  if (lane === 'BOTTOM' || role === 'SUPPORT') return 'SUP';
  return 'JUNGLE';
}

function queueLabel(queueId: number) {
  const map: Record<number, string> = {
    420: '솔로랭크',
    430: '일반',
    440: '자유랭크',
    450: '칼바람 나락',
    700: '클래시',
    900: '우르프',
    1700: '아레나',
  };
  return map[queueId] ?? `큐 ${queueId}`;
}

function formatRelativeDate(value: string) {
  const date = new Date(value);
  const diffMs = Date.now() - date.getTime();
  const diffHours = Math.max(0, Math.floor(diffMs / (1000 * 60 * 60)));
  if (diffHours < 24) return `${diffHours}시간 전`;
  const diffDays = Math.floor(diffHours / 24);
  if (diffDays < 7) return `${diffDays}일 전`;
  return new Intl.DateTimeFormat('ko-KR', { month: 'short', day: 'numeric' }).format(date);
}

function formatDuration(totalSeconds: number) {
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;
  return `${minutes}분 ${`${seconds}`.padStart(2, '0')}초`;
}

function computeKda(kills: number, deaths: number, assists: number) {
  return ((kills + assists) / Math.max(1, deaths)).toFixed(2);
}

function compactNumber(value: number) {
  return new Intl.NumberFormat('ko-KR').format(Math.round(value));
}

function formatPercent(value: number | null) {
  if (typeof value !== 'number') return '-';
  return `${Math.round(value * 100)}%`;
}

function profileTierFromKda(kda: number) {
  if (kda >= 6) return '플래티넘 4';
  if (kda >= 4.5) return '플래티넘 3';
  if (kda >= 3.4) return '골드 1';
  return '실버 2';
}

function buildMatchInsightTags(match: RecentMatch) {
  const minutes = Math.max(1, match.gameDurationSec / 60);
  const csPerMinute = match.totalMinionsKilled / minutes;
  const damagePerMinute = match.totalDamageToChampions / minutes;
  const kp = typeof match.killParticipation === 'number' ? match.killParticipation : null;

  const candidates: Array<MatchInsightTag & { priority: number }> = [];

  if (kp !== null && kp >= 0.55) {
    candidates.push({ key: 'kill-participation', label: '킬 관여 강함', tone: 'red', priority: 100 });
  }
  if (match.deaths >= 7) {
    candidates.push({ key: 'death-management', label: '데스 관리 필요', tone: 'slate', priority: 96 + Math.min(match.deaths - 7, 3) });
  }
  if (match.assists >= 10 || (kp !== null && kp >= 0.48)) {
    candidates.push({ key: 'teamfight-join', label: '교전 합류 빈도 높음', tone: 'gold', priority: 92 });
  }
  if (match.visionScore >= Math.max(18, Math.round(minutes * 1.15))) {
    candidates.push({ key: 'vision', label: '시야 기여 높음', tone: 'green', priority: 88 });
  }
  if (damagePerMinute >= 850 || match.kills >= 9) {
    candidates.push({ key: 'carry-damage', label: '캐리 화력 높음', tone: 'red', priority: 86 });
  }
  if (csPerMinute >= 7.5) {
    candidates.push({ key: 'farm', label: 'CS 수급 안정적', tone: 'blue', priority: 82 });
  }
  if (match.kdaRatio >= 4.5 && match.deaths <= 3) {
    candidates.push({ key: 'stable-kda', label: '안정적인 KDA', tone: 'blue', priority: 80 });
  }

  return candidates
    .sort((left, right) => right.priority - left.priority)
    .slice(0, 3)
    .map(({ priority: _priority, ...tag }) => tag);
}

function getInsightBadgePalette(tone: MatchInsightTag['tone']) {
  switch (tone) {
    case 'red':
      return { background: '#e45868', border: 'rgba(255,255,255,0.08)' };
    case 'gold':
      return { background: '#d2a73b', border: 'rgba(255,255,255,0.08)' };
    case 'green':
      return { background: '#6fad30', border: 'rgba(255,255,255,0.08)' };
    case 'slate':
      return { background: '#8a8ea3', border: 'rgba(255,255,255,0.08)' };
    case 'blue':
    default:
      return { background: '#5383e8', border: 'rgba(255,255,255,0.08)' };
  }
}

function getPerkIcon(id: number) {
  return `https://opgg-static.akamaized.net/meta/images/lol/16.9.1/perk/${id}.png?image=q_auto:good,f_webp,w_56,h_56&v=1609`;
}

function clamp(value: number, min: number, max: number) {
  return Math.min(max, Math.max(min, value));
}

function translateTeamResult(value: MatchDetail['teams'][number]['result']) {
  return value === 'Victory' ? '승리' : '패배';
}

function translateTeamSide(value: MatchDetail['teams'][number]['side']) {
  return value === 'BLUE' ? '블루팀' : '레드팀';
}

function RosterChampionIcon({ championName }: { championName: string }) {
  const icon = getChampionSquareUrl(championName);
  if (!icon) return null;
  return <img src={icon} alt={championName} className="h-4 w-4 rounded-[4px]" />;
}

function TeamChampionIcon({ championName }: { championName: string }) {
  const icon = getChampionSquareUrl(championName);
  if (!icon) return null;
  return <img src={icon} alt={championName} className="h-8 w-8 rounded-full" />;
}
