import { beforeEach, describe, expect, it, vi } from 'vitest'
import { Hono } from 'hono'
import type { AppEnv } from '../src/types'

const mockSession = {
  type: 'user' as const,
  sub: 'user-1',
  tid: 'tenant-1',
  permissions: ['inventory:read', 'inventory:write'],
}

const mockGetInventorySettings = vi.fn()
const mockUpdateInventorySettings = vi.fn()

vi.mock('../src/middleware/auth', () => ({
  authMiddleware: async (
    c: { set: (key: string, value: unknown) => void },
    next: () => Promise<void>,
  ) => {
    c.set('db', {})
    c.set('session', mockSession)
    await next()
  },
}))

vi.mock('../src/middleware/guards', () => ({
  requirePermission: (permission: string) => async (
    c: {
      get: (key: string) => { permissions?: string[] } | undefined
      json: (body: unknown, status: number) => Response
    },
    next: () => Promise<void>,
  ) => {
    if (!c.get('session')?.permissions?.includes(permission)) {
      return c.json({ error: 'Forbidden' }, 403)
    }
    await next()
  },
}))

vi.mock('@zync/db/queries', () => ({
  createDb: vi.fn(() => ({})),
  getInventorySettings: (...args: unknown[]) => mockGetInventorySettings(...args),
  updateInventorySettings: (...args: unknown[]) => mockUpdateInventorySettings(...args),
}))

import { inventorySettingsRoute } from '../src/routes/settings/inventory'

function appWithRoute() {
  const app = new Hono<AppEnv>()
  app.route('/api/settings/inventory', inventorySettingsRoute)
  return app
}

describe('/api/settings/inventory', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockSession.permissions = ['inventory:read', 'inventory:write']
    mockGetInventorySettings.mockResolvedValue({
      inventory_method: 'fifo',
      allow_negative: true,
    })
    mockUpdateInventorySettings.mockResolvedValue({
      inventory_method: 'lifo',
      allow_negative: false,
    })
  })

  it('returns the canonical inventory settings payload on GET', async () => {
    const res = await appWithRoute().request('/api/settings/inventory')

    expect(res.status).toBe(200)
    await expect(res.json()).resolves.toEqual({
      inventory_method: 'fifo',
      allow_negative: true,
    })
    expect(mockGetInventorySettings).toHaveBeenCalledWith({}, 'tenant-1')
  })

  it('persists valid PATCH payloads', async () => {
    const res = await appWithRoute().request('/api/settings/inventory', {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ inventory_method: 'lifo', allow_negative: false }),
    })

    expect(res.status).toBe(200)
    expect(mockUpdateInventorySettings).toHaveBeenCalledWith({}, 'tenant-1', {
      inventory_method: 'lifo',
      allow_negative: false,
    })
  })

  it('does not accept legacy settings permissions', async () => {
    mockSession.permissions = ['settings:read', 'settings:write']

    const res = await appWithRoute().request('/api/settings/inventory')

    expect(res.status).toBe(403)
  })

  it('rejects invalid payloads', async () => {
    const res = await appWithRoute().request('/api/settings/inventory', {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ allow_negative: 'nope' }),
    })

    expect(res.status).toBe(400)
    expect(mockUpdateInventorySettings).not.toHaveBeenCalled()
  })
})
