// Shared visual vocabulary: inline SVG icons (stroke-only, currentColor so
// they inherit the Dawn palette), the initials avatar, and the command-card
// building blocks reused across the login and contacts surfaces.
//
// Visual canon: «Боты» mockup at
// docs/design/new-direct-messages-design/project/stream-v2-dawn.jsx
// (BotsDesktop) — Dawn palette, fleet-violet accent, letter avatars,
// friendly cards, no terminal styling.

import type { ComponentChildren } from 'preact';

export const RefreshIcon = () => (
  <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true">
    <path d="M3.5 8.5a6.5 6.5 0 0 1 11.4-3.2" stroke-linecap="round" />
    <path d="M16.5 11.5a6.5 6.5 0 0 1-11.4 3.2" stroke-linecap="round" />
    <path d="M14.6 3.2v3.5h-3.5" stroke-linecap="round" stroke-linejoin="round" />
    <path d="M5.4 16.8v-3.5h3.5" stroke-linecap="round" stroke-linejoin="round" />
  </svg>
);

export const InfoIcon = () => (
  <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true">
    <circle cx="10" cy="10" r="7.5" />
    <path d="M10 9.2 L10 14" stroke-linecap="round" />
    <circle cx="10" cy="6.4" r="0.7" fill="currentColor" stroke="none" />
  </svg>
);

export const PhoneIcon = () => (
  <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true">
    <rect x="6" y="2.5" width="8" height="15" rx="1.6" />
    <line x1="8.6" y1="14.5" x2="11.4" y2="14.5" stroke-linecap="round" />
  </svg>
);

export const QrIcon = () => (
  <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true">
    <rect x="3" y="3" width="5" height="5" rx="0.6" />
    <rect x="12" y="3" width="5" height="5" rx="0.6" />
    <rect x="3" y="12" width="5" height="5" rx="0.6" />
    <path
      d="M12 12 H13.5 M15.5 12 H17 M12 14.5 H14 M16 14.5 H17 M12 17 H13.5 M15.5 17 H17"
      stroke-linecap="round"
    />
  </svg>
);

export const SearchIcon = () => (
  <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true">
    <circle cx="9" cy="9" r="5.5" />
    <line x1="13.2" y1="13.2" x2="17" y2="17" stroke-linecap="round" />
  </svg>
);

export const BackIcon = () => (
  <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
    <path d="M12.5 4 L6.5 10 L12.5 16" stroke-linecap="round" stroke-linejoin="round" />
  </svg>
);

export const UserIcon = () => (
  <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true">
    <circle cx="10" cy="6.8" r="3.3" />
    <path d="M3.8 17c.9-3 3.3-4.6 6.2-4.6s5.3 1.6 6.2 4.6" stroke-linecap="round" />
  </svg>
);

export const LogoutIcon = () => (
  <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true">
    <path d="M11 3.5 H4.5 V16.5 H11" stroke-linecap="round" stroke-linejoin="round" />
    <line x1="9" y1="10" x2="17" y2="10" stroke-linecap="round" />
    <path d="M14 7 L17 10 L14 13" stroke-linecap="round" stroke-linejoin="round" />
  </svg>
);

// Eye + eye-with-slash for the password reveal toggle. SVG paths copied
// verbatim from folds `Icons.Eye(false)` / `Icons.EyeBlind(false)` — the
// unfilled variants Vojo's main auth uses. Importing folds into the widget
// bundle would pull the whole component library, so we inline the geometry.
export const EyeIcon = () => (
  <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
    <path d="M15 12C15 13.6569 13.6569 15 12 15C10.3431 15 9 13.6569 9 12C9 10.3431 10.3431 9 12 9C13.6569 9 15 10.3431 15 12Z" />
    <path
      fillRule="evenodd"
      clipRule="evenodd"
      d="M1 12C1 12 5.92487 19 12 19C18.0751 19 23 12 23 12C23 12 18.0751 5 12 5C5.92487 5 1 12 1 12ZM2.90443 12C2.93793 12.0401 2.97258 12.0813 3.00836 12.1235C3.53083 12.7395 4.28523 13.5585 5.21221 14.3734C7.11461 16.0459 9.51515 17.5 12 17.5C14.4849 17.5 16.8854 16.0459 18.7878 14.3734C19.7148 13.5585 20.4692 12.7395 20.9916 12.1235C21.0274 12.0813 21.0621 12.0401 21.0956 12C21.0621 11.9599 21.0274 11.9187 20.9916 11.8765C20.4692 11.2605 19.7148 10.4415 18.7878 9.62656C16.8854 7.9541 14.4849 6.5 12 6.5C9.51515 6.5 7.11461 7.9541 5.21221 9.62656C4.28523 10.4415 3.53083 11.2605 3.00836 11.8765C2.97258 11.9187 2.93793 11.9599 2.90443 12Z"
    />
  </svg>
);

