import { CreateAppCommand, CreateAppOptions } from './create-app'
import { TemplateEngine } from '../template-engine/engine'
import inquirer from 'inquirer'
import * as fs from 'fs/promises'

jest.mock('../template-engine/engine')
jest.mock('inquirer')
jest.mock('fs/promises')

describe('CreateAppCommand', () => {
  let command: CreateAppCommand
  let mockEngine: jest.Mocked<TemplateEngine>

  beforeEach(() => {
    mockEngine = {
      generateFromTemplate: jest.fn().mockResolvedValue(undefined)
    } as any
    command = new CreateAppCommand(mockEngine)
    jest.spyOn(console, 'log').mockImplementation()
    ;(fs.mkdir as jest.Mock).mockResolvedValue(undefined)
  })

  afterEach(() => {
    jest.clearAllMocks()
  })

  it('should create a web app with provided options', async () => {
    const options: CreateAppOptions = {
      type: 'web',
      name: 'my-app',
      framework: 'vite-react',
      includeRouter: true,
      includeTailwind: false
    }

    await command.execute(options)

    expect(fs.mkdir).toHaveBeenCalledWith(
      expect.stringContaining('apps'),
      { recursive: true }
    )

    expect(mockEngine.generateFromTemplate).toHaveBeenCalledWith(
      expect.stringContaining('templates/typescript/apps/web'),
      {
        appName: 'my-app',
        framework: 'vite-react',
        includeRouter: true,
        includeTailwind: false
      },
      {
        outputPath: expect.stringContaining('apps/my-app'),
        dryRun: false
      }
    )

    expect(console.log).toHaveBeenCalledWith('✨ Created web app: my-app')
  })

  it('should create a mobile app with provided options', async () => {
    const options: CreateAppOptions = {
      type: 'mobile',
      name: 'my-mobile-app',
      displayName: 'My Mobile App',
      includeNavigator: true,
      includeAuth: false,
      includeApiClient: true
    }

    await command.execute(options)

    expect(mockEngine.generateFromTemplate).toHaveBeenCalledWith(
      expect.stringContaining('templates/typescript/apps/mobile'),
      {
        appName: 'my-mobile-app',
        displayName: 'My Mobile App',
        includeNavigator: true,
        includeAuth: false,
        includeApiClient: true,
        framework: undefined,
        includeRouter: undefined,
        includeTailwind: undefined
      },
      {
        outputPath: expect.stringContaining('apps/my-mobile-app'),
        dryRun: false
      }
    )

    expect(console.log).toHaveBeenCalledWith('✨ Created mobile app: my-mobile-app')
  })

  it('should create an API app with provided options', async () => {
    const options: CreateAppOptions = {
      type: 'api',
      name: 'my-api',
      port: 3001,
      includeDatabase: true,
      includeAuth: true,
      includeValidation: true,
      includeSwagger: false,
      includeGraphQL: false
    }

    await command.execute(options)

    expect(mockEngine.generateFromTemplate).toHaveBeenCalledWith(
      expect.stringContaining('templates/typescript/apps/api'),
      {
        appName: 'my-api',
        port: 3001,
        includeDatabase: true,
        includeAuth: true,
        includeValidation: true,
        includeSwagger: false,
        includeGraphQL: false,
        displayName: undefined,
        framework: undefined,
        includeRouter: undefined,
        includeTailwind: undefined,
        includeNavigator: undefined,
        includeApiClient: undefined
      },
      {
        outputPath: expect.stringContaining('apps/my-api'),
        dryRun: false
      }
    )

    expect(console.log).toHaveBeenCalledWith('✨ Created api app: my-api')
  })

  it('should create a desktop app with provided options', async () => {
    const options: CreateAppOptions = {
      type: 'desktop',
      name: 'my-desktop',
      displayName: 'My Desktop App',
      appId: 'com.vibes.my-desktop',
      includeAutoUpdater: true,
      includeSystemTray: false,
      includeMenuBar: true,
      includeIpc: true,
      includeDarkMode: true
    }

    await command.execute(options)

    expect(mockEngine.generateFromTemplate).toHaveBeenCalledWith(
      expect.stringContaining('templates/typescript/apps/desktop'),
      {
        appName: 'my-desktop',
        displayName: 'My Desktop App',
        appId: 'com.vibes.my-desktop',
        includeAutoUpdater: true,
        includeSystemTray: false,
        includeMenuBar: true,
        includeIpc: true,
        includeDarkMode: true,
        framework: undefined,
        includeRouter: undefined,
        includeTailwind: undefined,
        includeNavigator: undefined,
        includeAuth: undefined,
        includeApiClient: undefined,
        port: undefined,
        includeDatabase: undefined,
        includeValidation: undefined,
        includeSwagger: undefined,
        includeGraphQL: undefined
      },
      {
        outputPath: expect.stringContaining('apps/my-desktop'),
        dryRun: false
      }
    )

    expect(console.log).toHaveBeenCalledWith('✨ Created desktop app: my-desktop')
  })

  it('should prompt for missing options', async () => {
    const options: CreateAppOptions = {
      type: 'web'
    }

    ;(inquirer.prompt as jest.Mock).mockResolvedValue({
      name: 'prompted-app',
      framework: 'nextjs',
      includeRouter: false,
      includeTailwind: true
    })

    await command.execute(options)

    expect(inquirer.prompt).toHaveBeenCalledWith(
      expect.arrayContaining([
        expect.objectContaining({ name: 'name' }),
        expect.objectContaining({ name: 'framework' }),
        expect.objectContaining({ name: 'includeRouter' }),
        expect.objectContaining({ name: 'includeTailwind' })
      ])
    )

    expect(mockEngine.generateFromTemplate).toHaveBeenCalledWith(
      expect.any(String),
      {
        appName: 'prompted-app',
        framework: 'nextjs',
        includeRouter: false,
        includeTailwind: true
      },
      expect.any(Object)
    )
  })

  it('should validate app name', async () => {
    const options: CreateAppOptions = {
      type: 'web'
    }

    ;(inquirer.prompt as jest.Mock).mockImplementation((questions) => {
      const nameQuestion = questions.find((q: any) => q.name === 'name')
      expect(nameQuestion.validate('Invalid Name!')).toBe(
        'App name must be lowercase alphanumeric with dashes'
      )
      expect(nameQuestion.validate('')).toBe('App name is required')
      expect(nameQuestion.validate('valid-name')).toBe(true)
      
      return Promise.resolve({
        name: 'valid-name',
        framework: 'vite-react',
        includeRouter: true,
        includeTailwind: false
      })
    })

    await command.execute(options)
  })
})