// «Dawn-evolved» hero used at the top of the user-profile card.
// Visual language is borrowed from the design bundle in
// `docs/design/new-direct-messages-design`: centred large gradient
// avatar with an inset green online dot, display name in display
// weight, monospaced handle in muted, presence row with coloured
// status dot + last-seen line. The optional e2ee chip mirrors the
// chat header's lock+«e2ee» token so the surfaces feel like one
// system rather than two design dialects.

import React from 'react';
import { Box, Icon, Icons, Text } from 'folds';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import * as css from './styles.css';
import { UserAvatar } from '../user-avatar';
import { getMxIdLocalPart } from '../../utils/matrix';
import { BreakWord, LineClamp3 } from '../../styles/Text.css';
import { Presence, UserPresence } from '../../hooks/useUserPresence';
import {
  timeDayMonYear,
  timeHourMinute,
  today as isToday,
  yesterday as isYesterday,
} from '../../utils/time';
import colorMXID from '../../../util/colorMXID';

// Shade a `#rrggbb` hex by clamping each channel by `amt`. Borrowed
// from the design bundle's `shared.jsx::shade` helper. We use it to
// build the avatar's 135° gradient bottom-stop without needing the
// CSS `color-mix()` function (which isn't widely enough supported
// on old Android WebView builds — see fallbackBg comment below).
const shadeHex = (hex: string, amt: number): string => {
  const cleaned = hex.replace('#', '');
  if (cleaned.length !== 6) return hex;
  const r = parseInt(cleaned.slice(0, 2), 16);
  const g = parseInt(cleaned.slice(2, 4), 16);
  const b = parseInt(cleaned.slice(4, 6), 16);
  if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return hex;
  const clamp = (v: number) => Math.max(0, Math.min(255, v + amt));
  const toByte = (v: number) => clamp(v).toString(16).padStart(2, '0');
  return `#${toByte(r)}${toByte(g)}${toByte(b)}`;
};

// Bucketed last-seen formatter. Each bucket has its own i18n key so
// translators can phrase «just now», «N minutes ago», «yesterday at
// 14:32» and an absolute date independently — i18next's plural
// suffixes (`_one` / `_few` / `_many` / `_other`) handle Russian
// number agreement automatically. We deliberately do not use
// `Intl.RelativeTimeFormat` here because it follows the *browser*
// locale, not the i18next-selected language; mixing those gives us
// English «5 minutes ago» under a Russian UI.
const formatLastSeen = (ts: number, t: ReturnType<typeof useTranslation>['t']): string => {
  const now = Date.now();
  const diffMs = Math.max(0, now - ts);
  const mins = Math.floor(diffMs / 60_000);
  if (mins < 1) return t('User.last_seen_just_now');
  if (mins < 60) return t('User.last_seen_minutes', { count: mins });
  const hours = Math.floor(diffMs / 3_600_000);
  if (hours < 24 && isToday(ts)) {
    return t('User.last_seen_hours', { count: hours });
  }
  if (isYesterday(ts)) {
    return t('User.last_seen_yesterday', { time: timeHourMinute(ts) });
  }
  return t('User.last_seen_date', { date: timeDayMonYear(ts) });
};

type UserHeroProps = {
  userId: string;
  displayName?: string;
  // Small (96px) cropped thumbnail for the collapsed hero — fast to fetch.
  avatarUrl?: string;
  // Full-resolution avatar used ONLY in the expanded (~340px) state below, so
  // the blown-up tile stays sharp instead of upscaling the 96px thumbnail.
  // Mirrors RoomViewProfilePanel's full-res mobile fullview. Falls back to
  // `avatarUrl` when absent.
  avatarUrlExpanded?: string;
  presence?: UserPresence;
  encrypted?: boolean;
  onAvatarClick?: () => void;
  // Desktop side-pane only. When true the avatar stretches to fill
  // the card width as a square; the rest of the hero (name / presence
  // / e2ee chip) stays in flow below. The online dot is suppressed in
  // this mode — it makes no sense as a tiny corner glyph on a
  // ~340px avatar tile.
  avatarExpanded?: boolean;
};

