import { Schema, Effect, Layer, Context } from 'effect';
import { BadArgument, SystemError, PlatformError } from '@effect/platform/Error';
import { ParseError } from 'effect/ParseResult';

declare const DatabaseError_base: Schema.TaggedErrorClass<DatabaseError, "DatabaseError", {
    readonly _tag: Schema.tag<"DatabaseError">;
} & {
    message: typeof Schema.String;
    cause: typeof Schema.Unknown;
}>;
declare class DatabaseError extends DatabaseError_base {
}
declare const JsonError_base: Schema.TaggedErrorClass<JsonError, "JsonError", {
    readonly _tag: Schema.tag<"JsonError">;
} & {
    message: typeof Schema.String;
    cause: typeof Schema.Unknown;
}>;
declare class JsonError extends JsonError_base {
}

/** Trim left space from a string */
type TrimLeft<T extends string> = T extends ` ${infer R}` ? TrimLeft<R> : T;
/** Trim right space from a string */
type TrimRight<T extends string> = T extends `${infer R} ` ? TrimRight<R> : T;
/** Trim left and right space from a string */
type Trim<T extends string> = TrimLeft<TrimRight<T>>;
type TypeMap = {
    string: string;
    number: number;
    boolean: boolean;
    date: Date;
    any: any;
    unknown: unknown;
    null: null;
    bigint: bigint;
};
/**
 * Parse a type string into a TypeScript type.
 */
type ParseType<T extends string> = Trim<T> extends keyof TypeMap ? TypeMap[Trim<T>] : Trim<T> extends `array<${infer Inner}>` ? ParseType<Inner>[] : Trim<T> extends `record<${infer K},${infer V}>` ? Record<ParseType<K> & (string | number | symbol), ParseType<V>> : ParseTypeName<T>;
type CleanKey<T extends string> = T extends `++${infer K}` | `&${infer K}` | `@${infer K}` | `[${infer K}]` | `*${infer K}` ? K : T;
/**
 * Maps a type name string to its corresponding TypeScript type.
 * If the type name exists in TypeMap, returns the mapped type; otherwise returns 'any'.
 */
type ParseTypeName<T extends string> = T extends keyof TypeMap ? TypeMap[T] : any;
/**
 * Verify if the key of a field has the `*` prefix
 */
type IsMultiEntry<T extends string> = T extends `*${string}` ? true : false;
type ParseField<T extends string> = T extends `${infer Key}:${infer TypeName}` ? {
    [K in CleanKey<Trim<Key>>]: IsMultiEntry<Trim<Key>> extends true ? ParseType<TypeName>[] : ParseType<TypeName>;
} : {
    [K in CleanKey<Trim<T>>]: IsMultiEntry<Trim<T>> extends true ? string[] : string;
};
type Split<S extends string, D extends string> = string extends S ? string[] : S extends "" ? [] : S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] : [S];
/**
 * Convert a union type to an intersection type.
 * This is a utility type that is useful for combining multiple types into one.
 */
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
/**
 * Parse a JSON Schema string into a JSON Schema object.
 * This is a utility type that is useful for converting
 * a JSON Schema string into a JSON Schema object.
 */
type ParseSchemaString<T extends string> = UnionToIntersection<ParseField<Split<T, ";">[number]>>;
/**
 * Represents a standard schema compliant with @standard-schema/spec.
 */
interface StandardSchemaV1<Input = any, Output = any> {
    readonly "~standard": {
        readonly version: 1;
        readonly vendor: string;
        readonly validate: (value: unknown) => StandardSchemaV1.Result<Output> | Promise<StandardSchemaV1.Result<Output>>;
        readonly types?: {
            readonly input: Input;
            readonly output: Output;
        };
    };
}
declare namespace StandardSchemaV1 {
    type Result<Output> = SuccessResult<Output> | FailureResult;
    interface SuccessResult<Output> {
        readonly value: Output;
        readonly issues?: undefined;
    }
    interface FailureResult {
        readonly issues: ReadonlyArray<Issue>;
    }
    interface Issue {
        readonly message: string;
        readonly path?: ReadonlyArray<PropertyKey>;
    }
}
/**
 * Represents a schema definition, which can be either
 * an Effect Schema.Struct, a standard-schema compliant validator, or a string.
 */
