// @vitest-environment jsdom
//
// The title bar: borderless actions that go accent when on, and the fullscreen
// shift (macOS hides the traffic lights, so the project name moves to the edge).
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
import { act, cleanup, render, waitFor } from '@testing-library/react'
import React from 'react'

vi.mock('../src/renderer/src/terminals', () => {
  let n = 0
  return { Terminal: () => null, lid: () => ++n }
})

import { App } from '../src/renderer/src/App'
import { ProjectProvider } from '../src/renderer/src/project'

/** Fires the fullscreen callbacks the App subscribed to. */
let fullscreenCbs: ((on: boolean) => void)[] = []

function stubBridge(): void {
  const noop = (): void => {}
  const off = (): (() => void) => noop
  ;(window as unknown as { helder: unknown }).helder = {
    platform: 'darwin',
    clipboard: { writeText: noop, readText: () => '' },
    project: {
      current: async () => ({ root: '/repo', name: 'repo' }),
      open: async () => ({ root: '/repo', name: 'repo' }),
      openPath: async () => ({ root: '/repo', name: 'repo' }),
      recent: async () => [],
    },
    fs: {
      tree: async () => ({ name: 'repo', type: 'dir', path: '', children: [] }),
      readDir: async () => [], files: async () => ({}), read: async () => '',
      imageDataUrl: async () => '', write: async () => {}, delete: async () => {},
      create: async () => {}, mkdir: async () => {},
    },
    shell: { reveal: noop },
    notes: { read: async () => '', write: async () => {} },
    git: {
      load: async () => ({ branch: 'main', changes: [] }),
      stage: async () => {}, unstage: async () => {}, commit: async () => {},
      push: async () => ({ ok: true, message: '' }), discard: async () => {},
    },
    pty: { available: async () => false, create: async () => 1, write: noop, resize: noop, kill: noop, onData: off, onExit: off },
    config: { get: async () => (await import('../src/renderer/src/types')).DEFAULT_CONFIG, theme: async () => '' },
    recent: { get: async () => [], set: async () => {} },
    search: { content: async () => [], files: async () => [] },
    dialog: { unsavedClose: async () => 'cancel' },
    log: { write: noop, path: async () => null, open: async () => {}, reveal: async () => {} },
    onFullscreen: (cb: (on: boolean) => void) => {
      fullscreenCbs.push(cb)
      return () => { fullscreenCbs = fullscreenCbs.filter((f) => f !== cb) }
    },
    onProjectChanged: off, onConfigChanged: off, onRefresh: off,
  }
}

beforeAll(() => {
  globalThis.ResizeObserver = class { observe(): void {} unobserve(): void {} disconnect(): void {} } as never
  if (!Element.prototype.scrollIntoView) Element.prototype.scrollIntoView = (): void => {}
})
beforeEach(() => { fullscreenCbs = []; stubBridge() })
afterEach(() => {
  cleanup()
  localStorage.clear()
  delete (window as unknown as { helder?: unknown }).helder
})

async function boot(): Promise<HTMLElement> {
  const c = render(<ProjectProvider><App /></ProjectProvider>).container
  await waitFor(() => { if (!c.querySelector('.git-foot')) throw new Error('not ready') })
  return c
}

function setFullscreen(on: boolean): void {
  act(() => { fullscreenCbs.forEach((cb) => cb(on)) })
}

describe('title bar', () => {
  it('shifts left in fullscreen and back out again', async () => {
    const c = await boot()
    const bar = c.querySelector('.titlebar') as HTMLElement
    expect(bar.className).not.toContain('fullscreen')

    await waitFor(() => expect(fullscreenCbs.length).toBe(1))
    setFullscreen(true)
    expect(bar.className).toContain('fullscreen')

    setFullscreen(false)
    expect(bar.className).not.toContain('fullscreen')
  })

  it('shows the state with the accent, not an On/Off badge', async () => {
    const c = await boot()
    const hidden = [...c.querySelectorAll<HTMLButtonElement>('.titlebar .tb-btn')]
      .find((b) => b.textContent?.includes('Hidden')) as HTMLButtonElement
    expect(c.querySelector('.titlebar .tb-state')).toBeNull()
    expect(hidden.className).not.toContain('on')

    act(() => { hidden.click() })
    await waitFor(() => expect(hidden.className).toContain('on'))
    expect(hidden.textContent).not.toContain('On')
  })
})
