import { describe, it, expect, vi, beforeEach } from 'vitest'
import * as profileApi from './profile.js'

const nextResults = []
function createChain() {
  const getResult = () => nextResults.shift() ?? { data: null, error: null }
  const chain = {
    from: vi.fn(function () { return this }),
    select: vi.fn(function () { return this }),
    eq: vi.fn(function () { return this }),
    ilike: vi.fn(function () { return this }),
    limit: vi.fn(function () { return this }),
    single: vi.fn(() => Promise.resolve(getResult())),
    update: vi.fn(function () { return this }),
    then(resolve) { return Promise.resolve(getResult()).then(resolve) },
    catch(fn) { return Promise.resolve(getResult()).catch(fn) },
  }
  return chain
}

const mockStorageFrom = {
  getPublicUrl: vi.fn(() => ({ data: { publicUrl: 'https://example.com/avatars/path' } })),
  upload: vi.fn(() => Promise.resolve({ error: null })),
}

vi.mock('../supabase.js', () => ({
  supabase: {
    from: vi.fn(() => createChain()),
    storage: { from: vi.fn(() => mockStorageFrom) },
  },
}))

describe('profile API', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    nextResults.length = 0
  })

  describe('getEmailForUsername', () => {
    it('returns null for empty or whitespace username', async () => {
      expect(await profileApi.getEmailForUsername('')).toBe(null)
      expect(await profileApi.getEmailForUsername('  ')).toBe(null)
      expect(await profileApi.getEmailForUsername(null)).toBe(null)
    })

    it('returns email when profile found', async () => {
      nextResults.push({ data: [{ email: 'user@example.com' }], error: null })
      const email = await profileApi.getEmailForUsername('johndoe')
      expect(email).toBe('user@example.com')
    })

    it('returns null when error or no data', async () => {
      nextResults.push({ data: [], error: null })
      expect(await profileApi.getEmailForUsername('x')).toBe(null)
      nextResults.push({ data: null, error: { message: 'err' } })
      expect(await profileApi.getEmailForUsername('y')).toBe(null)
      nextResults.push({ data: [{}], error: null })
      expect(await profileApi.getEmailForUsername('z')).toBe(null)
    })
  })

  describe('getProfile', () => {
    it('returns profile when found', async () => {
      const p = { id: 'u1', display_name: 'Alice', email: 'a@b.com', avatar_url: null }
      nextResults.push({ data: p, error: null })
      const result = await profileApi.getProfile('u1')
      expect(result).toEqual(p)
    })

    it('returns null when not found (PGRST116)', async () => {
      nextResults.push({ data: null, error: { code: 'PGRST116' } })
      const result = await profileApi.getProfile('u1')
      expect(result).toBe(null)
    })

    it('throws when other error', async () => {
      nextResults.push({ data: null, error: new Error('Network error') })
      await expect(profileApi.getProfile('u1')).rejects.toThrow('Network error')
    })
  })

  describe('updateProfile', () => {
    it('updates display_name and returns data', async () => {
      const updated = { id: 'u1', display_name: 'New Name', email: 'a@b.com', avatar_url: null }
      nextResults.push({ data: updated, error: null })
      const result = await profileApi.updateProfile('u1', { display_name: 'New Name' })
      expect(result).toEqual(updated)
    })

    it('trims display_name and allows avatar_url', async () => {
      nextResults.push({ data: { id: 'u1' }, error: null })
      await profileApi.updateProfile('u1', { display_name: '  Trim  ', avatar_url: 'path/to/av.jpg' })
      expect(nextResults.length).toBe(0)
    })

    it('throws on error', async () => {
      nextResults.push({ data: null, error: new Error('DB error') })
      await expect(profileApi.updateProfile('u1', { display_name: 'X' })).rejects.toThrow('DB error')
    })
  })

  describe('getAvatarPublicUrl', () => {
    it('returns publicUrl from storage', () => {
      const url = profileApi.getAvatarPublicUrl('user1/avatar.jpg')
      expect(url).toBe('https://example.com/avatars/path')
      expect(mockStorageFrom.getPublicUrl).toHaveBeenCalledWith('user1/avatar.jpg')
    })
  })

  describe('uploadAvatar', () => {
    it('uploads file and returns public URL', async () => {
      const file = new File(['x'], 'photo.jpg', { type: 'image/jpeg' })
      const url = await profileApi.uploadAvatar('u1', file)
      expect(mockStorageFrom.upload).toHaveBeenCalledWith('u1/avatar.jpg', file, { upsert: true, contentType: 'image/jpeg' })
      expect(url).toBe('https://example.com/avatars/path')
    })

    it('throws on upload error', async () => {
      mockStorageFrom.upload.mockResolvedValueOnce({ error: new Error('Storage full') })
      const file = new File(['x'], 'a.png', { type: 'image/png' })
      await expect(profileApi.uploadAvatar('u1', file)).rejects.toThrow('Storage full')
    })
  })
})
