-- Bot API type definitions for luau-lsp autocomplete

export type BenchFuncs = { [string]: (...any) -> ...any }
export type BenchResults = {
    [string]: {
        Mean: number,
        Median: number,
        Min: number,
        Max: number,
        StdDev: number,
    },
}
export type BenchFuncsWithParams = {
    ParameterGenerator: () -> ...any,
    Functions: { [string]: (profile: any, ...any) -> ...any },
}
export type PlotOptions = {
    offset: Vector2?,
    size: Vector2?,
    scale: number?,
    maxPixelSize: number?,
    isXFunction: boolean?,
    isParametric: boolean?,
    tRange: { number }?,
    color: ({ Color3 } | Color3)?,
    backgroundColor: Color3?,
    showAxes: boolean?,
    axesColor: Color3?,
    showGrid: boolean?,
    gridColor: Color3?,
    gridSpacing: number?,
    showLabels: boolean?,
    labelColor: Color3?,
    labelSpacing: number?,
    labelSize: number?,
    thickness: number?,
    filename: string?,
}
export type GifOptions = {
    loop: number?,
}
export type DrawImageOptions = {
    CombineType: Enum.ImageCombineType?,
    ResamplerMode: Enum.ResamplerMode?,
    PivotPoint: Vector2?,
}
export type TextOptions = {
    Size: number?,
    Font: Enum.Font?,
    Bold: boolean?,
    Align: ("Left" | "Center" | "Right" | Enum.TextXAlignment)?,
    MaxWidth: number?,
    LineHeight: number?,
    StrokeColor: Color3?,
    StrokeThickness: number?,
    StrokeTransparency: number?,
}
export type FromInstanceOptions = {
    Size: Vector2?,
    Camera: (Camera | CFrame)?,
    FieldOfView: number?,
    Background: (Color3 | false)?,
    LightDirection: Vector3?,
}
export type TableStats = {
    arrayS: number,
    hashS: number,
    estSize: number,
    ["#"]: number,
}
export type DiscordButtonStyle = "Primary" | "Secondary" | "Success" | "Danger"
export type DiscordButtonOptions = {
    Label: string?,
    Style: DiscordButtonStyle?,
    Disabled: boolean?,
    OwnerOnly: boolean?,
    label: string?,
    style: DiscordButtonStyle?,
    disabled: boolean?,
    ownerOnly: boolean?,
}
export type DiscordButton = {
    Clicked: RBXScriptSignal<string, string>,
    Event: RBXScriptSignal<string, string>,
    Update: (self: DiscordButton, changes: DiscordButtonOptions) -> DiscordButton,
    Destroy: (self: DiscordButton) -> (),
}

declare extern type Image with
    Size: Vector2
    function SetPixel(self, x: number, y: number, color: Color3, transparency: number, combineType: Enum.ImageCombineType?): ()
    function GetPixel(self, x: number, y: number): (Color3, number)
    function Fill(self, color: Color3, transparency: number): ()
    function DrawRectangle(self, position: Vector2, size: Vector2, color: Color3, transparency: number, combineType: Enum.ImageCombineType?): ()
    function DrawLine(self, startPos: Vector2, endPos: Vector2, color: Color3, transparency: number, combineType: Enum.ImageCombineType?): ()
    function DrawCircle(self, center: Vector2, radius: number, color: Color3, transparency: number, combineType: Enum.ImageCombineType?): ()
    function DrawText(self, text: string, position: Vector2, color: Color3, transparency: number?, options: (number | TextOptions)?): Vector2
    function DrawImageTransformed(self, position: Vector2, scale: Vector2, rotation: number, sourceImage: Image, options: DrawImageOptions?): ()
    function ReadPixelsBuffer(self, position: Vector2, size: Vector2): buffer
    function WritePixelsBuffer(self, position: Vector2, size: Vector2, pixelBuffer: buffer): ()
    function GetBuffer(self): buffer
    function Clone(self): Image
    function Crop(self, position: Vector2, size: Vector2): Image
    function Resize(self, newSize: Vector2): Image
    function Grayscale(self): ()
    function Invert(self): ()
    function AdjustBrightness(self, amount: number): ()
    function AdjustContrast(self, factor: number): ()
    function AdjustSaturation(self, factor: number): ()
    function Tint(self, color: Color3, strength: number): ()
    function Threshold(self, cutoff: number?): ()
    function Blur(self, radius: number): ()
    function GaussianBlur(self, sigma: number): ()
    function Sharpen(self): ()
    function EdgeDetect(self): ()
    function Emboss(self): ()
    function Pixelate(self, blockSize: number): ()
    function Dither(self, levels: number?): ()
    function Posterize(self, levels: number): ()
    function ChromaticAberration(self, offset: number): ()
    function Vignette(self, strength: number): ()
    function FlipHorizontal(self): ()
    function FlipVertical(self): ()
    function Rotate90(self, times: number): ()
    function GetHistogram(self): { r: { number }, g: { number }, b: { number } }
    function AutoLevels(self): ()
    function Compare(self, otherImage: Image): Image
    function AlphaErode(self, threshold: number?): ()
