import { describe, it, expect, vi, beforeEach } from 'vitest'

// Import AFTER mocking
import {
  createModelType,
  updateModelType,
  deleteModelType,
  getModelType,
  type ModelType,
  type ModelTypePayload,
} from '~/services/modelTypes'

// The service uses useRequestFetch from #imports (explicit import)
// AND useRuntimeConfig as a Nuxt auto-import (bare global).
const mockFetch = vi.fn()

// Mock the explicit #imports module
vi.mock('#imports', () => ({
  useRuntimeConfig: () => ({
    public: { apiBaseUrl: 'http://test-api:8081/api' },
  }),
  useRequestFetch: () => mockFetch,
}))

// Also stub the global auto-import (Nuxt makes these globally available)
vi.stubGlobal('useRuntimeConfig', () => ({
  public: { apiBaseUrl: 'http://test-api:8081/api' },
}))

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

const fakeModelType = (overrides: Partial<ModelType> = {}): ModelType => ({
  id: 'mt-1',
  name: 'Test Type',
  code: 'test-type',
  category: 'COMPONENT',
  structure: null,
  createdAt: '2025-01-01T00:00:00Z',
  updatedAt: '2025-01-01T00:00:00Z',
  ...overrides,
})

beforeEach(() => {
  mockFetch.mockReset()
})

// ---------------------------------------------------------------------------
// normalizeModelType (tested via getModelType which calls .then(normalizeModelType))
// ---------------------------------------------------------------------------
describe('normalizeModelType (via getModelType)', () => {
  it('returns structure as-is for COMPONENT', async () => {
    const structure = { customFields: [{ name: 'Weight' }] }
    mockFetch.mockResolvedValue(fakeModelType({
      category: 'COMPONENT',
      structure: structure as any,
    }))

    const result = await getModelType('mt-1')
    expect(result.structure).toEqual(structure)
  })

  it('returns structure as-is for PIECE', async () => {
    const structure = { customFields: [{ name: 'Size' }] }
    mockFetch.mockResolvedValue(fakeModelType({
      category: 'PIECE',
      structure: structure as any,
    }))

    const result = await getModelType('mt-1')
    expect(result.structure).toEqual(structure)
  })

  it('returns structure as-is for PRODUCT', async () => {
    const structure = { customFields: [{ name: 'Brand' }] }
    mockFetch.mockResolvedValue(fakeModelType({
      category: 'PRODUCT',
      structure: structure as any,
    }))

    const result = await getModelType('mt-1')
    expect(result.structure).toEqual(structure)
  })

  it('preserves null structure', async () => {
    mockFetch.mockResolvedValue(fakeModelType({
      category: 'COMPONENT',
      structure: null,
    }))

    const result = await getModelType('mt-1')
    expect(result.structure).toBeNull()
  })
})

// ---------------------------------------------------------------------------
// createModelType — sends structure directly
// ---------------------------------------------------------------------------
describe('createModelType', () => {
  it('sends POST with structure for COMPONENT', async () => {
    const structure = { customFields: [] }
    mockFetch.mockResolvedValue(fakeModelType())

    const payload: ModelTypePayload = {
      name: 'New Type',
      code: 'new-type',
      category: 'COMPONENT',
      structure: structure as any,
    }

    await createModelType(payload)

    expect(mockFetch).toHaveBeenCalledOnce()
    const [endpoint, options] = mockFetch.mock.calls[0]
    expect(endpoint).toBe('/model_types')
    expect(options.method).toBe('POST')
    expect(options.body.structure).toEqual(structure)
  })

  it('sends POST with structure for PIECE', async () => {
    const structure = { customFields: [], products: [] }
    mockFetch.mockResolvedValue(fakeModelType({ category: 'PIECE' }))

    await createModelType({
      name: 'Piece Type',
      code: 'piece-type',
      category: 'PIECE',
      structure: structure as any,
    })

    const [, options] = mockFetch.mock.calls[0]
    expect(options.body.structure).toEqual(structure)
  })

  it('sends POST with structure for PRODUCT', async () => {
    const structure = { customFields: [] }
    mockFetch.mockResolvedValue(fakeModelType({ category: 'PRODUCT' }))

    await createModelType({
      name: 'Product Type',
      code: 'product-type',
      category: 'PRODUCT',
      structure: structure as any,
    })

    const [, options] = mockFetch.mock.calls[0]
    expect(options.body.structure).toEqual(structure)
  })
})

// ---------------------------------------------------------------------------
// updateModelType — sends structure directly
// ---------------------------------------------------------------------------
describe('updateModelType', () => {
  it('sends PATCH with correct endpoint and structure', async () => {
    const structure = { customFields: [{ name: 'Updated' }] }
    mockFetch.mockResolvedValue(fakeModelType())

    await updateModelType('mt-1', {
      name: 'Updated',
      code: 'updated',
      category: 'COMPONENT',
      structure: structure as any,
    })

    expect(mockFetch).toHaveBeenCalledOnce()
    const [endpoint, options] = mockFetch.mock.calls[0]
    expect(endpoint).toBe('/model_types/mt-1')
    expect(options.method).toBe('PATCH')
    expect(options.headers['Content-Type']).toBe('application/merge-patch+json')
    expect(options.body.structure).toEqual(structure)
  })

  it('sends payload without structure when not provided', async () => {
    mockFetch.mockResolvedValue(fakeModelType())

    await updateModelType('mt-1', {
      name: 'Just Name',
      code: 'just-name',
      category: 'COMPONENT',
    })

    const [, options] = mockFetch.mock.calls[0]
    expect(options.body.structure).toBeUndefined()
    expect(options.body.name).toBe('Just Name')
  })
})

// ---------------------------------------------------------------------------
// deleteModelType
// ---------------------------------------------------------------------------
describe('deleteModelType', () => {
  it('sends DELETE to correct endpoint', async () => {
    mockFetch.mockResolvedValue(undefined)

    await deleteModelType('mt-42')

    expect(mockFetch).toHaveBeenCalledOnce()
    const [endpoint, options] = mockFetch.mock.calls[0]
    expect(endpoint).toBe('/model_types/mt-42')
    expect(options.method).toBe('DELETE')
  })
})

// ---------------------------------------------------------------------------
// getModelType
// ---------------------------------------------------------------------------
describe('getModelType', () => {
  it('sends GET to correct endpoint', async () => {
    mockFetch.mockResolvedValue(fakeModelType({ id: 'mt-99' }))

    const result = await getModelType('mt-99')

    expect(mockFetch).toHaveBeenCalledOnce()
    const [endpoint, options] = mockFetch.mock.calls[0]
    expect(endpoint).toBe('/model_types/mt-99')
    expect(options.method).toBe('GET')
    expect(result.id).toBe('mt-99')
  })
})