type SchemaOrString = string | Schema.Schema<any, any> | StandardSchemaV1<any, any>;

type ComparisonOperator<T> = {
    _tag: "eq";
    value: T;
} | {
    _tag: "ne";
    value: T;
} | {
    _tag: "gt";
    value: T;
} | {
    _tag: "gte";
    value: T;
} | {
    _tag: "lt";
    value: T;
} | {
    _tag: "lte";
    value: T;
} | {
    _tag: "in";
    values: T[];
} | {
    _tag: "nin";
    values: T[];
} | {
    _tag: "startsWith";
    value: string;
} | {
    _tag: "regex";
    pattern: string;
    flags?: string | undefined;
};
type LogicalOperator<Doc> = {
    _tag: "and";
    filters: FilterExpression<Doc>[];
} | {
    _tag: "or";
    filters: FilterExpression<Doc>[];
} | {
    _tag: "not";
    filter: FilterExpression<Doc>;
};
type FilterExpression<Doc> = {
    [K in keyof Doc]?: Doc[K] | ComparisonOperator<Doc[K]>;
} | LogicalOperator<Doc>;

/**
 * A filter object used to specify criteria for querying documents in a collection.
 */
type Filter<Doc> = FilterExpression<Doc>;
interface OrderBy<Doc> {
    /**
     * The field to order by.
     * This should be a key of the document type `Doc`.
     */
    field: keyof Doc;
    /**
     * The order direction, either ascending (`asc`) or descending (`desc`).
     */
    order: "asc" | "desc";
}
interface QueryOptions<Doc> {
    /**
     * The filter criteria to apply to the query.
     *
     * @example
     * db.collections.user.find({
     *    where: {age: 20}
     * })
     */
    where: Filter<Doc>;
    /**
     * The ordering criteria to apply to the query.
     *
     * @example
     * db.collections.user.find({
     *    where: { age: 20 }
     *    order_by: { field: 'age', order: 'asc' }
     * })
     */
    order_by?: OrderBy<Doc>;
    /**
     * The number of documents to skip in the query results.
     */
    skip?: number;
    /**
     * The maximum number of documents to return in the query results.
     */
    limit?: number;
}
/**
 * The result of a batch operation.
 */
interface BatchResult {
    /** The number of successful operations. */
    success: number;
    /** A list of failed operations with their index or ID and error message. */
    failures: Array<{
        index?: number;
        id?: string;
        error: string;
    }>;
}
/**
 * Represents batch operations on a collection, returning `Effect` computations.
 */
interface BatchOperationsEffect<Doc> {
    /**
     * Inserts multiple documents into the collection.
     * @param docs The array of documents to insert.
     * @returns An `Effect` that resolves to a `BatchResult`.
     */
    insert: (docs: Doc[]) => Effect.Effect<BatchResult, DatabaseError>;
    /**
     * Deletes multiple documents matching the filter.
     * @param filter The filter to match documents for deletion.
     * @returns An `Effect` that resolves to a `BatchResult`.
     */
    delete: (filter: Filter<Doc>) => Effect.Effect<BatchResult, DatabaseError>;
    /**
     * Updates multiple documents matching the filter.
     * @param filter The filter to match documents for update.
     * @param data The partial data to update in matching documents.
     * @returns An `Effect` that resolves to a `BatchResult`.
     */
    update: (filter: Filter<Doc>, data: Partial<Omit<Doc, "id">>) => Effect.Effect<BatchResult, DatabaseError>;
}
/**
 * Represents batch operations on a collection.
 */
interface BatchOperations<Doc> {
    /**
     * Inserts multiple documents into the collection.
     * @param docs The array of documents to insert.
     * @returns A promise that resolves to a `BatchResult`.
     */
    insert: (docs: Doc[]) => Promise<BatchResult>;
    /**
     * Deletes multiple documents matching the filter.
     * @param filter The filter to match documents for deletion.
     * @returns A promise that resolves to a `BatchResult`.
     */
    delete: (filter: Filter<Doc>) => Promise<BatchResult>;
    /**
     * Updates multiple documents matching the filter.
     * @param filter The filter to match documents for update.
     * @param data The partial data to update in matching documents.
     * @returns A promise that resolves to a `BatchResult`.
     */
    update: (filter: Filter<Doc>, data: Partial<Omit<Doc, "id">>) => Promise<BatchResult>;
}
/**
 * Represents a collection of documents, with methods that return `Effect` computations.
 * This interface is for users who prefer to work within the `Effect` ecosystem for handling asynchronous operations and errors.
 */