end

-- Simple value globals
declare TIMEOUT: number
declare OUTPUT_LOGS: boolean | "LOG"
declare TIMESTAMP: boolean
declare native: boolean
declare optimize: number
declare GLOBAL: { [any]: any }
declare user: {
    Name: string,
    UserId: number,
}

-- Global functions
declare function colorstring(text: any, color: string?): string
declare function println(message: any, line: number?): number
declare function log(text: any, color: string?, newLine: boolean?): ()
declare function bench(funcs: BenchFuncs | BenchFuncsWithParams, printMode: number?, iterations: number?, delayEveryXIter: number?): BenchResults
declare function ask(prompt: string, contextId: (string | boolean)?): string
declare function setenvkey(key: string, value: any): ()
declare function firesignal(signal: RBXScriptSignal, ...: any): ()
declare function fireevent(instance: Instance, eventName: string, ...: any): ()
declare _native: {
    encodePng: (width: number, height: number, pixels: buffer | string, compression: number?) -> string,
    encodeGif: (width: number, height: number, frames: { buffer | string }, delays: { number }, loops: number?) -> string,
    convolveSeparable: (width: number, height: number, pixels: buffer, kernel: { number }) -> (),
    convolve2D: (width: number, height: number, pixels: buffer, weights: { number }, size: number, divisor: number?, bias: number?) -> (),
    clock: () -> number,
    fnv1a: (data: string) -> string,
}?

