import { Effect, Schema, Struct, Context } from "effect"
import { NonNegativeInt, withStatics } from "@/util/schema"
import { zod } from "@/util/effect-zod"
import type { MessageID, SessionID } from "@/session/schema"

export const Patch = Schema.Struct({
  hash: Schema.String,
  files: Schema.mutable(Schema.Array(Schema.String)),
}).pipe(withStatics((s) => ({ zod: zod(s) })))
export type Patch = typeof Patch.Type

export const FileDiff = Schema.Struct({
  file: Schema.String,
  patch: Schema.String,
  additions: NonNegativeInt,
  deletions: NonNegativeInt,
  status: Schema.optional(Schema.Literals(["added", "deleted", "modified"])),
})
  .annotate({ identifier: "SnapshotFileDiff" })
  .pipe(withStatics((s) => ({ zod: zod(s) })))
export type FileDiff = typeof FileDiff.Type

export const SummaryFileDiff = FileDiff.mapFields(Struct.omit(["patch"]))
  .annotate({ identifier: "SnapshotSummaryFileDiff" })
  .pipe(withStatics((s) => ({ zod: zod(s) })))
export type SummaryFileDiff = typeof SummaryFileDiff.Type

export interface Interface {
  readonly init: () => Effect.Effect<void>
  readonly cleanup: () => Effect.Effect<void>
  readonly track: (opts?: { sessionID?: SessionID; messageID?: MessageID }) => Effect.Effect<string | undefined>
  readonly patch: (hash: string) => Effect.Effect<Patch>
  readonly restore: (snapshot: string) => Effect.Effect<void>
  readonly revert: (patches: Patch[]) => Effect.Effect<void>
  readonly diff: (hash: string) => Effect.Effect<string>
  readonly diffFull: (from: string, to: string) => Effect.Effect<FileDiff[]>
}

export class Service extends Context.Service<Service, Interface>()("@saeeol/Snapshot") {}

export const MAX_DIFF_SIZE = 256 * 1024
export const cache = new Map<string, Promise<FileDiff[]>>()
export const maxCache = 100
