import { renderHook, act, waitFor } from '@testing-library/react'
import { vi, beforeEach, describe, it, expect } from 'vitest'
import { useSupabaseTable } from '../src/hooks/useSupabaseTable'
import * as sb from '../src/lib/supabase'

function builder(result: any) {
  const b: any = {}
  b.select = vi.fn(() => Promise.resolve(result))
  b.insert = vi.fn(() => ({ select: vi.fn(() => Promise.resolve({ data: [{ id: 'new', title: 'x' }], error: null })) }))
  b.update = vi.fn(() => ({ eq: vi.fn(() => Promise.resolve({ error: null })) }))
  b.delete = vi.fn(() => ({ eq: vi.fn(() => Promise.resolve({ error: null })) }))
  return b
}
beforeEach(() => vi.restoreAllMocks())

describe('useSupabaseTable', () => {
  it('loads rows on mount', async () => {
    const b = builder({ data: [{ id: '1', title: 'a' }], error: null })
    vi.spyOn(sb, 'getSupabase').mockReturnValue({ from: () => b } as any)
    const { result } = renderHook(() => useSupabaseTable<any>('posts', { enabled: true }))
    await waitFor(() => expect(result.current.loading).toBe(false))
    expect(b.select).toHaveBeenCalled()
    expect(result.current.data).toEqual([{ id: '1', title: 'a' }])
  })

  it('sets error and keeps data empty on failure', async () => {
    const b = builder({ data: null, error: { message: 'relation does not exist' } })
    vi.spyOn(sb, 'getSupabase').mockReturnValue({ from: () => b } as any)
    const { result } = renderHook(() => useSupabaseTable<any>('missing', { enabled: true }))
    await waitFor(() => expect(result.current.loading).toBe(false))
    expect(result.current.error).toBeTruthy()
    expect(result.current.data).toEqual([])
  })

  it('does nothing when disabled', async () => {
    const b = builder({ data: [{ id: '1' }], error: null })
    vi.spyOn(sb, 'getSupabase').mockReturnValue({ from: () => b } as any)
    const { result } = renderHook(() => useSupabaseTable<any>('posts', { enabled: false }))
    await waitFor(() => expect(result.current.loading).toBe(false))
    expect(b.select).not.toHaveBeenCalled()
    expect(result.current.data).toEqual([])
  })

  it('does not throw when client is null', async () => {
    vi.spyOn(sb, 'getSupabase').mockReturnValue(null as any)
    const { result } = renderHook(() => useSupabaseTable<any>('posts', { enabled: true }))
    await waitFor(() => expect(result.current.loading).toBe(false))
    expect(result.current.data).toEqual([])
  })

  it('add() inserts and appends the new row to local data', async () => {
    const b = builder({ data: [{ id: '1', title: 'a' }], error: null })
    vi.spyOn(sb, 'getSupabase').mockReturnValue({ from: () => b } as any)
    const { result } = renderHook(() => useSupabaseTable<any>('posts', { enabled: true }))
    await waitFor(() => expect(result.current.loading).toBe(false))
    let id: string | null = null
    await act(async () => { id = await result.current.add({ title: 'x' }) })
    expect(b.insert).toHaveBeenCalledWith({ title: 'x' })
    expect(id).toBe('new')
    expect(result.current.data).toContainEqual({ id: 'new', title: 'x' })
  })

  it('add() strips platform-managed columns (id, created_at, user_id) from inserts', async () => {
    const b = builder({ data: [{ id: '1', title: 'a' }], error: null })
    vi.spyOn(sb, 'getSupabase').mockReturnValue({ from: () => b } as any)
    const { result } = renderHook(() => useSupabaseTable<any>('posts', { enabled: true }))
    await waitFor(() => expect(result.current.loading).toBe(false))
    await act(async () => {
      await result.current.add({ title: 'x', created_at: '', id: 'should-drop', user_id: 'should-drop' })
    })
    expect(b.insert).toHaveBeenCalledWith({ title: 'x' })
  })

  it('update() patches the local row', async () => {
    const b = builder({ data: [{ id: '1', title: 'a' }], error: null })
    vi.spyOn(sb, 'getSupabase').mockReturnValue({ from: () => b } as any)
    const { result } = renderHook(() => useSupabaseTable<any>('posts', { enabled: true }))
    await waitFor(() => expect(result.current.loading).toBe(false))
    let ok = false
    await act(async () => { ok = await result.current.update('1', { title: 'b' }) })
    expect(b.update).toHaveBeenCalledWith({ title: 'b' })
    expect(ok).toBe(true)
    expect(result.current.data).toContainEqual({ id: '1', title: 'b' })
  })

  it('remove() drops the local row', async () => {
    const b = builder({ data: [{ id: '1', title: 'a' }], error: null })
    vi.spyOn(sb, 'getSupabase').mockReturnValue({ from: () => b } as any)
    const { result } = renderHook(() => useSupabaseTable<any>('posts', { enabled: true }))
    await waitFor(() => expect(result.current.loading).toBe(false))
    let ok = false
    await act(async () => { ok = await result.current.remove('1') })
    expect(b.delete).toHaveBeenCalled()
    expect(ok).toBe(true)
    expect(result.current.data).toEqual([])
  })
})