export const EyeBlindIcon = () => (
  <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
    <path
      fillRule="evenodd"
      clipRule="evenodd"
      d="M4.75213 3.69141L3.69147 4.75207L6.02989 7.09049C3.00297 9.15318 1 12.0001 1 12.0001C1 12.0001 5.92487 19.0001 12 19.0001C13.663 19.0001 15.2399 18.4756 16.6531 17.7137L19.2478 20.3084L20.3085 19.2478L4.75213 3.69141ZM15.5394 16.6L13.5242 14.5848C13.0775 14.8488 12.5565 15.0003 12 15.0003C10.3431 15.0003 9 13.6572 9 12.0003C9 11.4439 9.1515 10.9228 9.4155 10.4761L7.11135 8.17195C6.4387 8.61141 5.80156 9.10856 5.21221 9.62667C4.28523 10.4416 3.53083 11.2607 3.00836 11.8766C2.97258 11.9188 2.93793 11.96 2.90443 12.0001C2.93793 12.0402 2.97258 12.0814 3.00836 12.1236C3.53083 12.7396 4.28523 13.5586 5.21221 14.3736C7.11461 16.046 9.51515 17.5001 12 17.5001C13.2162 17.5001 14.4122 17.1518 15.5394 16.6ZM18.5058 14.6167C18.6009 14.5363 18.6949 14.4552 18.7878 14.3736C19.7148 13.5586 20.4692 12.7396 20.9916 12.1236C21.0274 12.0814 21.0621 12.0402 21.0956 12.0001C21.0621 11.96 21.0274 11.9188 20.9916 11.8766C20.4692 11.2607 19.7148 10.4416 18.7878 9.62667C16.8854 7.95422 14.4849 6.50011 12 6.50011C11.5118 6.50011 11.0268 6.55625 10.5482 6.65915L9.32458 5.43554C10.181 5.16161 11.0772 5.00011 12 5.00011C18.0751 5.00011 23 12.0001 23 12.0001C23 12.0001 21.6825 13.8727 19.5699 15.6808L18.5058 14.6167Z"
    />
  </svg>
);

// --- Initials avatar --------------------------------------------------------

// Dawn accent rotation (same hues the design mock assigns to bot list
// entries). Hash by codepoints so a contact keeps its color across loads.
const AVATAR_COLORS = ['#9580ff', '#7ab6d9', '#d4b88a', '#7dd3a8', '#c08e7b', '#a59cff'];

const hashString = (s: string): number => {
  let h = 0;
  for (let i = 0; i < s.length; i += 1) {
    h = (h * 31 + s.charCodeAt(i)) | 0; // eslint-disable-line no-bitwise
  }
  return Math.abs(h);
};

export const avatarColor = (key: string): string =>
  AVATAR_COLORS[hashString(key) % AVATAR_COLORS.length];

export const initialsOf = (name: string | undefined, fallback = '?'): string => {
  const trimmed = (name ?? '').trim();
  if (!trimmed) return fallback;
  const parts = trimmed.split(/\s+/).filter(Boolean);
  if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
  return trimmed.slice(0, 1).toUpperCase();
};

type AvatarProps = { name?: string; colorKey: string; size?: number; src?: string | null };

// Initials always render underneath; the photo (when the MSC4039 download
// resolved) layers on top, so a slow or failed download degrades to the
// colored-letter look instead of an empty box.
export const Avatar = ({ name, colorKey, size = 38, src }: AvatarProps) => (
  <span
    class="avatar"
    aria-hidden="true"
    style={{
      width: `${size}px`,
      height: `${size}px`,
      fontSize: `${Math.round(size * 0.42)}px`,
      background: avatarColor(colorKey),
    }}
  >
    {initialsOf(name)}
    {src ? <img class="avatar-img" src={src} alt="" loading="lazy" /> : null}
  </span>
);

// --- Command card -----------------------------------------------------------

type CommandCardProps = {
  icon: ComponentChildren;
  name: string;
  desc: string;
  onClick: () => void;
  danger?: boolean;
  disabled?: boolean;
  spinning?: boolean;
};

export const CommandCard = ({
  icon,
  name,
  desc,
  onClick,
  danger,
  disabled,
  spinning,
}: CommandCardProps) => (
  <button
    class={`command-card${danger ? ' danger' : ''}${spinning ? ' refreshing' : ''}`}
    type="button"
    onClick={onClick}
    disabled={disabled}
  >
    <span class="command-card-lead-icon" aria-hidden="true">
      {icon}
    </span>
    <div class="command-card-body">
      <div class="command-card-name">{name}</div>
      <div class="command-card-desc">{desc}</div>
    </div>
    <span class="command-card-chevron" aria-hidden="true">
      ›
    </span>
  </button>
);

// --- Status / notice --------------------------------------------------------

type StatusPillProps = {
  tone: 'connected' | 'disconnected' | 'checking';
  children: ComponentChildren;
};

export const StatusPill = ({ tone, children }: StatusPillProps) => (
  <span class={`section-status ${tone}`} role="status">
    <span class="dot" />
    {children}
  </span>
);

type NoticeProps = {
  tone: 'error' | 'warn' | 'info';
  children: ComponentChildren;
  onDismiss?: () => void;
};

// Inline notice strip — the replacement for the old transcript pane. One
// line of feedback right where the action happened, dismissable, no log.
export const Notice = ({ tone, children, onDismiss }: NoticeProps) => (
  <div class={`notice ${tone}`} role={tone === 'error' ? 'alert' : 'status'}>
    <span class="notice-body">{children}</span>
    {onDismiss ? (
      <button type="button" class="notice-dismiss" onClick={onDismiss} aria-label="×">
        ×
      </button>
    ) : null}
  </div>
);

export const Spinner = () => <span class="spinner" aria-hidden="true" />;
