import {describe, it, expect, beforeEach, afterEach} from 'vitest'
import {mkdtempSync, mkdirSync, writeFileSync, rmSync} from 'node:fs'
import {join} from 'node:path'
import {tmpdir} from 'node:os'
import {validateProject} from './validate.js'

function setupValidProject(dir) {
  writeFileSync(join(dir, '.mcp.json'), JSON.stringify({
    mcpServers: {kobiton: {type: 'http', url: 'https://api.kobiton.com/mcp'}}
  }))

  mkdirSync(join(dir, '.claude-plugin'))
  writeFileSync(join(dir, '.claude-plugin/plugin.json'), JSON.stringify({
    name: 'automate',
    description: 'Test',
    mcpServers: './.mcp.json',
    skills: './skills/'
  }))
  writeFileSync(join(dir, '.claude-plugin/marketplace.json'), JSON.stringify({
    name: 'kobiton',
    owner: {name: 'Kobiton'},
    plugins: [{
      name: 'automate',
      source: './',
      description: 'Test',
      category: 'testing',
      policy: {installation: 'AVAILABLE', authentication: 'ON_INSTALL'}
    }]
  }))
  mkdirSync(join(dir, '.agents/plugins'), {recursive: true})
  writeFileSync(join(dir, '.agents/plugins/marketplace.json'), JSON.stringify({
    name: 'kobiton',
    plugins: [{
      name: 'automate',
      source: './.codex',
      category: 'testing',
      policy: {installation: 'AVAILABLE', authentication: 'ON_INSTALL'}
    }]
  }))
  mkdirSync(join(dir, '.codex/.codex-plugin'), {recursive: true})
  writeFileSync(join(dir, '.codex/.codex-plugin/plugin.json'), JSON.stringify({
    name: 'automate',
    version: '1.0.2',
    description: 'Test',
    mcpServers: './.mcp.json'
  }))
  writeFileSync(join(dir, '.codex/.mcp.json'), JSON.stringify({
    mcpServers: {kobiton: {url: 'https://api.kobiton.com/mcp'}}
  }))
  writeFileSync(join(dir, 'gemini-extension.json'), JSON.stringify({
    name: 'kobiton-automate',
    version: '1.0.2',
    description: 'Test',
    mcpServers: {kobiton: {httpUrl: 'https://api.kobiton.com/mcp'}}
  }))

  mkdirSync(join(dir, '.cursor-plugin'))
  writeFileSync(join(dir, '.cursor-plugin/plugin.json'), JSON.stringify({
    name: 'automate',
    version: '1.0.2',
    description: 'Test',
    mcpServers: './.cursor/mcp.json',
    skills: './skills/'
  }))
  writeFileSync(join(dir, '.cursor-plugin/marketplace.json'), JSON.stringify({
    name: 'kobiton',
    owner: {name: 'Kobiton'},
    plugins: [{name: 'automate', source: './', description: 'Test'}]
  }))
  mkdirSync(join(dir, '.cursor/hooks'), {recursive: true})
  writeFileSync(join(dir, '.cursor/mcp.json'), JSON.stringify({
    mcpServers: {kobiton: {url: 'https://api.kobiton.com/mcp'}}
  }))
  writeFileSync(join(dir, '.cursor/hooks/hooks.json'), JSON.stringify({
    version: 1,
    hooks: {sessionStart: [{command: 'bash ${CURSOR_PLUGIN_ROOT}/scripts/install-cli.sh'}]}
  }))

  mkdirSync(join(dir, 'tools'))
  for (const file of ['devices.yaml', 'sessions.yaml', 'apps.yaml', 'user.yaml']) {
    writeFileSync(join(dir, 'tools', file), [
      'tools:',
      '  - name: testTool',
      '    description: A test tool',
      '    inputSchema:',
      '      type: object',
    ].join('\n'))
  }

  mkdirSync(join(dir, 'skills/run-automation-suite'), {recursive: true})
  writeFileSync(join(dir, 'skills/run-automation-suite/SKILL.md'), [
    '---',
    'name: run-automation-suite',
    'description: Test skill',
    '---',
    '## Workflow',
  ].join('\n'))

  mkdirSync(join(dir, 'skills/run-interactive-test'), {recursive: true})
  writeFileSync(join(dir, 'skills/run-interactive-test/SKILL.md'), [
    '---',
    'name: run-interactive-test',
    'description: Test skill',
    '---',
    '## Setup',
  ].join('\n'))

}