-- Extended table library
declare etable: {
    stats: (tbl: { any }) -> TableStats,
    getAllocatedArraySize: (tbl: { any }) -> number,
    getAllocatedHashSize: (tbl: { any }) -> number,
    getTableMemorySize: (tbl: { any }, includeContents: boolean?) -> number,
    getTableSize: (tbl: { any }, isContiguous: boolean?) -> number,
    mergeTables: (inPlace: boolean?, depth: number?, ...{ any }) -> { any },
    mergeTablesWith: (inPlace: boolean?, depth: number?, resolver: (key: any, value1: any, value2: any) -> any, ...{ any }) -> { any },
    accumulateTables: (inPlace: boolean?, ...{ any }) -> { any },
    diffTables: (old: { any }, new: { any }) -> { any },
    syncTable: (src: { any }, target: { any }) -> (),
    mapTable: <K, V, NK, NV>(tbl: { [K]: V }, callback: (key: K, value: V) -> (NK, NV)) -> { [NK]: NV },
    filterTable: <K, V>(tbl: { [K]: V }, predicate: (key: K, value: V) -> boolean, preAllocate: boolean?) -> { [K]: V },
    reduceTable: <K, V, R>(tbl: { [K]: V }, reducer: (accumulator: R, key: K, value: V) -> R, initial: R) -> R,
    groupTableBy: <K, V>(tbl: { [K]: V }, callback: (key: K, value: V) -> any) -> { [any]: { [K]: V } },
    tableToPairs: <K, V>(tbl: { [K]: V }) -> { { key: K, value: V } },
    invertTable: <K, V>(tbl: { [K]: V }) -> { [V]: K },
    tableIntersection: <K, V>(tbl1: { [K]: V }, tbl2: { [K]: V }, matchValue: boolean?) -> { [K]: V },
    tableDifference: <K, V>(tbl1: { [K]: V }, tbl2: { [K]: V }, matchValue: boolean?, symmetric: boolean?) -> { [K]: V },
    removeAll: (tbl: { any }, value: any) -> (),
    extendArraySize: (tbl: { any }, expandSize: number) -> (),
    setArraySize: (tbl: { any }, newSize: number) -> (),
    truncateArray: (array: { any }, newSize: number) -> (),
    concatArrays: (inPlace: boolean?, ...{ any }) -> { any },
    unionArrays: (...{ any }) -> { any },
    flattenArray: (src: { any }, depth: number?) -> { any },
    compactArray: (array: { any }, allocateExactSize: boolean?, isArray: boolean?) -> { any },
    fillWithValue: <T>(array: { T }, value: T, startIndex: number?, endIndex: number?, sourceArray: { T }?) -> (),
    fillWithPattern: <T>(array: { T }, pattern: { T }, startIndex: number?, endIndex: number?) -> (),
    reverseArray: <T>(array: { T }) -> (),
    rotateArray: <T>(array: { T }, shift: number) -> (),
    sliceArray: <T>(array: { T }, startIndex: number?, endIndex: number?) -> { T },
    shuffleArray: <T>(array: { T }) -> (),
    sampleArray: <T>(array: { T }, sampleSize: number, allowDuplicates: boolean?) -> { T },
    sampleValue: <T>(array: { T }, startIndex: number?, endIndex: number?) -> T,
    removeDuplicate: <T>(array: { T }, by: ((value: T) -> any)?) -> { T },
    swapRemove: <T>(array: { T }, index: number) -> T?,
    getAndRemove: <T>(array: { T }, index: number, swap: boolean?) -> T?,
    findAndRemove: <T>(array: { T }, value: T, swap: boolean?) -> boolean,
    zipArray: (...{ any }) -> { { any } },
    unzipArray: (zippedArray: { { any } }) -> ...{ any },
    partitionArray: <T>(array: { T }, size: number) -> { { T } },
    partitionArrayBy: <T>(array: { T }, predicate: (value: T) -> boolean) -> ({ T }, { T }),
    binarySearch: <T>(array: { T }, value: T, comparator: ((a: T, b: T) -> number)?) -> number?,
    getKeys: <K, V>(tbl: { [K]: V }) -> { K },
    getValues: <K, V>(tbl: { [K]: V }) -> { V },
    isContiguousArray: (tbl: { any }) -> boolean,
    isSparseArray: (tbl: { any }) -> boolean,
    isArray: (tbl: { any }) -> boolean,
    isEmpty: (tbl: { any }) -> boolean,
    hasKey: <K>(tbl: { [K]: any }, key: K) -> boolean,
    hasValue: <V>(tbl: { [any]: V }, value: V) -> boolean,
    countOccurences: <V>(tbl: { [any]: V }, value: V) -> number,
    countKeysByType: (tbl: { [any]: any }, keyType: string) -> number,
    countValuesByType: (tbl: { [any]: any }, valueType: string) -> number,
    keysOfValue: <K, V>(tbl: { [K]: V }, targetValue: V) -> { K },
    deepCopy: <T>(tbl: T) -> T,
    deepEqual: (a: any, b: any) -> boolean,
    deepFreeze: (tbl: { any }, layers: number?) -> (),
    findMatch: <K, V>(tbl: { [K]: V }, filter: (key: K, value: V) -> boolean) -> K?,
    every: <K, V>(tbl: { [K]: V }, filter: (key: K, value: V) -> boolean) -> boolean,
    some: <K, V>(tbl: { [K]: V }, filter: (key: K, value: V) -> boolean) -> boolean,
    toSet: <T>(tbl: { T }) -> { [T]: true },
    prettyPrint: (tbl: { any }, indent: number?) -> string,
    toCSV: (rows: { { any } }, delimiter: string?) -> string,
}

-- IO library (bot-specific, overrides standard io)
declare io: {
    write: (...any) -> (),
    read: () -> string,
    writefile: (content: string | buffer, filename: string?) -> (),
    writeimage: (value: Image | { Image } | Instance, filename: string?, options: { [string]: any }?) -> string,
    import: (url: string) -> Instance,
    readfile: (filename: string?) -> (string, string),
    getoutput: () -> string,
    clearoutput: () -> (),
    -- Deprecated: use discord.followUpNext()
    followupnext: () -> (),
}

-- Discord library (buttons also work in the playground)
declare discord: {
    isWeb: boolean,
    button: (options: string | DiscordButtonOptions) -> DiscordButton,
    followUpNext: () -> (),
}

-- Misc library
declare misc: {
    truth: (vars: { any }) -> {},
    calculate: (vars: string, ...string) -> (),
    uwuify: (text: string) -> string,
}