export function UserHero({
  userId,
  displayName,
  avatarUrl,
  avatarUrlExpanded,
  presence,
  encrypted,
  onAvatarClick,
  avatarExpanded,
}: UserHeroProps) {
  const { t } = useTranslation();
  const username = getMxIdLocalPart(userId);

  const online = presence?.presence === Presence.Online;

  // Offline / idle states get the formatted last-seen line (with the
  // grey dot prefix as a quiet «last activity» cue). Online state is
  // rendered separately as a coloured «онлайн» tag matching the chat
  // header — no dot prefix because the avatar already carries the
  // green online indicator, and a duplicated dot reads as visual noise.
  let presenceLabel: string | undefined;
  if (!online) {
    if (presence?.lastActiveTs && presence.lastActiveTs > 0) {
      // Both Unavailable and Offline get last-seen detail when we
      // have a real timestamp to anchor it. The SDK sometimes
      // reports `lastActiveTs === 0` for users we've never seen —
      // those still fall through to the plain «Idle / Offline»
      // labels so we don't claim «last seen 1970».
      presenceLabel = formatLastSeen(presence.lastActiveTs, t);
    } else if (presence?.presence === Presence.Unavailable) {
      presenceLabel = t('User.presence_unavailable');
    } else if (presence) {
      presenceLabel = t('User.presence_offline');
    }
  }

  const initial = (username?.[0] ?? userId.replace(/^@/, '')[0] ?? '?').toUpperCase();
  // The Dawn mockups use a 135° gradient on the avatar fallback —
  // base colour to a slightly darker shade of itself. We compute
  // the darker stop in JS rather than via CSS `color-mix(in srgb,
  // …)` because the latter only ships in Chrome 111+ / Safari 16.2+
  // / Firefox 113+, and Capacitor's bundled WebView on stale
  // Android devices can be older than that — a missing `color-mix`
  // would parse the gradient as `none` and the fallback would
  // render flat black.
  const fallbackColor = colorMXID(userId);
  const fallbackBg = `linear-gradient(135deg, ${fallbackColor}, ${shadeHex(fallbackColor, -22)})`;

  const avatarNode = (
    <span
      className={classNames(css.HeroAvatar, avatarExpanded && css.HeroAvatarExpanded)}
      aria-hidden={onAvatarClick ? undefined : true}
    >
      <UserAvatar
        userId={userId}
        src={avatarExpanded ? avatarUrlExpanded ?? avatarUrl : avatarUrl}
        alt={displayName ?? userId}
        renderFallback={() => (
          <span className={css.HeroAvatarFallback} style={{ backgroundImage: fallbackBg }}>
            {initial}
          </span>
        )}
      />
      {online && !avatarExpanded && <span className={css.HeroOnlineDot} aria-hidden />}
    </span>
  );

  return (
    <Box direction="Column" alignItems="Center" gap="200" className={css.HeroRoot}>
      {onAvatarClick ? (
        <button
          type="button"
          onClick={onAvatarClick}
          className={classNames(
            css.HeroAvatarButton,
            avatarExpanded && css.HeroAvatarButtonExpanded
          )}
          aria-label={t(avatarExpanded ? 'Room.collapse_avatar' : 'Room.expand_avatar', {
            defaultValue: avatarExpanded ? 'Close avatar' : 'Open avatar',
          })}
        >
          {avatarNode}
        </button>
      ) : (
        avatarNode
      )}

      <Box direction="Column" alignItems="Center" gap="100" className={css.HeroIdentity}>
        <Text
          size="H3"
          align="Center"
          className={classNames(BreakWord, LineClamp3)}
          title={displayName ?? username ?? userId}
        >
          {displayName ?? username ?? userId}
        </Text>
      </Box>

      {(online || presenceLabel || encrypted) && (
        <Box alignItems="Center" gap="200" className={css.HeroPresence}>
          {online ? (
            <Text as="span" size="T200" className={css.HeroOnlineTag}>
              {t('Room.status_online')}
            </Text>
          ) : (
            presenceLabel && (
              <>
                <span className={css.PresenceDot} aria-hidden />
                <Text as="span" size="T200" priority="300">
                  {presenceLabel}
                </Text>
              </>
            )
          )}
          {encrypted && (
            <>
              {(online || presenceLabel) && (
                <span className={css.HeroBullet} aria-hidden>
                  ·
                </span>
              )}
              <span className={css.HeroE2ee}>
                <Icon size="50" src={Icons.Lock} filled />
                <Text as="span" size="T200">
                  {t('Room.encrypted_short')}
                </Text>
              </span>
            </>
          )}
        </Box>
      )}
    </Box>
  );
}
