import '@testing-library/jest-dom'
import { beforeEach, vi } from 'vitest'

if (typeof window !== 'undefined') {
  // Synchronous rAF mock — fires the callback immediately so that
  // RAF-throttled store updates behave synchronously in tests.
  let rafId = 0
  window.requestAnimationFrame = (cb: FrameRequestCallback): number => {
    cb(performance.now())
    return ++rafId
  }
  window.cancelAnimationFrame = vi.fn()

  // Mock matchMedia for theme detection
  Object.defineProperty(window, 'matchMedia', {
    writable: true,
    value: vi.fn().mockImplementation((query: string) => ({
      matches: query === '(prefers-color-scheme: dark)',
      media: query,
      onchange: null,
      addListener: vi.fn(),
      removeListener: vi.fn(),
      addEventListener: vi.fn(),
      removeEventListener: vi.fn(),
      dispatchEvent: vi.fn()
    }))
  })

  if (!document.fonts) {
    Object.defineProperty(document, 'fonts', {
      writable: true,
      configurable: true,
      value: {
        load: vi.fn().mockResolvedValue([])
      }
    })
  }

  if (!HTMLCanvasElement.prototype.getContext) {
    Object.defineProperty(HTMLCanvasElement.prototype, 'getContext', {
      writable: true,
      configurable: true,
      value: vi.fn(() => ({
        fillRect: vi.fn(),
        clearRect: vi.fn(),
        getImageData: vi.fn(),
        putImageData: vi.fn(),
        createImageData: vi.fn(),
        setTransform: vi.fn(),
        drawImage: vi.fn(),
        save: vi.fn(),
        fillText: vi.fn(),
        restore: vi.fn(),
        beginPath: vi.fn(),
        moveTo: vi.fn(),
        lineTo: vi.fn(),
        closePath: vi.fn(),
        stroke: vi.fn(),
        translate: vi.fn(),
        scale: vi.fn(),
        rotate: vi.fn(),
        arc: vi.fn(),
        fill: vi.fn(),
        measureText: vi.fn(() => ({ width: 0 })),
        transform: vi.fn(),
        rect: vi.fn(),
        clip: vi.fn()
      }))
    })
  }

  if (typeof window.localStorage?.getItem !== 'function') {
    const storage = new Map<string, string>()
    Object.defineProperty(window, 'localStorage', {
      writable: true,
      configurable: true,
      value: {
        getItem: vi.fn((key: string) => storage.get(key) ?? null),
        setItem: vi.fn((key: string, value: string) => {
          storage.set(key, value)
        }),
        removeItem: vi.fn((key: string) => {
          storage.delete(key)
        }),
        clear: vi.fn(() => {
          storage.clear()
        })
      }
    })
  }

  beforeEach(() => {
    window.localStorage?.clear?.()
  })

  // Mock gitOps for components that use GitStatusPanel
  const mockGitOps = {
    getFileStatuses: vi.fn().mockResolvedValue({ success: true, files: [] }),
    getBranchInfo: vi.fn().mockResolvedValue({
      success: true,
      branch: { name: 'main', tracking: null, ahead: 0, behind: 0 }
    }),
    stageFile: vi.fn().mockResolvedValue({ success: true }),
    unstageFile: vi.fn().mockResolvedValue({ success: true }),
    stageAll: vi.fn().mockResolvedValue({ success: true }),
    unstageAll: vi.fn().mockResolvedValue({ success: true }),
    discardChanges: vi.fn().mockResolvedValue({ success: true }),
    addToGitignore: vi.fn().mockResolvedValue({ success: true }),
    commit: vi.fn().mockResolvedValue({ success: true, commitHash: 'abc1234' }),
    push: vi.fn().mockResolvedValue({ success: true }),
    pull: vi.fn().mockResolvedValue({ success: true }),
    openInEditor: vi.fn().mockResolvedValue({ success: true }),
    showInFinder: vi.fn().mockResolvedValue({ success: true }),
    onStatusChanged: vi.fn().mockReturnValue(() => {}),
    watchBranch: vi.fn().mockResolvedValue({ success: true }),
    unwatchBranch: vi.fn().mockResolvedValue({ success: true }),
    onBranchChanged: vi.fn().mockReturnValue(() => {}),
    getFileContent: vi.fn().mockResolvedValue({ success: true, content: '' }),
    getRemoteUrl: vi.fn().mockResolvedValue({ success: true, url: null, remote: null })
  }

  // Mock fileTreeOps
  const mockFileTreeOps = {
    scan: vi.fn().mockResolvedValue({ success: true, tree: [] }),
    loadChildren: vi.fn().mockResolvedValue({ success: true, children: [] }),
    watch: vi.fn().mockResolvedValue({ success: true }),
    unwatch: vi.fn().mockResolvedValue({ success: true }),
    onChange: vi.fn().mockReturnValue(() => {})
  }

  // Set up window mocks if they don't exist
  if (!window.gitOps) {
    Object.defineProperty(window, 'gitOps', {
      writable: true,
      configurable: true,
      value: mockGitOps
    })
  }

  if (!window.fileTreeOps) {
    Object.defineProperty(window, 'fileTreeOps', {
      writable: true,
      value: mockFileTreeOps
    })
  }

  // Mock systemOps for components that use system operations
  if (!window.systemOps) {
    Object.defineProperty(window, 'systemOps', {
      writable: true,
      configurable: true,
      value: {
        getLogDir: vi.fn().mockResolvedValue('/tmp/logs'),
        getAppVersion: vi.fn().mockResolvedValue('1.0.0'),
        getAppPaths: vi.fn().mockResolvedValue({ userData: '/tmp', home: '/tmp', logs: '/tmp' }),
        isLogMode: vi.fn().mockResolvedValue(false),
        detectAgentSdks: vi.fn().mockResolvedValue({ opencode: true, claude: true, codex: false }),
        detectAgentRuntimes: vi.fn().mockResolvedValue({
          opencode: true,
          claude: true,
          codex: false
        }),
        setSessionQueuedState: vi.fn().mockResolvedValue({ success: true }),
        checkFullDiskAccess: vi.fn().mockResolvedValue({ supported: true, granted: false }),
        openFullDiskAccessSettings: vi.fn().mockResolvedValue({ success: true }),
        setKeepAwakeEnabled: vi.fn().mockResolvedValue({ success: true }),
        runOnboardingDoctor: vi.fn().mockResolvedValue({
          platform: 'darwin',
          environmentChecks: [],
          agents: [],
          recommendedAgent: 'terminal'
        }),
        quitApp: vi.fn().mockResolvedValue(undefined),
        openInApp: vi.fn().mockResolvedValue({ success: true }),
        openInChrome: vi.fn().mockResolvedValue({ success: true }),
        onNewSessionShortcut: vi.fn().mockReturnValue(() => {}),
        onCloseSessionShortcut: vi.fn().mockReturnValue(() => {}),
        onFileSearchShortcut: vi.fn().mockReturnValue(() => {}),
        onNotificationNavigate: vi.fn().mockReturnValue(() => {}),
        onWindowFocused: vi.fn().mockReturnValue(() => {}),
        updateMenuState: vi.fn().mockResolvedValue(undefined),
        isPackaged: vi.fn().mockResolvedValue(false),
        getPlatform: vi.fn().mockResolvedValue('darwin')
      }
    })
  }

  if (!window.analyticsOps) {
    Object.defineProperty(window, 'analyticsOps', {
      writable: true,
      configurable: true,
      value: {
        track: vi.fn().mockResolvedValue(undefined),
        setEnabled: vi.fn().mockResolvedValue(undefined),
        isEnabled: vi.fn().mockResolvedValue(true)
      }
    })
  }
}