-- Image library (constructor + utilities)
type ImageLib = {
    new: (size: Vector2) -> Image,
    fromPng: (pngData: string) -> Image,
    fromAsset: (id: number | string, size: number?) -> Image,
    fromInstance: (root: Instance, options: FromInstanceOptions?) -> Image,
    measureText: (text: string, options: TextOptions?) -> Vector2,
    toPng: (image: Image, outputLogs: boolean?) -> string,
    toGif: (frames: { Image }, frameDelays: { number }, options: GifOptions?, outputLogs: boolean?) -> string,
    plotFunction: (func: (...number) -> ...any, options: PlotOptions?) -> Image,
}
declare Image: ImageLib

-- PacketSizeCounter library
declare PacketSizeCounter: {
    BaseRemoteOverhead: number,
    RemoteFunctionOverhead: number,
    ClientToServerOverhead: number,
    TypeOverhead: number,
    GetPacketSize: (counterData: {
        RunContext: "Server" | "Client",
        RemoteType: "RemoteEvent" | "RemoteFunction",
        PacketData: { any },
    }) -> number,
    GetDataByteSize: (data: any) -> number,
}

-- Lune-only libraries: using any of these runs the script on Lune instead of Roblox
export type SerdeEncodeFormat = "json" | "jsonc" | "yaml" | "toml"
export type SerdeCompressFormat = "brotli" | "gzip" | "lz4" | "zlib" | "zstd"
export type SerdeHashAlgorithm = "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512" | "sha3-224" | "sha3-256" | "sha3-384" | "sha3-512" | "blake3"
declare serde: {
    encode: (format: SerdeEncodeFormat, value: any, pretty: boolean?) -> string,
    decode: (format: SerdeEncodeFormat, encoded: string | buffer) -> any,
    compress: (format: SerdeCompressFormat, source: string | buffer, level: number?) -> string,
    decompress: (format: SerdeCompressFormat, source: string | buffer) -> string,
    hash: (algorithm: SerdeHashAlgorithm, message: string | buffer) -> string,
    hmac: (algorithm: SerdeHashAlgorithm, message: string | buffer, secret: string | buffer) -> string,
}

export type RegexMatch = {
    start: number,
    finish: number,
    len: number,
    text: string,
}
export type RegexCaptures = {
    get: (self: RegexCaptures, index: number) -> RegexMatch?,
    group: (self: RegexCaptures, name: string) -> RegexMatch?,
    format: (self: RegexCaptures, format: string) -> string,
}
export type Regex = {
    isMatch: (self: Regex, text: string) -> boolean,
    find: (self: Regex, text: string) -> RegexMatch?,
    captures: (self: Regex, text: string) -> RegexCaptures?,
    split: (self: Regex, text: string) -> { string },
    replace: (self: Regex, haystack: string, replacer: string) -> string,
    replaceAll: (self: Regex, haystack: string, replacer: string) -> string,
}
declare regex: {
    new: (pattern: string) -> Regex,
}

export type LuneDateTimeValues = {
    year: number,
    month: number,
    day: number,
    hour: number,
    minute: number,
    second: number,
    millisecond: number?,
}
export type LuneDateTime = {
    unixTimestamp: number,
    unixTimestampMillis: number,
    toIsoDate: (self: LuneDateTime) -> string,
    toRfc3339: (self: LuneDateTime) -> string,
    toRfc2822: (self: LuneDateTime) -> string,
    formatLocalTime: (self: LuneDateTime, formatString: string?, locale: string?) -> string,
    formatUniversalTime: (self: LuneDateTime, formatString: string?, locale: string?) -> string,
    toLocalTime: (self: LuneDateTime) -> LuneDateTimeValues,
    toUniversalTime: (self: LuneDateTime) -> LuneDateTimeValues,
}
declare datetime: {
    now: () -> LuneDateTime,
    fromUnixTimestamp: (unixTimestamp: number) -> LuneDateTime,
    fromUniversalTime: (values: LuneDateTimeValues) -> LuneDateTime,
    fromLocalTime: (values: LuneDateTimeValues) -> LuneDateTime,
    fromIsoDate: (isoDate: string) -> LuneDateTime,
    fromRfc3339: (rfc3339Date: string) -> LuneDateTime,
    fromRfc2822: (rfc2822Date: string) -> LuneDateTime,
}
