import React, { ReactNode, useImperativeHandle, useRef } from 'react';
import classNames from 'classnames';
import { as } from 'folds';
import * as css from './layout.css';
import { useStreamLayoutDebug } from './streamDebug';
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';

// Stream rows use a fixed `S400` gap so the rail-bridge offsets in
// layout.css.ts (StreamRailBridgeY = S400) always match the gap between rows.
// All three Stream call sites — RoomTimeline.tsx StreamDayDivider wrapper plus
// Message.tsx Message / Event MessageBase — share this single constant.
export const STREAM_MESSAGE_SPACING = '400' as const;

// Stream layout — DM redesign (docs/plans/dm_1x1_redesign.md §6.5b).
//
// Visual structure:
//   ┌──┬─────────────────────────────────────────────────┐
//   │  │  bubble (asymmetric radius)                     │
//   │● │  ┌──────────────────────────────────────────┐   │
//   │  │  │ time · sender · e2ee chip (header line)  │   │
//   │  │  │ message body                             │   │
//   │  │  └──────────────────────────────────────────┘   │
//   └──┴─────────────────────────────────────────────────┘
//    rail+dot  bubble (time absolutely-positioned to the
//                      LEFT of the bubble, on the rail-time column)
//
// Read-state lives entirely on the dot now (own = Primary violet, opacity
// 0.3 → 1.0 when peer reads; incoming = author hash, opacity 1.0 → 0.3 once
// I read it). The legacy WhatsApp checkmark `<MessageStatus>` is intentionally
// not rendered in Stream — the dot already encodes that signal.
//
// Geometry constants live in layout.css.ts (StreamRailWidth, StreamDotSize).
// `time` and `header` are caller-controlled ReactNodes so Message.tsx keeps
// ownership of the timestamp formatting / sender Username component / e2ee
// indicators it already builds.

export type StreamLayoutProps = {
  time?: ReactNode;
  dotColor: string;
  dotOpacity: number;
  isOwn?: boolean;
  compact?: boolean;
  header?: ReactNode;
  railStart?: boolean;
  railEnd?: boolean;
};

// Stream day divider — used by RoomTimeline.tsx in place of the legacy
// TimelineDivider for DM rooms. Sits as a regular row in the timeline so the
// rail flows through it: rail-column has the date label (small mono uppercase),
// a slightly larger Fleet-soft dot anchors on the rail, and the rest of the
// row is a faint hairline. Matches stream-v2-dawn.jsx::DawnPhoneV3 line 73-78.
export type StreamDayDividerProps = {
  label: ReactNode;
};

export const StreamDayDivider = as<'div', StreamDayDividerProps>(
  ({ className, label, ...props }, ref) => {
    // Reads screen size internally instead of taking a prop so RoomTimeline
    // doesn't have to thread it through. The day-divider must use the SAME
    // `compact` value as message rows above and below or the rail and label
    // would mis-align horizontally on desktop.
    const compact = useScreenSizeContext() === ScreenSize.Mobile;
    return (
      <div
        className={classNames(css.StreamDayRoot({ compact }), className)}
        {...props}
        ref={ref}
      >
        {/* Rail segment under the row — same recipe as message rows so the
            rail flows continuously through the day boundary. */}
        <span className={css.StreamRail} aria-hidden />
        <div className={css.StreamDayLabel}>{label}</div>
        <span className={css.StreamDayDot} aria-hidden />
        <div className={css.StreamDayLine} aria-hidden />
      </div>
    );
  }
);

export const StreamLayout = as<'div', StreamLayoutProps>(
  (
    {
      className,
      time,
      dotColor,
      dotOpacity,
      isOwn,
      compact,
      header,
      railStart,
      railEnd,
      children,
      ...props
    },
    ref
  ) => {
    const rootRef = useRef<HTMLDivElement>(null);
    const timeRef = useRef<HTMLSpanElement>(null);
    const railRef = useRef<HTMLSpanElement>(null);
    const dotRef = useRef<HTMLSpanElement>(null);
    const bubbleRef = useRef<HTMLDivElement>(null);
    const headerRef = useRef<HTMLDivElement>(null);

    useImperativeHandle(ref, () => rootRef.current as HTMLDivElement);

    // Debug helper is dev-only and behind a localStorage opt-in (see
    // streamDebug.ts). After P3c every timeline row goes through StreamLayout,
    // so `active` is unconditionally true here.
    useStreamLayoutDebug(
      'message',
      {
        root: rootRef,
        timeColumn: timeRef,
        rail: railRef,
        dot: dotRef,
        content: bubbleRef,
        header: headerRef,
      },
      true
    );

    return (
      <div
        className={classNames(css.StreamRoot({ compact: !!compact }), className)}
        {...props}
        ref={rootRef}
      >
        <span
          className={classNames(
            css.StreamRail,
            railStart && railEnd && css.StreamRailSingle,
            railStart && !railEnd && css.StreamRailStart,
            railEnd && !railStart && css.StreamRailEnd
          )}
          aria-hidden
          ref={railRef}
        />
        <div className={css.StreamBubble({ own: !!isOwn, compact: !!compact })} ref={bubbleRef}>
          <span className={css.StreamHeaderTime} ref={timeRef}>
            {time}
          </span>
          <span
            className={classNames(css.StreamDotHalo, css.StreamHeaderDotHalo)}
            aria-hidden
            ref={dotRef}
          >
            <span
              className={css.StreamDotFill}
              style={{ backgroundColor: dotColor, opacity: dotOpacity }}
            />
          </span>
          {header && (
            <div className={css.StreamBubbleHeader} ref={headerRef}>
              {header}
            </div>
          )}
          {children}
        </div>
      </div>
    );
  }
);
