import { beforeEach, vi } from 'vitest'

// G6: vitest 4 + jsdom 29 mist localStorage/sessionStorage op globalThis.
// MemoryStorage-binding zodat tests zonder echte browser draaien.
class MemoryStorage implements Storage {
  private store = new Map<string, string>()

  get length(): number {
    return this.store.size
  }

  clear(): void {
    this.store.clear()
  }

  getItem(key: string): string | null {
    return this.store.has(key) ? this.store.get(key)! : null
  }

  key(index: number): string | null {
    return Array.from(this.store.keys())[index] ?? null
  }

  removeItem(key: string): void {
    this.store.delete(key)
  }

  setItem(key: string, value: string): void {
    this.store.set(key, String(value))
  }
}

const localStorageMemory = new MemoryStorage()
const sessionStorageMemory = new MemoryStorage()

Object.defineProperty(globalThis, 'localStorage', {
  value: localStorageMemory,
  configurable: true,
  writable: true,
})
Object.defineProperty(globalThis, 'sessionStorage', {
  value: sessionStorageMemory,
  configurable: true,
  writable: true,
})

if (typeof window !== 'undefined') {
  Object.defineProperty(window, 'localStorage', {
    value: localStorageMemory,
    configurable: true,
    writable: true,
  })
  Object.defineProperty(window, 'sessionStorage', {
    value: sessionStorageMemory,
    configurable: true,
    writable: true,
  })
}

// G7: maak globalThis.fetch herconfigureerbaar zodat vi.spyOn / vi.fn-stubs
// de eigenschap kunnen redefineren. Default Node 24 / jsdom 29 binding is
// vaak non-configurable, wat vi.spyOn(globalThis, 'fetch') laat falen.
const originalFetch = (globalThis as { fetch?: typeof fetch }).fetch
Object.defineProperty(globalThis, 'fetch', {
  value: originalFetch,
  configurable: true,
  writable: true,
})

beforeEach(() => {
  localStorageMemory.clear()
  sessionStorageMemory.clear()
  vi.restoreAllMocks()
  // Default fetch-stub voorkomt dat fire-and-forget ensure*Loaded calls
  // (b.v. via setActivePbi) lekken naar het echte network. Tests die
  // specifieke responses willen overrulen dit met vi.spyOn/vi.fn.
  // G8: mockImplementation (niet mockResolvedValue) zodat elke call een
  // verse Response krijgt — body wordt anders maar één keer leesbaar.
  ;(globalThis as { fetch: typeof fetch }).fetch = vi
    .fn()
    .mockImplementation(() =>
      Promise.resolve(new Response('null', { status: 200 })),
    ) as unknown as typeof fetch
})
