import type { InvoiceProvider } from './port.js'
import { toInvoiceError } from './errors.js'
import type { DocumentSpec, InvoiceDocumentResult } from './types.js'

type IssueBehavior = (
  credential: unknown,
  spec: DocumentSpec,
  callCount: number,
) => Promise<InvoiceDocumentResult> | InvoiceDocumentResult

export class MockInvoiceProvider implements InvoiceProvider {
  readonly calls: Array<{ credential: unknown; spec: DocumentSpec }> = []
  readonly issueCallCount: { current: number } = { current: 0 }
  readonly #behavior: IssueBehavior

  constructor(behavior?: IssueBehavior) {
    this.#behavior =
      behavior ??
      ((_, spec, callCount) => ({
        documentId: `mock-doc-${callCount}`,
        documentNumber: `INV-${callCount}`,
        documentUrl: `https://example.test/invoices/${encodeURIComponent(spec.idempotencyKey)}`,
      }))
  }

  async issue(credential: unknown, spec: DocumentSpec): Promise<InvoiceDocumentResult> {
    this.calls.push({ credential, spec })
    this.issueCallCount.current += 1
    try {
      return await this.#behavior(credential, spec, this.issueCallCount.current)
    } catch (error) {
      throw { ...toInvoiceError(error), issued: 'no' }
    }
  }
}
