/** Part schemas + ToolState + Input schemas — extracted from message-v2.ts */

import { SessionID, MessageID, PartID } from "../core/schema"
import { LSP } from "@/lsp/lsp"
import { Snapshot } from "@/snapshot"
import { Effect, Schema, Types } from "effect"
import { zod } from "@/util/effect-zod"
import { NonNegativeInt, withStatics } from "@/util/schema"
import { APIError } from "./message-errors"
import { ModelID, ProviderID } from "@/provider/schema"
import z from "zod"

const partBase = { id: PartID, sessionID: SessionID, messageID: MessageID }

export const SnapshotPart = Schema.Struct({ ...partBase, type: Schema.Literal("snapshot"), snapshot: Schema.String }).annotate({ identifier: "SnapshotPart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type SnapshotPart = Types.DeepMutable<Schema.Schema.Type<typeof SnapshotPart>>
export const PatchPart = Schema.Struct({ ...partBase, type: Schema.Literal("patch"), hash: Schema.String, files: Schema.Array(Schema.String) }).annotate({ identifier: "PatchPart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type PatchPart = Types.DeepMutable<Schema.Schema.Type<typeof PatchPart>>
export const TextPart = Schema.Struct({
  ...partBase, type: Schema.Literal("text"), text: Schema.String, synthetic: Schema.optional(Schema.Boolean), ignored: Schema.optional(Schema.Boolean),
  time: Schema.optional(Schema.Struct({ start: NonNegativeInt, end: Schema.optional(NonNegativeInt) })), metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
}).annotate({ identifier: "TextPart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type TextPart = Types.DeepMutable<Schema.Schema.Type<typeof TextPart>>
export const ReasoningPart = Schema.Struct({
  ...partBase, type: Schema.Literal("reasoning"), text: Schema.String, metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
  time: Schema.Struct({ start: NonNegativeInt, end: Schema.optional(NonNegativeInt) }),
}).annotate({ identifier: "ReasoningPart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type ReasoningPart = Types.DeepMutable<Schema.Schema.Type<typeof ReasoningPart>>

const filePartSourceBase = { text: Schema.Struct({ value: Schema.String, start: NonNegativeInt, end: NonNegativeInt }).annotate({ identifier: "FilePartSourceText" }) }
export const FileSource = Schema.Struct({ ...filePartSourceBase, type: Schema.Literal("file"), path: Schema.String }).annotate({ identifier: "FileSource" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export const SymbolSource = Schema.Struct({ ...filePartSourceBase, type: Schema.Literal("symbol"), path: Schema.String, range: LSP.Range, name: Schema.String, kind: NonNegativeInt }).annotate({ identifier: "SymbolSource" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export const ResourceSource = Schema.Struct({ ...filePartSourceBase, type: Schema.Literal("resource"), clientName: Schema.String, uri: Schema.String }).annotate({ identifier: "ResourceSource" }).pipe(withStatics((s) => ({ zod: zod(s) })))

const _FilePartSource = Schema.Union([FileSource, SymbolSource, ResourceSource]).annotate({ discriminator: "type", identifier: "FilePartSource" })
export const FilePartSource = Object.assign(_FilePartSource, { zod: zod(_FilePartSource) })

export const FilePart = Schema.Struct({ ...partBase, type: Schema.Literal("file"), mime: Schema.String, filename: Schema.optional(Schema.String), url: Schema.String, source: Schema.optional(_FilePartSource) }).annotate({ identifier: "FilePart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type FilePart = Types.DeepMutable<Schema.Schema.Type<typeof FilePart>>
export const AgentPart = Schema.Struct({ ...partBase, type: Schema.Literal("agent"), name: Schema.String, source: Schema.optional(Schema.Struct({ value: Schema.String, start: NonNegativeInt, end: NonNegativeInt })) }).annotate({ identifier: "AgentPart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type AgentPart = Types.DeepMutable<Schema.Schema.Type<typeof AgentPart>>
export const CompactionPart = Schema.Struct({ ...partBase, type: Schema.Literal("compaction"), auto: Schema.Boolean, overflow: Schema.optional(Schema.Boolean), tail_start_id: Schema.optional(MessageID) }).annotate({ identifier: "CompactionPart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type CompactionPart = Types.DeepMutable<Schema.Schema.Type<typeof CompactionPart>>
export const SubtaskPart = Schema.Struct({ ...partBase, type: Schema.Literal("subtask"), prompt: Schema.String, description: Schema.String, agent: Schema.String, model: Schema.optional(Schema.Struct({ providerID: ProviderID, modelID: ModelID })), command: Schema.optional(Schema.String) }).annotate({ identifier: "SubtaskPart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type SubtaskPart = Types.DeepMutable<Schema.Schema.Type<typeof SubtaskPart>>
export const RetryPart = Schema.Struct({ ...partBase, type: Schema.Literal("retry"), attempt: NonNegativeInt, error: APIError.EffectSchema, time: Schema.Struct({ created: NonNegativeInt }) }).annotate({ identifier: "RetryPart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type RetryPart = Omit<Types.DeepMutable<Schema.Schema.Type<typeof RetryPart>>, "error"> & { error: APIError }
export const StepStartPart = Schema.Struct({ ...partBase, type: Schema.Literal("step-start"), snapshot: Schema.optional(Schema.String) }).annotate({ identifier: "StepStartPart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type StepStartPart = Types.DeepMutable<Schema.Schema.Type<typeof StepStartPart>>
export const StepFinishPart = Schema.Struct({
  ...partBase, type: Schema.Literal("step-finish"), reason: Schema.String, snapshot: Schema.optional(Schema.String), cost: Schema.Finite,
  tokens: Schema.Struct({ total: Schema.optional(NonNegativeInt), input: NonNegativeInt, output: NonNegativeInt, reasoning: NonNegativeInt, cache: Schema.Struct({ read: NonNegativeInt, write: NonNegativeInt }) }),
}).annotate({ identifier: "StepFinishPart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type StepFinishPart = Types.DeepMutable<Schema.Schema.Type<typeof StepFinishPart>>

export const ToolStatePending = Schema.Struct({ status: Schema.Literal("pending"), input: Schema.Record(Schema.String, Schema.Any), raw: Schema.String }).annotate({ identifier: "ToolStatePending" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type ToolStatePending = Types.DeepMutable<Schema.Schema.Type<typeof ToolStatePending>>
export const ToolStateRunning = Schema.Struct({ status: Schema.Literal("running"), input: Schema.Record(Schema.String, Schema.Any), title: Schema.optional(Schema.String), metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)), time: Schema.Struct({ start: NonNegativeInt }) }).annotate({ identifier: "ToolStateRunning" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type ToolStateRunning = Types.DeepMutable<Schema.Schema.Type<typeof ToolStateRunning>>
export const ToolStateCompleted = Schema.Struct({
  status: Schema.Literal("completed"), input: Schema.Record(Schema.String, Schema.Any), output: Schema.String, title: Schema.String, metadata: Schema.Record(Schema.String, Schema.Any),
  time: Schema.Struct({ start: NonNegativeInt, end: NonNegativeInt, compacted: Schema.optional(NonNegativeInt) }), attachments: Schema.optional(Schema.Array(FilePart)),
}).annotate({ identifier: "ToolStateCompleted" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type ToolStateCompleted = Types.DeepMutable<Schema.Schema.Type<typeof ToolStateCompleted>>
export function truncateToolOutput(text: string, maxChars?: number) { if (!maxChars || text.length <= maxChars) return text; return `${text.slice(0, maxChars)}\n[Tool output truncated for compaction: omitted ${text.length - maxChars} chars]` }
export const ToolStateError = Schema.Struct({ status: Schema.Literal("error"), input: Schema.Record(Schema.String, Schema.Any), error: Schema.String, metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)), time: Schema.Struct({ start: NonNegativeInt, end: NonNegativeInt }) }).annotate({ identifier: "ToolStateError" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type ToolStateError = Types.DeepMutable<Schema.Schema.Type<typeof ToolStateError>>

const _ToolState = Schema.Union([ToolStatePending, ToolStateRunning, ToolStateCompleted, ToolStateError]).annotate({ discriminator: "status", identifier: "ToolState" })
export const ToolState = Object.assign(_ToolState, { zod: zod(_ToolState) as unknown as z.ZodType<ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError> })
export type ToolState = ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError
export const ToolPart = Schema.Struct({ ...partBase, type: Schema.Literal("tool"), callID: Schema.String, tool: Schema.String, state: _ToolState, metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)) }).annotate({ identifier: "ToolPart" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type ToolPart = Omit<Types.DeepMutable<Schema.Schema.Type<typeof ToolPart>>, "state"> & { state: ToolState }

// Input variants (without ambient IDs)
export const TextPartInput = Schema.Struct({ id: Schema.optional(PartID), type: Schema.Literal("text"), text: Schema.String, synthetic: Schema.optional(Schema.Boolean), ignored: Schema.optional(Schema.Boolean), time: Schema.optional(Schema.Struct({ start: NonNegativeInt, end: Schema.optional(NonNegativeInt) })), metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)) }).annotate({ identifier: "TextPartInput" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type TextPartInput = Types.DeepMutable<Schema.Schema.Type<typeof TextPartInput>>
export const FilePartInput = Schema.Struct({ id: Schema.optional(PartID), type: Schema.Literal("file"), mime: Schema.String, filename: Schema.optional(Schema.String), url: Schema.String, source: Schema.optional(_FilePartSource) }).annotate({ identifier: "FilePartInput" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type FilePartInput = Types.DeepMutable<Schema.Schema.Type<typeof FilePartInput>>
export const AgentPartInput = Schema.Struct({ id: Schema.optional(PartID), type: Schema.Literal("agent"), name: Schema.String, source: Schema.optional(Schema.Struct({ value: Schema.String, start: NonNegativeInt, end: NonNegativeInt })) }).annotate({ identifier: "AgentPartInput" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type AgentPartInput = Types.DeepMutable<Schema.Schema.Type<typeof AgentPartInput>>
export const SubtaskPartInput = Schema.Struct({ id: Schema.optional(PartID), type: Schema.Literal("subtask"), prompt: Schema.String, description: Schema.String, agent: Schema.String, model: Schema.optional(Schema.Struct({ providerID: ProviderID, modelID: ModelID })), command: Schema.optional(Schema.String) }).annotate({ identifier: "SubtaskPartInput" }).pipe(withStatics((s) => ({ zod: zod(s) })))
export type SubtaskPartInput = Types.DeepMutable<Schema.Schema.Type<typeof SubtaskPartInput>>
const _Part = Schema.Union([TextPart, SubtaskPart, ReasoningPart, FilePart, ToolPart, StepStartPart, StepFinishPart, SnapshotPart, PatchPart, AgentPart, RetryPart, CompactionPart]).annotate({ discriminator: "type", identifier: "Part" })
export const Part = Object.assign(_Part, { zod: zod(_Part) as unknown as z.ZodType<TextPart | SubtaskPart | ReasoningPart | FilePart | ToolPart | StepStartPart | StepFinishPart | SnapshotPart | PatchPart | AgentPart | RetryPart | CompactionPart> })
export type Part = TextPart | SubtaskPart | ReasoningPart | FilePart | ToolPart | StepStartPart | StepFinishPart | SnapshotPart | PatchPart | AgentPart | RetryPart | CompactionPart
export { _Part }