/**
 * Tests for energy/DeviceForm.tsx
 *
 * Coverage:
 * 1. Renders empty form in create mode (title "New Device").
 * 2. Renders pre-filled form in edit mode (title "Edit Device"), shows UUID.
 * 3. Validation: missing friendly_name shows error.
 * 4. Profile dropdown loads options from GET /api/modbus/profiles.
 * 5. Submit in create mode calls POST /api/modbus/devices.
 * 6. Submit in edit mode calls PATCH /api/modbus/devices/{uuid}.
 * 7. Cancel button invokes onClose without submitting.
 */

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { screen, waitFor, fireEvent } from '@testing-library/react'
import { renderWithProviders } from '../test-utils'
import { DeviceForm } from './DeviceForm'

// ---------------------------------------------------------------------------
// Fixtures
// ---------------------------------------------------------------------------

const DEVICE = {
  uuid: 'abc-123',
  friendly_name: 'SDM120 Main',
  transport: 'tcp',
  host: '192.168.1.100',
  port: 502,
  unit_id: 1,
  profile: 'sdm120',
  poll_interval_s: 5,
  enabled: true,
  last_poll_at: null,
  last_poll_ok: null,
  created_at: '2026-06-01T00:00:00Z',
  updated_at: '2026-06-01T00:00:00Z',
}

const PROFILES_RESP = {
  profiles: [{ name: 'sdm120', description: 'Eastron SDM120 single-phase energy meter' }],
}

// ---------------------------------------------------------------------------
// Mock apiClient
// ---------------------------------------------------------------------------

const mockGet = vi.fn()
const mockPost = vi.fn()
const mockPatch = vi.fn()

vi.mock('../api/client', () => ({
  default: {
    GET: (...args: unknown[]) => mockGet(...args),
    POST: (...args: unknown[]) => mockPost(...args),
    PATCH: (...args: unknown[]) => mockPatch(...args),
    DELETE: vi.fn(),
  },
  ApiError: class ApiError extends Error {
    status: number
    body: unknown
    constructor(status: number, body: unknown) {
      super(`API error ${status}`)
      this.name = 'ApiError'
      this.status = status
      this.body = body
    }
  },
  registerLoginRedirect: vi.fn(),
}))

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

function setupProfilesMock() {
  mockGet.mockImplementation((path: string) => {
    if (path === '/api/modbus/profiles') {
      return Promise.resolve({ data: PROFILES_RESP })
    }
    // modbus-devices for invalidation
    return Promise.resolve({ data: { items: [], total: 0 } })
  })
}

function renderCreateForm(onClose = vi.fn(), onSaved = vi.fn()) {
  return renderWithProviders(<DeviceForm onClose={onClose} onSaved={onSaved} />, {
    initialPath: '/',
  })
}

function renderEditForm(onClose = vi.fn(), onSaved = vi.fn()) {
  return renderWithProviders(<DeviceForm device={DEVICE} onClose={onClose} onSaved={onSaved} />, {
    initialPath: '/',
  })
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

describe('DeviceForm — create mode', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    setupProfilesMock()
  })

  it('renders with title "New Device" and empty friendly-name input', async () => {
    renderCreateForm()

    await waitFor(() => {
      expect(screen.getByTestId('device-form-modal')).toBeInTheDocument()
    })

    // Modal title
    expect(screen.getByText('New Device')).toBeInTheDocument()

    // UUID display should NOT be present in create mode
    expect(screen.queryByTestId('device-uuid-display')).not.toBeInTheDocument()
  })

  it('loads profile options from GET /api/modbus/profiles', async () => {
    renderCreateForm()

    await waitFor(() => {
      expect(screen.getByTestId('device-form')).toBeInTheDocument()
    })

    // Profile select should be rendered
    expect(screen.getByTestId('device-profile')).toBeInTheDocument()
    expect(mockGet).toHaveBeenCalledWith('/api/modbus/profiles')
  })

  it('shows validation error when friendly name is empty', async () => {
    renderCreateForm()

    await waitFor(() => expect(screen.getByTestId('device-form')).toBeInTheDocument())

    fireEvent.submit(screen.getByTestId('device-form'))

    await waitFor(() => {
      expect(screen.getByTestId('device-form-error')).toBeInTheDocument()
    })

    expect(screen.getByTestId('device-form-error').textContent).toContain('Friendly name')
  })

  it('submit calls POST /api/modbus/devices with correct body (profile auto-selected)', async () => {
    mockPost.mockResolvedValue({ data: DEVICE })
    const onSaved = vi.fn()
    renderCreateForm(vi.fn(), onSaved)

    // Wait for profiles to load; sdm120 is auto-selected as first profile.
    await waitFor(() => expect(screen.getByTestId('device-form')).toBeInTheDocument())

    // Fill form (profile auto-selected to 'sdm120')
    fireEvent.change(screen.getByTestId('device-friendly-name'), {
      target: { value: 'My Meter' },
    })
    fireEvent.change(screen.getByTestId('device-host'), {
      target: { value: '10.0.0.1' },
    })

    fireEvent.submit(screen.getByTestId('device-form'))

    await waitFor(() => {
      expect(mockPost).toHaveBeenCalledWith(
        '/api/modbus/devices',
        expect.objectContaining({
          body: expect.objectContaining({
            friendly_name: 'My Meter',
            host: '10.0.0.1',
            transport: 'tcp',
            profile: 'sdm120',
          }),
        }),
      )
    })
  })

  it('cancel calls onClose without POST', async () => {
    const onClose = vi.fn()
    renderCreateForm(onClose)

    await waitFor(() => expect(screen.getByTestId('device-form-cancel')).toBeInTheDocument())

    fireEvent.click(screen.getByTestId('device-form-cancel'))

    expect(onClose).toHaveBeenCalledOnce()
    expect(mockPost).not.toHaveBeenCalled()
  })
})

describe('DeviceForm — edit mode', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    setupProfilesMock()
  })

  it('renders with title "Edit Device" and pre-filled friendly-name', async () => {
    renderEditForm()

    // Wait for modal to appear
    await waitFor(() => {
      expect(screen.getByTestId('device-form-modal')).toBeInTheDocument()
    })

    expect(screen.getByText('Edit Device')).toBeInTheDocument()

    // Wait for form to appear (profiles must load first)
    await waitFor(() => {
      expect(screen.getByTestId('device-form')).toBeInTheDocument()
    })

    expect(screen.getByTestId('device-uuid-display').textContent).toContain('abc-123')

    // Friendly name input should be pre-filled
    const input = screen.getByTestId('device-friendly-name') as HTMLInputElement
    expect(input.value).toBe('SDM120 Main')
  })

  it('submit calls PATCH /api/modbus/devices/{uuid}', async () => {
    mockPatch.mockResolvedValue({ data: DEVICE })
    renderEditForm()

    // Wait for profiles to load so the form is visible
    await waitFor(() => expect(screen.getByTestId('device-form')).toBeInTheDocument())

    fireEvent.change(screen.getByTestId('device-friendly-name'), {
      target: { value: 'SDM120 Updated' },
    })

    fireEvent.submit(screen.getByTestId('device-form'))

    await waitFor(() => {
      expect(mockPatch).toHaveBeenCalledWith('/api/modbus/devices/{uuid}', {
        params: { path: { uuid: 'abc-123' } },
        body: expect.objectContaining({ friendly_name: 'SDM120 Updated' }),
      })
    })
  })
})
