import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createEvent, mockSuccessReadBody } from './setup'
import fs from 'fs/promises'
import sharp from 'sharp'

vi.mock('../server/utils/auth.js', () => ({
  getUserFromToken: vi.fn(),
  verifyToken: vi.fn(),
  readUsers: vi.fn(),
  writeUsers: vi.fn(),
  hasAnyRole: vi.fn((user, ...roles) => {
    if (!user) return false
    const userRoles = Array.isArray(user.roles) ? user.roles : (user.role ? [user.role] : [])
    return roles.some(r => userRoles.includes(r))
  })
}))

vi.mock('sharp', () => ({
  default: vi.fn(() => ({
    metadata: vi.fn().mockResolvedValue({ width: 1200, height: 800 }),
    resize: vi.fn().mockReturnThis(),
    rotate: vi.fn().mockReturnThis(),
    toFile: vi.fn().mockResolvedValue({}),
    toBuffer: vi.fn().mockResolvedValue(Buffer.from('image data'))
  }))
}))

vi.mock('multer', () => {
  const single = vi.fn((field) => (req, _res, cb) => {
    if (req.__mockMulterError) {
      cb(req.__mockMulterError)
      return
    }
    req.file = req.__mockFile || null
    req.body = req.body || {}
    cb(null)
  })

  const multerFn = vi.fn(() => ({ single }))
  const diskStorage = vi.fn(() => ({}))
  multerFn.diskStorage = diskStorage

  return {
    default: multerFn,
    diskStorage
  }
})

const authUtils = await import('../server/utils/auth.js')

import uploadHandler from '../server/api/galerie/upload.post.js'
import listHandler from '../server/api/galerie/list.get.js'
import imageHandler from '../server/api/galerie/[id].get.js'

