import { Identifier } from "@/id/id"
import { NonNegativeInt, withStatics } from "@/util/schema"
import * as DateTime from "effect/DateTime"
import { Schema } from "effect"

export const SessionEventID = Schema.String.pipe(
  Schema.brand("Session.Event.ID"),
  withStatics((s) => ({
    create: () => s.make(Identifier.create("evt", "ascending")),
  })),
)
export type SessionEventID = Schema.Schema.Type<typeof SessionEventID>

export type Stamp = Schema.Schema.Type<typeof Schema.DateTimeUtc>

export type BaseInput = {
  id?: SessionEventID
  metadata?: Record<string, unknown>
  timestamp?: Stamp
}

export const Base = {
  id: SessionEventID,
  metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
  timestamp: Schema.DateTimeUtc,
}

export class Source extends Schema.Class<Source>("Session.Event.Source")({
  start: NonNegativeInt,
  end: NonNegativeInt,
  text: Schema.String,
}) {}

export class FileAttachment extends Schema.Class<FileAttachment>("Session.Event.FileAttachment")({
  uri: Schema.String,
  mime: Schema.String,
  name: Schema.String.pipe(Schema.optional),
  description: Schema.String.pipe(Schema.optional),
  source: Source.pipe(Schema.optional),
}) {
  static create(input: FileAttachment) {
    return new FileAttachment({ uri: input.uri, mime: input.mime, name: input.name, description: input.description, source: input.source })
  }
}

export class AgentAttachment extends Schema.Class<AgentAttachment>("Session.Event.AgentAttachment")({
  name: Schema.String,
  source: Source.pipe(Schema.optional),
}) {}

export class RetryError extends Schema.Class<RetryError>("Session.Event.Retry.Error")({
  message: Schema.String,
  statusCode: NonNegativeInt.pipe(Schema.optional),
  isRetryable: Schema.Boolean,
  responseHeaders: Schema.Record(Schema.String, Schema.String).pipe(Schema.optional),
  responseBody: Schema.String.pipe(Schema.optional),
  metadata: Schema.Record(Schema.String, Schema.String).pipe(Schema.optional),
}) {}

export class Prompt extends Schema.Class<Prompt>("Session.Event.Prompt")({
  ...Base,
  type: Schema.Literal("prompt"),
  text: Schema.String,
  files: Schema.Array(FileAttachment).pipe(Schema.optional),
  agents: Schema.Array(AgentAttachment).pipe(Schema.optional),
}) {
  static create(input: BaseInput & { text: string; files?: FileAttachment[]; agents?: AgentAttachment[] }) {
    return new Prompt({ id: input.id ?? SessionEventID.create(), type: "prompt", timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()), metadata: input.metadata, text: input.text, files: input.files, agents: input.agents })
  }
}

export class Synthetic extends Schema.Class<Synthetic>("Session.Event.Synthetic")({
  ...Base,
  type: Schema.Literal("synthetic"),
  text: Schema.String,
}) {
  static create(input: BaseInput & { text: string }) {
    return new Synthetic({ id: input.id ?? SessionEventID.create(), type: "synthetic", timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()), metadata: input.metadata, text: input.text })
  }
}

export class Retried extends Schema.Class<Retried>("Session.Event.Retried")({
  ...Base,
  type: Schema.Literal("retried"),
  attempt: NonNegativeInt,
  error: RetryError,
}) {
  static create(input: BaseInput & { attempt: number; error: RetryError }) {
    return new Retried({ id: input.id ?? SessionEventID.create(), type: "retried", timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()), metadata: input.metadata, attempt: input.attempt, error: input.error })
  }
}

export class Compacted extends Schema.Class<Compacted>("Session.Event.Compacted")({
  ...Base,
  type: Schema.Literal("compacted"),
  auto: Schema.Boolean,
  overflow: Schema.Boolean.pipe(Schema.optional),
}) {
  static create(input: BaseInput & { auto: boolean; overflow?: boolean }) {
    return new Compacted({ id: input.id ?? SessionEventID.create(), type: "compacted", timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()), metadata: input.metadata, auto: input.auto, overflow: input.overflow })
  }
}
