import { describe, it, expect } from 'vitest'
import { findRuleCandidates } from '../extractor.js'
import { isValidRuleAction } from '../rules.js'

describe('findRuleCandidates', () => {
  describe('bounded capture', () => {
    it('captures short simple corrections', () => {
      const out = findRuleCandidates(`don't use mocks in integration tests`)
      expect(out).toHaveLength(1)
      expect(out[0].action).toBe(`don't use mocks in integration tests`)
    })

    it('stops at sentence-end punctuation', () => {
      const text = `don't spend questions refining details of a project. We already discussed it.`
      const out = findRuleCandidates(text)
      expect(out).toHaveLength(1)
      expect(out[0].action).toBe(`don't spend questions refining details of a project`)
    })

    it('rejects unbounded skill-paste content (the 0d1655ba bug)', () => {
      const paste =
        `**Invoke relevant or requested skills BEFORE any response or action.** ` +
        `Even a 1% chance a skill might apply means that you should invoke the skill to check. ` +
        `always use TDD" and a skill says "always use TDD," follow the user's instructions. ` +
        `\\n\\n## How to Access Skills\\n\\n**In SANDCODE:** Use the \`Skill\` tool.`
      const out = findRuleCandidates(paste)
      expect(out).toHaveLength(0)
    })

    it('rejects messages containing code fences', () => {
      const text = 'please always use \n```typescript\nconst x = 1\n```\nthis pattern'
      const out = findRuleCandidates(text)
      expect(out).toHaveLength(0)
    })

    it('rejects messages containing XML-ish tags', () => {
      const text = `always do what <EXTREMELY_IMPORTANT> block says`
      const out = findRuleCandidates(text)
      expect(out).toHaveLength(0)
    })

    it('rejects messages containing JSON-escaped content', () => {
      const text = `always invoke the skill.\\n\\nFollow it directly.`
      const out = findRuleCandidates(text)
      expect(out).toHaveLength(0)
    })

    it('rejects captures longer than 200 chars', () => {
      const longTail = 'a '.repeat(150) // > 200 chars
      const out = findRuleCandidates(`don't ${longTail}`)
      expect(out).toHaveLength(0)
    })

    it('rejects messages over 2000 chars outright', () => {
      const big = `don't do it. ${'x '.repeat(2000)}`
      const out = findRuleCandidates(big)
      expect(out).toHaveLength(0)
    })
  })

  describe('polarity preservation', () => {
    it(`keeps "don't" in the stored action`, () => {
      const out = findRuleCandidates(`don't spend questions refining details`)
      expect(out[0].action.startsWith(`don't `)).toBe(true)
    })

    it(`keeps "never" in the stored action`, () => {
      const out = findRuleCandidates(`never push without running tests`)
      expect(out[0].action.startsWith('never ')).toBe(true)
    })

    it(`rewrites "stop doing X" as "don't X"`, () => {
      const out = findRuleCandidates(`stop doing full repo rebuilds on every change`)
      expect(out[0].action).toBe(`don't full repo rebuilds on every change`)
    })

    it(`keeps "always" in the stored action`, () => {
      const out = findRuleCandidates(`always run typecheck before commit`)
      expect(out[0].action).toBe('always run typecheck before commit')
    })

    it(`captures both sides of "use X instead of Y"`, () => {
      const out = findRuleCandidates(`use bun instead of npm for this repo`)
      expect(out[0].action).toBe('use bun instead of npm for this repo')
    })
  })

  describe('confidence floor', () => {
    it('assigns initial correction confidence below 0.5 so rules need reinforcement', () => {
      const out = findRuleCandidates(`don't touch generated files`)
      expect(out[0].confidence).toBeLessThan(0.5)
      expect(out[0].confidence).toBeGreaterThan(0)
    })
  })

  describe('text length floor', () => {
    it('ignores very short messages', () => {
      expect(findRuleCandidates('no')).toHaveLength(0)
      expect(findRuleCandidates("don't")).toHaveLength(0)
    })
  })
})

describe('isValidRuleAction', () => {
  it('accepts simple rule actions', () => {
    expect(isValidRuleAction(`don't use mocks in integration tests`)).toBe(true)
    expect(isValidRuleAction('always run typecheck')).toBe(true)
    expect(isValidRuleAction('use bun instead of npm')).toBe(true)
  })

  it('rejects actions with quote marks', () => {
    expect(isValidRuleAction(`always say "hello" first`)).toBe(false)
    expect(isValidRuleAction('use the `Skill` tool')).toBe(false)
  })

  it('rejects actions with markdown/XML markup', () => {
    expect(isValidRuleAction('always use <EXTREMELY_IMPORTANT>')).toBe(false)
    expect(isValidRuleAction('never do {foo: bar}')).toBe(false)
  })

  it('rejects actions with newlines', () => {
    expect(isValidRuleAction('always\nrun typecheck')).toBe(false)
  })

  it('rejects actions with URLs', () => {
    expect(isValidRuleAction('always check https://example.com docs')).toBe(false)
  })

  it('rejects too-short or too-long actions', () => {
    expect(isValidRuleAction('do')).toBe(false)
    expect(isValidRuleAction('x'.repeat(201))).toBe(false)
  })

  it('rejects actions that look like skill/prompt fragments', () => {
    expect(
      isValidRuleAction(`use TDD and a skill says always use TDD follow the user instructions`),
    ).toBe(false)
  })
})