describe('validateProject', () => {
  let tmpDir

  beforeEach(() => {
    tmpDir = mkdtempSync(join(tmpdir(), 'automate-test-'))
  })

  afterEach(() => {
    rmSync(tmpDir, {recursive: true, force: true})
  })

  it('passes with a valid project', () => {
    setupValidProject(tmpDir)
    const {errors, passes} = validateProject(tmpDir)
    expect(errors).toEqual([])
    expect(passes.length).toBeGreaterThan(0)
  })

  it('fails when .mcp.json is missing', () => {
    setupValidProject(tmpDir)
    rmSync(join(tmpDir, '.mcp.json'))
    const {errors} = validateProject(tmpDir)
    expect(errors).toContainEqual(expect.stringContaining('.mcp.json does not exist'))
  })

  it('fails when .mcp.json has invalid JSON', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, '.mcp.json'), '{invalid')
    const {errors} = validateProject(tmpDir)
    expect(errors).toContainEqual(expect.stringContaining('not valid JSON'))
  })

  it('fails when .mcp.json is missing kobiton server', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, '.mcp.json'), JSON.stringify({mcpServers: {}}))
    const {errors} = validateProject(tmpDir)
    expect(errors).toContainEqual(expect.stringContaining('missing mcpServers.kobiton'))
  })

  it('fails when plugin.json is missing name', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, '.claude-plugin/plugin.json'), JSON.stringify({
      description: 'Test',
      mcpServers: './.mcp.json',
      skills: './skills/'
    }))
    const {errors} = validateProject(tmpDir)
    expect(errors).toContainEqual(expect.stringContaining('missing "name"'))
  })

  it('fails when tool YAML is missing tools array', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, 'tools/devices.yaml'), 'notTools: true')
    const {errors} = validateProject(tmpDir)
    expect(errors).toContainEqual(expect.stringContaining('missing "tools" array'))
  })

  it('fails when tool is missing inputSchema', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, 'tools/devices.yaml'), [
      'tools:',
      '  - name: testTool',
      '    description: A test tool',
    ].join('\n'))
    const {errors} = validateProject(tmpDir)
    expect(errors).toContainEqual(expect.stringContaining('missing "inputSchema"'))
  })

  it('fails when skill is missing frontmatter', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, 'skills/run-automation-suite/SKILL.md'), '## No frontmatter')
    const {errors} = validateProject(tmpDir)
    expect(errors).toContainEqual(expect.stringContaining('missing YAML frontmatter'))
  })

  it('fails when skill frontmatter is missing name', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, 'skills/run-automation-suite/SKILL.md'), [
      '---',
      'description: Test',
      '---',
    ].join('\n'))
    const {errors} = validateProject(tmpDir)
    expect(errors).toContainEqual(expect.stringContaining('frontmatter missing "name"'))
  })

  it('fails when plugin.json references nonexistent path', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, '.claude-plugin/plugin.json'), JSON.stringify({
      name: 'test',
      description: 'Test',
      mcpServers: './nonexistent.json',
      skills: './skills/'
    }))
    const {errors} = validateProject(tmpDir)
    expect(errors).toContainEqual(expect.stringContaining('does not exist'))
  })

  it('passes with OAuth-format .mcp.json', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, '.mcp.json'), JSON.stringify({
      mcpServers: {kobiton: {type: 'http', url: 'https://api.kobiton.com/mcp', oauth: {authServerMetadataUrl: 'https://api.kobiton.com/.well-known/oauth-authorization-server'}}}
    }))
    const {errors} = validateProject(tmpDir)
    expect(errors).toEqual([])
  })

  it('passes with API-key-format .mcp.json', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, '.mcp.json'), JSON.stringify({
      mcpServers: {kobiton: {type: 'http', url: 'https://api.kobiton.com/mcp', headers: {Authorization: '${KOBITON_AUTH}'}}}
    }))
    const {errors} = validateProject(tmpDir)
    expect(errors).toEqual([])
  })

  it('passes with minimal-format .mcp.json', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, '.mcp.json'), JSON.stringify({
      mcpServers: {kobiton: {type: 'http', url: 'https://api.kobiton.com/mcp'}}
    }))
    const {errors} = validateProject(tmpDir)
    expect(errors).toEqual([])
  })

  it('fails when oauth block is missing authServerMetadataUrl', () => {
    setupValidProject(tmpDir)
    writeFileSync(join(tmpDir, '.mcp.json'), JSON.stringify({
      mcpServers: {kobiton: {type: 'http', url: 'https://api.kobiton.com/mcp', oauth: {}}}
    }))
    const {errors} = validateProject(tmpDir)
    expect(errors).toContainEqual(expect.stringContaining('oauth block missing authServerMetadataUrl'))
  })
})
