import { Assert } from '@japa/assert'
import sinonLib, { type MatchPartialArguments, type SinonSpy } from 'sinon'

declare module '@japa/assert' {
  interface Assert {
    sinon: typeof sinonLib

    /**
     * Asserts that a spy was called at least once with the provided arguments.
     * Can be used for partial matching, Sinon only checks the provided arguments against actual arguments,
     * so a call that received the provided arguments (in the same spots) and possibly others as well will return true.
     * @param args
     */
    calledWith(target: SinonSpy, ...args: MatchPartialArguments<unknown>[]): void
  }
}

Assert.macro('calledWith', function (this: Assert, target: SinonSpy, ...args: MatchPartialArguments<unknown>[]) {
  this.incrementAssertionsCount()
  const result = target.calledWith(...args)
  const type = target.constructor.name
  this.evaluate(result, `expected "${type}" to be called with arguments`, {
    thisObject: target,
    expected: !!result,
    actual: !result,
    showDiff: false,
    operator: 'ok',
  })
})
