import { faker } from '@faker-js/faker'
import type { MediaPatchRevision } from '../../patch/types.js'
import { nanoid } from '../../nanoid.js'
import type {
  AddOperation,
  CopyOperation,
  MoveOperation,
  Operation,
  RemoveOperation,
  ReplaceOperation,
  TestOperation,
} from '@api-client/json/patch/types.js'

export class Patch {
  /**
   * Generates a random invitation object.
   * @param init Optional values to be present in the object.
   * @returns Random invitation
   */
  mediaPatchRevision(init: Partial<MediaPatchRevision> = {}): MediaPatchRevision {
    const {
      id = nanoid(),
      timestamp = faker.date.recent().getTime(),
      patch = this.patches(faker.number.int({ min: 1, max: 5 })),
      version = faker.number.int({ min: 1, max: 1000 }),
      revert = [],
      newVersion = version + 1,
    } = init
    return {
      id,
      timestamp,
      patch,
      version,
      revert,
      newVersion,
    } as MediaPatchRevision
  }

  /**
   * Generates a list of random invitation objects.
   * @param size Number of invitations to generate. Default is 25.
   * @returns List of random invitations
   */
  mediaPatchRevisions(size = 25): MediaPatchRevision[] {
    const result = []
    for (let i = 0; i < size; i++) {
      result.push(this.mediaPatchRevision())
    }
    return result
  }

  /**
   * Generates a random JSON patch operation.
   * @returns A random patch information.
   */
  patch(): Operation {
    const type = faker.helpers.arrayElement(['add', 'remove', 'replace', 'move', 'copy', 'test'] as const)
    switch (type) {
      case 'add':
        return this.addPatch()
      case 'remove':
        return this.removePatch()
      case 'replace':
        return this.replacePatch()
      case 'move':
        return this.movePatch()
      case 'copy':
        return this.copyPatch()
      case 'test':
        return this.testPatch()
    }
  }

  patches(size = 25): Operation[] {
    const result = []
    for (let i = 0; i < size; i++) {
      result.push(this.patch())
    }
    return result
  }

  addPatch(): AddOperation {
    return {
      op: 'add',
      path: `/${nanoid()}`,
      value: faker.lorem.word(),
    }
  }

  removePatch(): RemoveOperation {
    return {
      op: 'remove',
      path: `/${nanoid()}`,
    } as RemoveOperation
  }

  replacePatch(): ReplaceOperation {
    return {
      op: 'replace',
      path: `/${nanoid()}`,
      value: faker.lorem.word(),
    } as ReplaceOperation
  }

  movePatch(): MoveOperation {
    return {
      op: 'move',
      from: `/${nanoid()}`,
      path: `/${nanoid()}`,
    } as MoveOperation
  }

  copyPatch(): CopyOperation {
    return {
      op: 'copy',
      from: `/${nanoid()}`,
      path: `/${nanoid()}`,
    } as CopyOperation
  }

  testPatch(): TestOperation {
    return {
      op: 'test',
      path: `/${nanoid()}`,
      value: faker.lorem.word(),
    } as TestOperation
  }
}
