import { describe, it, expect, beforeEach, vi } from 'vitest'
import { prismaMock, resetPrismaMock } from '../../../mocks/prisma'
import { OrderStatus, AutomationMode, LogLevel } from '@prisma/client'

import {
  processAutomation,
  setAutomationMode,
  resumeAutomation,
  pauseAutomation,
  takeManualControl,
  enableAutoMode,
} from '@/lib/services/automation-worker'

describe('Automation Worker', () => {
  beforeEach(() => {
    resetPrismaMock()
    // Suppress console.error from logAutomation error handling
    vi.spyOn(console, 'error').mockImplementation(() => {})
    // Mock provisioningLog.create for all tests (used by logAutomation)
    prismaMock.provisioningLog.create.mockResolvedValue({} as any)
  })

  describe('processAutomation', () => {
    it('should return not triggered when order not found', async () => {
      prismaMock.order.findUnique.mockResolvedValue(null)

      const result = await processAutomation('invalid-id')

      expect(result.triggered).toBe(false)
      expect(result.error).toBe('Order not found')
    })

    it('should skip processing when mode is MANUAL', async () => {
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.MANUAL,
        status: OrderStatus.SERVER_READY,
      } as any)

      const result = await processAutomation('order-123')

      expect(result.triggered).toBe(false)
      expect(result.action).toContain('MANUAL')
    })

    it('should skip processing when mode is PAUSED', async () => {
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.PAUSED,
        status: OrderStatus.SERVER_READY,
      } as any)

      const result = await processAutomation('order-123')

      expect(result.triggered).toBe(false)
      expect(result.action).toContain('PAUSED')
    })

    it('should wait for server credentials in AWAITING_SERVER status', async () => {
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.AWAITING_SERVER,
        serverIp: null,
        serverPasswordEncrypted: null,
      } as any)

      const result = await processAutomation('order-123')

      expect(result.triggered).toBe(false)
      expect(result.action).toContain('Waiting for server credentials')
    })

    it('should auto-transition AWAITING_SERVER to SERVER_READY when credentials present', async () => {
      // First call: AWAITING_SERVER with credentials
      prismaMock.order.findUnique.mockResolvedValueOnce({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.AWAITING_SERVER,
        serverIp: '10.0.0.1',
        serverPasswordEncrypted: 'enc-password',
        dnsVerification: null,
        serverConnection: null,
      } as any)
      prismaMock.order.update.mockResolvedValue({} as any)

      // Recursive call after transition: now SERVER_READY
      prismaMock.order.findUnique.mockResolvedValueOnce({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.SERVER_READY,
        serverIp: '10.0.0.1',
        serverPasswordEncrypted: 'enc-password',
        dnsVerification: null,
        serverConnection: null,
      } as any)

      await processAutomation('order-123')

      // Should have updated to SERVER_READY
      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: expect.objectContaining({
          status: OrderStatus.SERVER_READY,
          serverReadyAt: expect.any(Date),
        }),
      })
    })

    it('should auto-transition SERVER_READY to DNS_PENDING', async () => {
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.SERVER_READY,
        dnsVerification: null,
        serverConnection: null,
      } as any)
      prismaMock.order.update.mockResolvedValue({} as any)

      const result = await processAutomation('order-123')

      expect(result.triggered).toBe(true)
      expect(result.action).toContain('DNS_PENDING')
      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: { status: OrderStatus.DNS_PENDING },
      })
    })

    it('should wait for DNS in DNS_PENDING status when not verified', async () => {
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.DNS_PENDING,
        dnsVerification: { allPassed: false, manualOverride: false },
        serverConnection: null,
      } as any)

      const result = await processAutomation('order-123')

      expect(result.triggered).toBe(false)
      expect(result.action).toContain('Waiting for DNS verification')
    })

    it('should auto-transition DNS_PENDING to DNS_READY when verified', async () => {
      // First call: DNS_PENDING with allPassed
      prismaMock.order.findUnique.mockResolvedValueOnce({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.DNS_PENDING,
        dnsVerification: { allPassed: true, manualOverride: false },
        serverConnection: null,
      } as any)
      prismaMock.order.update.mockResolvedValue({} as any)

      // Recursive call: DNS_READY
      prismaMock.order.findUnique.mockResolvedValueOnce({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.DNS_READY,
        dnsVerification: { allPassed: true, manualOverride: false },
        serverConnection: null,
      } as any)

      await processAutomation('order-123')

      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: expect.objectContaining({
          status: OrderStatus.DNS_READY,
          dnsVerifiedAt: expect.any(Date),
        }),
      })
    })

    it('should auto-transition DNS_PENDING to DNS_READY when manually overridden', async () => {
      prismaMock.order.findUnique.mockResolvedValueOnce({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.DNS_PENDING,
        dnsVerification: { allPassed: false, manualOverride: true },
        serverConnection: null,
      } as any)
      prismaMock.order.update.mockResolvedValue({} as any)

      // Recursive call
      prismaMock.order.findUnique.mockResolvedValueOnce({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.DNS_READY,
        dnsVerification: { allPassed: false, manualOverride: true },
        serverConnection: null,
      } as any)

      await processAutomation('order-123')

      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: expect.objectContaining({
          status: OrderStatus.DNS_READY,
        }),
      })
    })

    it('should signal ready for provisioning in DNS_READY status', async () => {
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.DNS_READY,
        dnsVerification: { allPassed: true },
        serverConnection: null,
      } as any)

      const result = await processAutomation('order-123')

      expect(result.triggered).toBe(true)
      expect(result.action).toContain('provision')
    })

    it('should report provisioning in progress', async () => {
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.PROVISIONING,
        dnsVerification: null,
        serverConnection: null,
      } as any)

      const result = await processAutomation('order-123')

      expect(result.triggered).toBe(false)
      expect(result.action).toContain('Provisioning in progress')
    })

    it('should report order fulfilled', async () => {
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.FULFILLED,
        dnsVerification: null,
        serverConnection: null,
      } as any)

      const result = await processAutomation('order-123')

      expect(result.triggered).toBe(false)
      expect(result.action).toContain('fulfilled')
    })

    it('should auto-pause on failure', async () => {
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.FAILED,
        dnsVerification: null,
        serverConnection: null,
      } as any)
      prismaMock.order.update.mockResolvedValue({} as any)

      const result = await processAutomation('order-123')

      expect(result.triggered).toBe(true)
      expect(result.action).toContain('Paused')
      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: expect.objectContaining({
          automationMode: AutomationMode.PAUSED,
          automationPausedAt: expect.any(Date),
        }),
      })
    })
  })

  describe('setAutomationMode', () => {
    it('should update order to PAUSED with reason', async () => {
      prismaMock.order.update.mockResolvedValue({} as any)
      prismaMock.order.findUnique.mockResolvedValue(null) // For processAutomation if called

      await setAutomationMode('order-123', AutomationMode.PAUSED, 'Test pause reason')

      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: expect.objectContaining({
          automationMode: AutomationMode.PAUSED,
          automationPausedAt: expect.any(Date),
          automationPausedReason: 'Test pause reason',
        }),
      })
    })

    it('should clear pause info when switching to AUTO', async () => {
      prismaMock.order.update.mockResolvedValue({} as any)
      // processAutomation is called after switching to AUTO
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.FULFILLED,
        dnsVerification: null,
        serverConnection: null,
      } as any)

      await setAutomationMode('order-123', AutomationMode.AUTO)

      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: expect.objectContaining({
          automationMode: AutomationMode.AUTO,
          automationPausedAt: null,
          automationPausedReason: null,
        }),
      })
    })

    it('should clear pause info when switching to MANUAL', async () => {
      prismaMock.order.update.mockResolvedValue({} as any)

      await setAutomationMode('order-123', AutomationMode.MANUAL)

      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: expect.objectContaining({
          automationMode: AutomationMode.MANUAL,
          automationPausedAt: null,
          automationPausedReason: null,
        }),
      })
    })

    it('should trigger processAutomation when switching to AUTO', async () => {
      prismaMock.order.update.mockResolvedValue({} as any)
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.SERVER_READY,
        dnsVerification: null,
        serverConnection: null,
      } as any)

      await setAutomationMode('order-123', AutomationMode.AUTO)

      // processAutomation should have been called (it reads order)
      expect(prismaMock.order.findUnique).toHaveBeenCalled()
    })
  })

  describe('resumeAutomation', () => {
    it('should return error when order not found', async () => {
      prismaMock.order.findUnique.mockResolvedValue(null)

      const result = await resumeAutomation('invalid-id')

      expect(result.triggered).toBe(false)
      expect(result.error).toBe('Order not found')
    })

    it('should return error when order is not paused', async () => {
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.MANUAL,
      } as any)

      const result = await resumeAutomation('order-123')

      expect(result.triggered).toBe(false)
      expect(result.error).toBe('Order is not paused')
    })

    it('should resume paused order to AUTO mode', async () => {
      // First call: check if paused
      prismaMock.order.findUnique.mockResolvedValueOnce({
        id: 'order-123',
        automationMode: AutomationMode.PAUSED,
      } as any)
      prismaMock.order.update.mockResolvedValue({} as any)

      // Second call from processAutomation after setAutomationMode
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.FULFILLED,
        dnsVerification: null,
        serverConnection: null,
      } as any)

      const result = await resumeAutomation('order-123')

      expect(result.triggered).toBe(true)
      expect(result.action).toBe('Automation resumed')
    })
  })

  describe('pauseAutomation', () => {
    it('should set mode to PAUSED with reason', async () => {
      prismaMock.order.update.mockResolvedValue({} as any)

      await pauseAutomation('order-123', 'Need to investigate')

      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: expect.objectContaining({
          automationMode: AutomationMode.PAUSED,
          automationPausedReason: 'Need to investigate',
        }),
      })
    })

    it('should use default reason when none provided', async () => {
      prismaMock.order.update.mockResolvedValue({} as any)

      await pauseAutomation('order-123')

      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: expect.objectContaining({
          automationPausedReason: 'Manually paused by staff',
        }),
      })
    })
  })

  describe('takeManualControl', () => {
    it('should set mode to MANUAL', async () => {
      prismaMock.order.update.mockResolvedValue({} as any)

      await takeManualControl('order-123')

      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: expect.objectContaining({
          automationMode: AutomationMode.MANUAL,
        }),
      })
    })
  })

  describe('enableAutoMode', () => {
    it('should set mode to AUTO and trigger processing', async () => {
      prismaMock.order.update.mockResolvedValue({} as any)
      prismaMock.order.findUnique.mockResolvedValue({
        id: 'order-123',
        automationMode: AutomationMode.AUTO,
        status: OrderStatus.DNS_READY,
        dnsVerification: null,
        serverConnection: null,
      } as any)

      const result = await enableAutoMode('order-123')

      expect(result.triggered).toBe(true)
      expect(prismaMock.order.update).toHaveBeenCalledWith({
        where: { id: 'order-123' },
        data: expect.objectContaining({
          automationMode: AutomationMode.AUTO,
        }),
      })
    })
  })
})