interface CollectionEffect<Doc> {
    /**
     * Batch operations for the collection.
     */
    batch: BatchOperationsEffect<Doc>;
    /**
     * Creates a new document in the collection.
     * @param data The data for the new document, excluding the 'id'.
     * @returns An `Effect` that resolves to the created document or fails with an `Error`.
     */
    create: (data: Doc) => Effect.Effect<Doc, DatabaseError>;
    /**
     * Retrieves a document by its ID.
     * @param id The ID of the document to retrieve.
     * @returns An `Effect` that resolves to the document or `undefined` if not found, and can fail with an `Error`.
     */
    findById: (id: string) => Effect.Effect<Doc | undefined, BadArgument | SystemError | JsonError | ParseError>;
    /**
     * Updates a document by its ID with partial data.
     * @param id The ID of the document to update.
     * @param data The partial data to update in the document.
     * @returns An `Effect` that resolves to the updated document or `undefined` if not found, and can fail with a `DatabaseError`.
     */
    update: (id: string, data: Partial<Doc>) => Effect.Effect<Doc | undefined, DatabaseError>;
    /**
     * Deletes a document by its ID.
     * @param id The ID of the document to delete.
     * @returns An `Effect` that resolves to `true` if the document was deleted, `false` otherwise, and can fail with a `DatabaseError`.
     */
    delete: (id: string) => Effect.Effect<boolean, DatabaseError>;
    /**
     * Finds documents based on query options.
     * @param options Query options for filtering, ordering, and pagination.
     * @returns An `Effect` that resolves to an array of documents and can fail with an `Error`.
     */
    find: (options: QueryOptions<Doc>) => Effect.Effect<Doc[], Error>;
    /**
     * Checks if a document with the given ID exists.
     * @param id The ID of the document to check.
     * @returns An `Effect` that resolves to `true` if the document exists, `false` otherwise, and can fail with a `PlatformError`.
     */
    has: (id: string) => Effect.Effect<boolean, PlatformError>;
    /**
     * Finds a single document based on query options.
     * @param options Query options for filtering and ordering.
     * @returns An `Effect` that resolves to a single document or `undefined` if not found, and can fail with a `PlatformError`.
     */
    findOne: (options: QueryOptions<Doc>) => Effect.Effect<Doc | undefined, PlatformError>;
}
/**
 * Represents a collection of documents in the database.
 */
interface Collection<Doc> {
    /**
     * Batch operations for the collection.
     */
    batch: BatchOperations<Doc>;
    /**
     * Creates a new document in the collection.
     * @param data - The data to be stored in the document.
     * @returns The created document.
     */
    create: (data: Doc) => Promise<Doc>;
    /**
     * Retrieves a document by its id.
     * @param id - The id of the document to retrieve.
     * @returns The retrieved document, or `undefined` if not found.
     */
    findById: (id: string) => Promise<Doc | undefined>;
    /**
     * Updates a document by its id.
     * @param id - The id of the document to update.
     * @param data - The data to update in the document.
     * @returns The updated document, or `undefined` if the document with the given id was not found.
     */
    update: (id: string, data: Partial<Omit<Doc, "id">>) => Promise<Doc | undefined>;
    /**
     * Deletes a document by its id.
     * @param id - The id of the document to delete.
     * @returns A promise that resolves to `true` if the document was deleted, `false` otherwise.
     */
    delete: (id: string) => Promise<boolean>;
    /**
     * Finds documents based on the provided query options.
     * @param options - Query options including filtering, ordering, skipping, and limiting.
     * @returns A promise that resolves to an array of documents.
     */
    find: (options: QueryOptions<Doc>) => Promise<Doc[]>;
    /**
     * Checks if a document with the given id exists in the collection.
     * @param id The id of the document to check.
     * @returns A promise that resolves to `true` if the document exists, `false` otherwise.
     */
    has: (id: string) => Promise<boolean>;
    /**
     * Finds a single document based on the provided query options.
     * @param options - Query options including filtering and ordering.
     * @returns A promise that resolves to a single document or undefined if not found.
     */
    findOne: (options: QueryOptions<Doc>) => Promise<Doc | undefined>;
}
/**
 * A utility type that infers the document types for all collections defined in the database configuration.
 * It maps over the `collections` object and resolves each schema (whether a `Schema` object or a schema string)
 * to its corresponding TypeScript type.
 *
 * @template T - The type of the `collections` configuration object.
 */