describe('Galerie API Endpoints', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.spyOn(fs, 'readFile').mockResolvedValue('[]')
    vi.spyOn(fs, 'writeFile').mockResolvedValue(undefined)
    vi.spyOn(fs, 'mkdir').mockResolvedValue(undefined)
    vi.spyOn(fs, 'access').mockResolvedValue(undefined)
    vi.spyOn(fs, 'rename').mockResolvedValue(undefined)
    vi.spyOn(fs, 'stat').mockResolvedValue({ size: 1024 } as any)
    vi.spyOn(fs, 'unlink').mockResolvedValue(undefined)
  })

  describe('POST /api/galerie/upload', () => {
    it('erfordert Authentifizierung', async () => {
      const event = createEvent({ method: 'POST' })
      event.node.req.__mockFile = { filename: 'test.jpg', path: 'tmp/test.jpg', originalname: 'test.jpg', mimetype: 'image/jpeg' }
      event.node.req.body = { title: 'Test', isPublic: 'true' }

      await expect(uploadHandler(event)).rejects.toMatchObject({ statusCode: 401 })
    })

    it('erfordert Admin- oder Vorstand-Rolle', async () => {
      const event = createEvent({ method: 'POST', cookies: { auth_token: 'token' } })
      event.node.req.__mockFile = { filename: 'test.jpg', path: 'tmp/test.jpg', originalname: 'test.jpg', mimetype: 'image/jpeg' }
      event.node.req.body = { title: 'Test', isPublic: 'true' }
      authUtils.verifyToken.mockReturnValue({ id: '1' })
      authUtils.getUserFromToken.mockResolvedValue({ id: '1', roles: ['mitglied'], active: true })
      authUtils.hasAnyRole.mockReturnValue(false)

      await expect(uploadHandler(event)).rejects.toMatchObject({ statusCode: 403 })
    })

    it('lädt Bild hoch und erstellt Thumbnail', async () => {
      const event = createEvent({ method: 'POST', cookies: { auth_token: 'token' } })
      const mockPath = 'tmp/test.jpg'
      event.node.req.__mockFile = { filename: 'test.jpg', path: mockPath, originalname: 'test.jpg', mimetype: 'image/jpeg' }
      event.node.req.body = { title: 'Test Bild', description: 'Beschreibung', isPublic: 'true' }
      authUtils.verifyToken.mockReturnValue({ id: '1' })
      authUtils.getUserFromToken.mockResolvedValue({ id: '1', roles: ['admin'], active: true })
      // hasAnyRole muss für diesen Test true zurückgeben
      authUtils.hasAnyRole.mockImplementation((user, ...roles) => {
        if (!user) return false
        const userRoles = Array.isArray(user.roles) ? user.roles : (user.role ? [user.role] : [])
        return roles.some(r => userRoles.includes(r))
      })
      
      // Mock fs.rename für die Datei-Verschiebung
      vi.spyOn(fs, 'rename').mockResolvedValue(undefined)
      // Mock fs.readFile für Metadaten
      vi.spyOn(fs, 'readFile').mockResolvedValue('[]')

      const response = await uploadHandler(event)

      expect(response.success).toBe(true)
      expect(sharp).toHaveBeenCalled()
      expect(fs.writeFile).toHaveBeenCalled()
    })
  })

  describe('GET /api/galerie/list', () => {
    it('zeigt öffentliche Bilder für alle', async () => {
      const event = createEvent()
      vi.spyOn(fs, 'readFile').mockResolvedValue(JSON.stringify([
        { id: '1', title: 'Öffentlich', isPublic: true, previewFilename: 'preview_1.jpg', uploadedAt: '2025-01-01' },
        { id: '2', title: 'Privat', isPublic: false, previewFilename: 'preview_2.jpg', uploadedAt: '2025-01-02' }
      ]))

      const response = await listHandler(event)

      expect(response.success).toBe(true)
      expect(response.images).toHaveLength(1)
      expect(response.images[0].id).toBe('1')
    })

    it('zeigt alle Bilder für eingeloggte Mitglieder', async () => {
      const event = createEvent({ cookies: { auth_token: 'token' } })
      vi.spyOn(fs, 'readFile').mockResolvedValue(JSON.stringify([
        { id: '1', title: 'Öffentlich', isPublic: true, previewFilename: 'preview_1.jpg', uploadedAt: '2025-01-01' },
        { id: '2', title: 'Privat', isPublic: false, previewFilename: 'preview_2.jpg', uploadedAt: '2025-01-02' }
      ]))
      authUtils.verifyToken.mockReturnValue({ id: '1' })
      authUtils.getUserFromToken.mockResolvedValue({ id: '1', active: true })

      const response = await listHandler(event)

      expect(response.images).toHaveLength(2)
    })
  })

  describe('GET /api/galerie/[id]', () => {
    it('verweigert Zugriff auf private Bilder für nicht eingeloggte Benutzer', async () => {
      const event = createEvent()
      event.context.params = { id: '1' }
      vi.spyOn(fs, 'readFile')
        .mockResolvedValueOnce(JSON.stringify([
          { id: '1', filename: 'test.jpg', previewFilename: 'preview_test.jpg', isPublic: false }
        ]))

      await expect(imageHandler(event)).rejects.toMatchObject({ statusCode: 403 })
    })

    it('liefert Bild für eingeloggte Mitglieder', async () => {
      const event = createEvent({ cookies: { auth_token: 'token' } })
      event.context.params = { id: '1' }
      vi.spyOn(fs, 'readFile')
        .mockResolvedValueOnce(JSON.stringify([
          { id: '1', filename: 'test.jpg', previewFilename: 'preview_test.jpg', isPublic: false }
        ]))
        .mockResolvedValueOnce(Buffer.from('image data'))
      vi.spyOn(fs, 'access').mockResolvedValue(undefined)
      authUtils.verifyToken.mockReturnValue({ id: '1' })
      authUtils.getUserFromToken.mockResolvedValue({ id: '1', active: true })

      const response = await imageHandler(event)

      expect(response).toBeInstanceOf(Buffer)
    })

    it('liefert Preview-Bild bei preview=true', async () => {
      const event = createEvent({ query: { preview: 'true' } })
      event.context.params = { id: '1' }
      vi.spyOn(fs, 'readFile')
        .mockResolvedValueOnce(JSON.stringify([
          { id: '1', filename: 'test.jpg', previewFilename: 'preview_test.jpg', isPublic: true }
        ]))
        .mockResolvedValueOnce(Buffer.from('preview data'))
      vi.spyOn(fs, 'access').mockResolvedValue(undefined)

      const response = await imageHandler(event)

      expect(response).toBeInstanceOf(Buffer)
    })
  })
})

