// Per-row stripe + bounded indent — pure-function lockdown. Threading
// visualization is `f(depth)` alone; no parent-child coordination, no
// central walker, no collapse. The helper at
// `src/components/comments/threadStyle.ts` returns either an empty object
// (no stripe, no extra padding) for ancestors / direct-replies (depth 0)
// or `{ borderLeftWidth: 3, borderLeftColor: <theme-token>, paddingLeft }`
// for descendants. Stripe colour cycles modulo `THREAD_PALETTE.length`
// (4 tokens, matches both light + dark blocks of `tamagui.config.ts`),
// indent caps at `INDENT_CAP=4` levels (paddings ceiling, NOT a structural
// collapse — colour keeps cycling past the cap so depth stays legible
// without burning more horizontal space).

import {
  INDENT_CAP,
  INDENT_STEP,
  THREAD_PALETTE,
  threadRowStyle,
} from 'src/components/comments/threadStyle'

describe('threadRowStyle', () => {
  it('returns an empty object at depth 0 (ancestors / direct-replies)', () => {
    // Ancestors render at depth 0 — they're chronological context above
    // the anchor, not part of the thread. Direct replies to the anchor
    // also start at depth 0 to keep the stripe palette anchored at depth 1.
    expect(threadRowStyle(0)).toEqual({})
  })

  it('returns an empty object for negative depths (defensive)', () => {
    // `flattenWithDepth` only emits non-negative depths, but the helper
    // is exported and reachable from future callers — keep the contract
    // total to make the failure mode obvious.
    expect(threadRowStyle(-1)).toEqual({})
  })

  it('applies the first palette token + base indent at depth 1', () => {
    // First level of nesting: 15 (base) + 1 * 10 (step) = 25.
    expect(threadRowStyle(1)).toEqual({
      borderLeftWidth: 3,
      borderLeftColor: THREAD_PALETTE[0],
      paddingLeft: 25,
    })
  })

  it('applies the fourth palette token + max indent at the cap', () => {
    // Indent cap (4) reached: 15 + 4 * 10 = 55. Palette is 0-indexed, so
    // (depth-1) % 4 = 3 lands on the fourth token.
    expect(threadRowStyle(INDENT_CAP)).toEqual({
      borderLeftWidth: 3,
      borderLeftColor: THREAD_PALETTE[3],
      paddingLeft: 55,
    })
  })

  it('wraps the palette modulo length and caps the indent past the cap', () => {
    // Past the cap: indent stays at 55 (Math.min with INDENT_CAP), but the
    // palette keeps cycling so the user can still distinguish nesting at
    // depth 5 from depth 4 even though they share horizontal position.
    expect(threadRowStyle(INDENT_CAP + 1)).toEqual({
      borderLeftWidth: 3,
      borderLeftColor: THREAD_PALETTE[0], // wrap: (5-1) % 4 = 0
      paddingLeft: 15 + INDENT_CAP * INDENT_STEP,
    })
  })

  it('continues to cycle palette colours at extreme depth', () => {
    // Server's authed `/context` returns up to 4096 rows depth-unbounded
    // (`ThreadLimits::contextLimit()`); the modulo handles arbitrary depth
    // gracefully without throwing or returning a sparse style.
    const deep = threadRowStyle(100)
    expect(deep).toEqual({
      borderLeftWidth: 3,
      borderLeftColor: THREAD_PALETTE[(100 - 1) % THREAD_PALETTE.length],
      paddingLeft: 15 + INDENT_CAP * INDENT_STEP,
    })
    // (100-1) % 4 = 3 → THREAD_PALETTE[3]; sanity-check the math doesn't
    // silently roll into negative indices on a future refactor.
    expect(deep.borderLeftColor).toBe(THREAD_PALETTE[3])
  })

  it('exposes a 4-element palette of theme tokens', () => {
    // Tokens are declared on both light + dark blocks of tamagui.config.ts;
    // `borderLeftColor` accepts theme tokens directly (no JS-side colour
    // resolution). The shape lock guards against a future refactor that
    // might forget to widen INDENT_CAP / palette in lockstep — the modulo
    // depends on `length === 4`.
    expect(THREAD_PALETTE).toHaveLength(4)
    for (const token of THREAD_PALETTE) {
      expect(token.startsWith('$threadAccent')).toBe(true)
    }
  })
})