type InferCollections<T extends Record<string, SchemaOrString>> = {
    [K in keyof T]: T[K] extends Schema.Schema<infer A, any> ? A : T[K] extends StandardSchemaV1<any, infer A> ? A : T[K] extends string ? ParseSchemaString<T[K]> : any;
};
/**
 * Configuration options for creating a JasonDB instance.
 */
interface JasonDBConfig<T extends Record<string, SchemaOrString>> {
    /**
     * The base path where the database files will be stored.
     *
     * @example
     * const db = await createJasonDB({
     *  base_path: "db_dir",
     * });
     */
    base_path: string;
    /**
     * A record defining the schemas for the database collections.
     *
     * You can define schemas using `Effect.Schema` objects or a convenient string-based syntax.
     * The types for your collections are automatically inferred from these definitions.
     *
     * Example of defining collections with schema strings
     * ```ts
     * const db = await createJasonDB({
     *  base_path: "db_dir",
     *  collections: {
     *    user: "@id;name;age:number;email;isManager:boolean",
     *    post: "@id;title;author;*tags"
     *  }
     * });
     * ```
     *
     * Accessing a collection
     * ```ts
     * const { user } = db.collections;
     * ```
     *
     * ### Schema String Syntax
     *
     * The string syntax is a shorthand for defining fields and indexes.
     * Fields are separated by semicolons `;`.
     *
     * #### Field Types
     *
     * Specify a type by appending a colon `:` followed by the type name.
     * If no type is specified, it defaults to `string`.
     *
     * - `fieldName:string` = `string`
     * - `fieldName:number` = `number`
     * - `fieldName:boolean` = `boolean`
     * - `fieldName:date` = `Date`
     * - `fieldName:array<type>` = `type[]` (e.g., `items:array<string>`)
     * - `fieldName:record<key, value>` = `Record<key, value>` (e.g., `props:record<string, number>`)
     *
     * #### Index Modifiers
     *
     * You can prefix a field name with a symbol to create an index.
     *
     * - `@id`: Primary key (UUID).
     * - `++id`: Primary key (auto-incrementing number).
     * - `&name`: A unique index on the `name` field.
     * - `*tags`: A multi-entry index, ideal for array fields. The field type will be inferred as an array (e.g., `*tags:string` becomes `tags: string[]`).
     * - `[name+email]`: A compound index on `name` and `email`.
     *
     * All defined schemas are validated at runtime using `Effect.Schema`.
     */
    collections: T;
    /**
     * Optional configuration for caching.
     */
    cache?: {
        /**
         * Maximum number of documents to cache per collection.
         * @default 1000
         */
        document_capacity?: number;
        /**
         * Maximum number of B-Tree nodes to cache per index.
         * @default 1000
         */
        index_capacity?: number;
    };
}

interface DatabaseEffect<Collections extends Record<string, any>> {
    readonly collections: {
        [K in keyof Collections]: CollectionEffect<Collections[K]>;
    };
}
interface Database<Collections extends Record<string, any>> {
    readonly collections: {
        [K in keyof Collections]: Collection<Collections[K]>;
    };
    readonly [Symbol.asyncDispose]: () => Promise<void>;
}

declare const JasonDB_base: Context.TagClass<JasonDB, "DatabaseService", DatabaseEffect<any>>;
declare class JasonDB extends JasonDB_base {
}
declare const createJasonDBLayer: <const T extends Record<string, SchemaOrString>>(config: JasonDBConfig<T>) => Layer.Layer<JasonDB, unknown, unknown>;
/**
 * Creates a JasonDB instance based on the provided configuration.
 *
 * @param config - The configuration object for the JasonDB instance.
 * @returns A Promise that resolves to a Database instance with collections defined in the config.
 */
declare const createJasonDB: <const T extends Record<string, SchemaOrString>>(config: JasonDBConfig<T>) => Promise<Database<InferCollections<T>>>;

export { JasonDB, createJasonDB, createJasonDBLayer };
