import z from "./Zod";
import TableColumnType from "../../Types/Database/TableColumnType";
import { getTableColumns, } from "../../Types/Database/TableColumn";
import Color from "../../Types/Color";
import BadDataException from "../../Types/Exception/BadDataException";
import { PermissionHelper } from "../../Types/Permission";
import { BaseSchema } from "./BaseSchema";
import ObjectID from "../../Types/ObjectID";
import Email from "../../Types/Email";
import Phone from "../../Types/Phone";
import Domain from "../../Types/Domain";
import Version from "../../Types/Version";
import Name from "../../Types/Name";
import IP from "../../Types/IP/IP";
import Port from "../../Types/Port";
import MonitorSteps from "../../Types/Monitor/MonitorSteps";
import OneUptimeDate from "../../Types/Date";
export class ModelSchema extends BaseSchema {
    /**
     * Format permissions array into a human-readable string for OpenAPI documentation
     */
    static formatPermissionsForSchema(permissions) {
        if (!permissions || permissions.length === 0) {
            return "No access - you don't have permission for this operation";
        }
        return PermissionHelper.getPermissionTitles(permissions).join(", ");
    }
    /**
     * Get permissions description for a column to add to OpenAPI schema
     */
    static getColumnPermissionsDescription(model, key) {
        const accessControl = model.getColumnAccessControlForAllColumns()[key];
        if (!accessControl) {
            return "";
        }
        const createPermissions = this.formatPermissionsForSchema(accessControl.create);
        const readPermissions = this.formatPermissionsForSchema(accessControl.read);
        const updatePermissions = this.formatPermissionsForSchema(accessControl.update);
        return `Permissions - Create: [${createPermissions}], Read: [${readPermissions}], Update: [${updatePermissions}]`;
    }
    static getModelSchema(data) {
        const modelType = data.modelType;
        const model = new modelType();
        const columns = getTableColumns(model);
        const shape = {};
        // Get column access control for permission filtering
        const columnAccessControl = model.getColumnAccessControlForAllColumns();
        for (const key in columns) {
            const column = columns[key];
            if (!column) {
                continue;
            }
            // Skip Entity columns but keep EntityArray columns
            if (column.type === TableColumnType.Entity) {
                continue;
            }
            // Filter out columns with no permissions (root-only access)
            const accessControl = columnAccessControl[key];
            if (accessControl) {
                // Check if column has any permissions defined for read operation (general schema assumes read access)
                const hasReadPermissions = accessControl.read && accessControl.read.length > 0;
                // If no read permissions are defined, exclude the column from general schema
                if (!hasReadPermissions) {
                    continue;
                }
            }
            let zodType;
            if (column.type === TableColumnType.ObjectID) {
                zodType = ObjectID.getSchema();
            }
            else if (column.type === TableColumnType.Color) {
                zodType = Color.getSchema();
            }
            else if (column.type === TableColumnType.MonitorSteps) {
                zodType = MonitorSteps.getSchema();
            }
            else if (column.type === TableColumnType.Date) {
                zodType = OneUptimeDate.getSchema();
            }
            else if (column.type === TableColumnType.VeryLongText) {
                zodType = z.string().openapi({
                    type: "string",
                    example: "This is an example of very long text content that might be stored in this field. It can contain a lot of information, such as detailed descriptions, comments, or any other lengthy text data that needs to be stored in the database.",
                });
            }
            else if (column.type === TableColumnType.Number ||
                column.type === TableColumnType.PositiveNumber) {
                const openapiConfig = { type: "number", example: 42 };
                if (column.defaultValue !== undefined) {
                    openapiConfig.default = column.defaultValue;
                }
                zodType = z.number().openapi(openapiConfig);
            }
            else if (column.type === TableColumnType.Email) {
                zodType = Email.getSchema();
            }
            else if (column.type === TableColumnType.HashedString) {
                zodType = z
                    .string()
                    .openapi({ type: "string", example: "hashed_string_value" });
            }
            else if (column.type === TableColumnType.Slug) {
                zodType = z
                    .string()
                    .openapi({ type: "string", example: "example-slug-value" });
            }
            else if (column.type === TableColumnType.ShortText) {
                const openapiConfig = {
                    type: "string",
                    example: "Example short text",
                };
                if (column.defaultValue !== undefined) {
                    openapiConfig.default = column.defaultValue;
                }
                zodType = z.string().openapi(openapiConfig);
            }
            else if (column.type === TableColumnType.LongText) {
                const openapiConfig = {
                    type: "string",
                    example: "This is an example of longer text content that might be stored in this field.",
                };
                if (column.defaultValue !== undefined) {
                    openapiConfig.default = column.defaultValue;
                }
                zodType = z.string().openapi(openapiConfig);
            }
            else if (column.type === TableColumnType.Phone) {
                zodType = Phone.getSchema();
            }
            else if (column.type === TableColumnType.Version) {
                zodType = Version.getSchema();
            }
            else if (column.type === TableColumnType.Password) {
                zodType = z.string().openapi({
                    type: "string",
                    format: "password",
                    example: "••••••••",
                });
            }
            else if (column.type === TableColumnType.Name) {
                zodType = Name.getSchema();
            }
            else if (column.type === TableColumnType.Description) {
                zodType = z.string().openapi({
                    type: "string",
                    example: "This is a description of the item",
                });
            }
            else if (column.type === TableColumnType.File) {
                zodType = z.any().openapi({
                    type: "string",
                    format: "binary",
                });
            }
            else if (column.type === TableColumnType.Buffer) {
                zodType = z.any().openapi({
                    type: "string",
                    format: "binary",
                });
            }
            else if (column.type === TableColumnType.ShortURL) {
                zodType = z.string().url().openapi({
                    type: "string",
                    example: "https://short.url/abc123",
                });
            }
            else if (column.type === TableColumnType.Markdown) {
                zodType = z.string().openapi({
                    type: "string",
                    example: "# Heading\n\nThis is **markdown** content",
                });
            }
            else if (column.type === TableColumnType.Domain) {
                zodType = Domain.getSchema();
            }
            else if (column.type === TableColumnType.Port) {
                zodType = Port.getSchema();
            }
            else if (column.type === TableColumnType.IP) {
                zodType = IP.getSchema();
            }
            else if (column.type === TableColumnType.LongURL) {
                zodType = z.string().url().openapi({
                    type: "string",
                    example: "https://www.example.com/path/to/resource?param=value",
                });
            }
            else if (column.type === TableColumnType.OTP) {
                zodType = z.string().openapi({
                    type: "string",
                    example: "123456",
                });
            }
            else if (column.type === TableColumnType.HTML) {
                zodType = z.string().openapi({
                    type: "string",
                    example: "<div><h1>Title</h1><p>Content</p></div>",
                });
            }
            else if (column.type === TableColumnType.JavaScript) {
                zodType = z.string().openapi({
                    type: "string",
                    example: "function example() { return true; }",
                });
            }
            else if (column.type === TableColumnType.CSS) {
                zodType = z.string().openapi({
                    type: "string",
                    example: "body { color: #333; margin: 0; }",
                });
            }
            else if (column.type === TableColumnType.Array) {
                zodType = z.array(z.any()).openapi({
                    type: "array",
                    items: {
                        type: "string",
                    },
                    "x-ordered": column.ordered === true,
                    example: ["item1", "item2", "item3"],
                });
            }
            else if (column.type === TableColumnType.SmallPositiveNumber) {
                const openapiConfig = {
                    type: "integer",
                    example: 5,
                };
                if (column.defaultValue !== undefined) {
                    openapiConfig.default = column.defaultValue;
                }
                zodType = z.number().int().nonnegative().openapi(openapiConfig);
            }
            else if (column.type === TableColumnType.BigPositiveNumber) {
                const openapiConfig = {
                    type: "number",
                    example: 1000000,
                };
                if (column.defaultValue !== undefined) {
                    openapiConfig.default = column.defaultValue;
                }
                zodType = z.number().nonnegative().openapi(openapiConfig);
            }
            else if (column.type === TableColumnType.SmallNumber) {
                const openapiConfig = {
                    type: "integer",
                    example: 10,
                };
                if (column.defaultValue !== undefined) {
                    openapiConfig.default = column.defaultValue;
                }
                zodType = z.number().int().openapi(openapiConfig);
            }
            else if (column.type === TableColumnType.BigNumber) {
                const openapiConfig = {
                    type: "number",
                    example: 1000000,
                };
                if (column.defaultValue !== undefined) {
                    openapiConfig.default = column.defaultValue;
                }
                zodType = z.number().openapi(openapiConfig);
            }
            else if (column.type === TableColumnType.Permission) {
                zodType = z.any().openapi({
                    type: "object",
                    example: { read: true, write: false, delete: false },
                });
            }
            else if (column.type === TableColumnType.CustomFieldType) {
                zodType = z.any().openapi({
                    type: "object",
                    example: { type: "text", required: true },
                });
            }
            else if (column.type === TableColumnType.MonitorType) {
                zodType = z.string().openapi({
                    type: "string",
                    example: "Manual",
                });
            }
            else if (column.type === TableColumnType.WorkflowStatus) {
                zodType = z.string().openapi({
                    type: "string",
                    example: "In Progress",
                });
            }
            else if (column.type === TableColumnType.Boolean) {
                const openapiConfig = { type: "boolean", example: true };
                if (column.defaultValue !== undefined) {
                    openapiConfig.default = column.defaultValue;
                }
                zodType = z.boolean().openapi(openapiConfig);
            }
            else if (column.type === TableColumnType.JSON) {
                zodType = z.any().openapi({
                    type: "object",
                    example: { key: "value", nested: { data: 123 } },
                });
            }
            else if (column.type === TableColumnType.EntityArray) {
                const entityArrayType = column.modelType;
                if (!entityArrayType) {
                    continue;
                }
                zodType = z
                    .array(z.lazy(() => {
                    return ModelSchema.getModelSchema({
                        modelType: entityArrayType,
                    });
                }))
                    .openapi({
                    type: "array",
                    items: {
                        type: "object",
                        properties: {
                            _id: {
                                type: "string",
                                format: "uuid",
                                description: "Unique identifier for the entity",
                            },
                        },
                    },
                    "x-ordered": column.ordered === true,
                    example: [
                        {
                            _id: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
                        },
                    ],
                });
            }
            else if (column.type === TableColumnType.Entity) {
                const entityType = column.modelType;
                if (!entityType) {
                    continue;
                }
                const schema = ModelSchema.getModelSchema({
                    modelType: entityType,
                });
                zodType = z
                    .lazy(() => {
                    return schema;
                })
                    .openapi({
                    type: "object",
                    properties: {
                        _id: {
                            type: "string",
                            format: "uuid",
                            description: "Unique identifier for the entity",
                            example: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
                        },
                    },
                    example: {
                        _id: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
                    },
                });
            }
            else {
                // Fallback for unknown column types - use a generic object type
                zodType = z.any().openapi({
                    type: "object",
                    description: "Unknown type field",
                    example: null,
                });
            }
            if (column.required) {
                // leave as is
            }
            else {
                zodType = zodType.optional();
            }
            // add title and description to the schema
            let finalDescription = "";
            // Add column description first if it exists
            if (column.description) {
                finalDescription = column.description;
            }
            else if (column.title) {
                finalDescription = column.title;
            }
            // Add permissions description, prefixed to the column description
            const permissionsDescription = this.getColumnPermissionsDescription(model, key);
            if (permissionsDescription) {
                if (finalDescription) {
                    finalDescription = `${finalDescription}. ${permissionsDescription}`;
                }
                else {
                    finalDescription = permissionsDescription;
                }
            }
            // Set the final combined description
            if (finalDescription) {
                zodType = zodType.describe(finalDescription);
            }
            // Mark computed fields as readOnly in OpenAPI spec
            if (column.computed) {
                zodType = zodType.openapi({ readOnly: true });
            }
            shape[key] = zodType;
        }
        const schema = z.object(shape);
        return schema;
    }
    static getSortableTypes() {
        return [
            TableColumnType.VeryLongText,
            TableColumnType.Slug,
            TableColumnType.ShortText,
            TableColumnType.LongText,
            TableColumnType.Number,
            TableColumnType.Date,
            TableColumnType.Boolean,
            TableColumnType.Description,
            TableColumnType.ObjectID,
        ];
    }
    static getQueryModelSchema(data) {
        const modelType = data.modelType;
        const model = new modelType();
        return this.generateQuerySchema({
            model,
            tableName: model.tableName || "model",
            getColumns: (model) => {
                const columns = getTableColumns(model);
                return Object.keys(columns)
                    .map((key) => {
                    const column = columns[key];
                    return column ? { key, type: column.type } : null;
                })
                    .filter((col) => {
                    // Skip Entity columns but keep EntityArray columns
                    if (col && col.type === TableColumnType.Entity) {
                        return false;
                    }
                    return col !== null;
                });
            },
            getValidOperatorsForColumnType: (columnType) => {
                return this.getValidOperatorsForColumnType(columnType);
            },
            getOperatorSchema: (operatorType, columnType) => {
                return this.getOperatorSchema(operatorType, columnType);
            },
            getQuerySchemaExample: () => {
                return this.getQuerySchemaExample(modelType);
            },
            getExampleValueForColumn: (columnType) => {
                return this.getExampleValueForColumn(columnType);
            },
            disableOpenApiSchema: data.disableOpenApiSchema || false,
        });
    }
    static getValidOperatorsForColumnType(columnType) {
        const commonOperators = [
            "EqualTo",
            "NotEqual",
            "IsNull",
            "NotNull",
            "EqualToOrNull",
        ];
        switch (columnType) {
            case TableColumnType.ObjectID:
            case TableColumnType.Email:
            case TableColumnType.Phone:
            case TableColumnType.HashedString:
            case TableColumnType.Slug:
                return [...commonOperators, "Includes"];
            case TableColumnType.ShortText:
            case TableColumnType.LongText:
            case TableColumnType.VeryLongText:
            case TableColumnType.Name:
            case TableColumnType.Description:
            case TableColumnType.Domain:
            case TableColumnType.Markdown:
                return [...commonOperators, "Search", "Includes"];
            case TableColumnType.Number:
            case TableColumnType.PositiveNumber:
            case TableColumnType.SmallNumber:
            case TableColumnType.SmallPositiveNumber:
            case TableColumnType.BigNumber:
            case TableColumnType.BigPositiveNumber:
                return [
                    ...commonOperators,
                    "GreaterThan",
                    "LessThan",
                    "GreaterThanOrEqual",
                    "LessThanOrEqual",
                    "InBetween",
                ];
            case TableColumnType.Date:
                return [
                    ...commonOperators,
                    "GreaterThan",
                    "LessThan",
                    "GreaterThanOrEqual",
                    "LessThanOrEqual",
                    "InBetween",
                ];
            case TableColumnType.Boolean:
                return commonOperators;
            case TableColumnType.JSON:
            case TableColumnType.Array:
                return ["EqualTo", "NotEqual", "IsNull", "NotNull"];
            case TableColumnType.Entity:
            case TableColumnType.EntityArray:
                return ["EqualTo", "NotEqual", "IsNull", "NotNull", "Includes"];
            case TableColumnType.Color:
            case TableColumnType.File:
            case TableColumnType.Buffer:
                return commonOperators;
            default:
                return commonOperators;
        }
    }
    static getOperatorSchema(operatorType, columnType) {
        const baseValue = this.getBaseValueSchemaForColumnType(columnType);
        switch (operatorType) {
            case "EqualTo":
                return baseValue;
            case "NotEqual":
            case "EqualToOrNull":
                return z.object({
                    _type: z.literal(operatorType),
                    value: baseValue,
                });
            case "GreaterThan":
            case "LessThan":
            case "GreaterThanOrEqual":
            case "LessThanOrEqual":
                return z.object({
                    _type: z.literal(operatorType),
                    value: baseValue,
                });
            case "Search":
                return z.object({
                    _type: z.literal("Search"),
                    value: z.string(),
                });
            case "InBetween":
                return z.object({
                    _type: z.literal("InBetween"),
                    startValue: baseValue,
                    endValue: baseValue,
                });
            case "IsNull":
            case "NotNull":
                return z.object({
                    _type: z.literal(operatorType),
                });
            case "Includes":
                return z.object({
                    _type: z.literal("Includes"),
                    value: z.array(baseValue),
                });
            default:
                return z.object({
                    _type: z.literal(operatorType),
                    value: baseValue,
                });
        }
    }
    static getBaseValueSchemaForColumnType(columnType) {
        switch (columnType) {
            case TableColumnType.ObjectID:
                return ObjectID.getSchema();
            case TableColumnType.Email:
                return Email.getSchema();
            case TableColumnType.Phone:
                return Phone.getSchema();
            case TableColumnType.Name:
                return Name.getSchema();
            case TableColumnType.Domain:
                return Domain.getSchema();
            case TableColumnType.Version:
                return Version.getSchema();
            case TableColumnType.IP:
                return IP.getSchema();
            case TableColumnType.Port:
                return Port.getSchema();
            case TableColumnType.HashedString:
            case TableColumnType.Slug:
            case TableColumnType.ShortText:
            case TableColumnType.LongText:
            case TableColumnType.VeryLongText:
            case TableColumnType.Description:
            case TableColumnType.Markdown:
            case TableColumnType.HTML:
            case TableColumnType.JavaScript:
            case TableColumnType.CSS:
            case TableColumnType.LongURL:
            case TableColumnType.ShortURL:
            case TableColumnType.OTP:
            case TableColumnType.Password:
                return z.string();
            case TableColumnType.Number:
            case TableColumnType.PositiveNumber:
            case TableColumnType.SmallNumber:
            case TableColumnType.SmallPositiveNumber:
            case TableColumnType.BigNumber:
            case TableColumnType.BigPositiveNumber:
                return z.number();
            case TableColumnType.Date:
                return z.date();
            case TableColumnType.Boolean:
                return z.boolean();
            case TableColumnType.JSON:
            case TableColumnType.Array:
            case TableColumnType.Permission:
            case TableColumnType.CustomFieldType:
                return z.any();
            case TableColumnType.Color:
                return Color.getSchema();
            case TableColumnType.Entity:
                return z.string(); // Entity IDs are typically strings
            case TableColumnType.EntityArray:
                return z.array(z.string()); // Array of entity IDs
            case TableColumnType.File:
            case TableColumnType.Buffer:
                return z.any();
            case TableColumnType.MonitorType:
            case TableColumnType.WorkflowStatus:
                return z.string();
            default:
                return z.string();
        }
    }
    static getSortModelSchema(data) {
        const modelType = data.modelType;
        const model = new modelType();
        return this.generateSortSchema({
            model,
            tableName: model.tableName || "model",
            getSortableTypes: () => {
                return this.getSortableTypes();
            },
            getColumnsForSorting: (model) => {
                const columns = getTableColumns(model);
                return Object.keys(columns)
                    .map((key) => {
                    const column = columns[key];
                    return column ? { key, type: column.type } : null;
                })
                    .filter((col) => {
                    return col !== null;
                });
            },
            disableOpenApiSchema: data.disableOpenApiSchema || false,
        });
    }
    static getSelectModelSchema(data) {
        const modelType = data.modelType;
        const model = new modelType();
        return this.generateSelectSchema({
            model,
            tableName: model.tableName || "model",
            getColumns: (model) => {
                const columns = getTableColumns(model);
                return Object.keys(columns)
                    .map((key) => {
                    const column = columns[key];
                    return column ? { key, type: column.type } : null;
                })
                    .filter((col) => {
                    // Skip Entity columns but keep EntityArray columns
                    if (col && col.type === TableColumnType.Entity) {
                        return false;
                    }
                    return col !== null;
                });
            },
            getSelectSchemaExample: () => {
                return this.getSelectSchemaExample(modelType);
            },
            allowNested: !data.isNested,
            getNestedSchema: (key, model) => {
                const columns = getTableColumns(model);
                const column = columns[key];
                if (column &&
                    column.modelType &&
                    // Only allow EntityArray columns, exclude Entity columns
                    column.type === TableColumnType.EntityArray) {
                    return this.getSelectModelSchema({
                        modelType: column.modelType,
                        isNested: true,
                    });
                }
                return null;
            },
        });
    }
    static getGroupByModelSchema(data) {
        const modelType = data.modelType;
        const model = new modelType();
        return this.generateGroupBySchema({
            model,
            tableName: model.tableName || "model",
            getColumns: (model) => {
                const columns = getTableColumns(model);
                return Object.keys(columns)
                    .map((key) => {
                    const column = columns[key];
                    return column ? { key, type: column.type } : null;
                })
                    .filter((col) => {
                    // Skip Entity columns but keep EntityArray columns
                    if (col && col.type === TableColumnType.Entity) {
                        return false;
                    }
                    return col !== null;
                });
            },
            getGroupableTypes: () => {
                return [
                    TableColumnType.ShortText,
                    TableColumnType.LongText,
                    TableColumnType.Name,
                    TableColumnType.Email,
                    TableColumnType.Slug,
                    TableColumnType.ObjectID,
                    TableColumnType.Boolean,
                    TableColumnType.Date,
                    TableColumnType.Number,
                    TableColumnType.PositiveNumber,
                    TableColumnType.SmallNumber,
                    TableColumnType.SmallPositiveNumber,
                    TableColumnType.BigNumber,
                    TableColumnType.BigPositiveNumber,
                ];
            },
            getGroupBySchemaExample: () => {
                return this.getGroupBySchemaExample(modelType);
            },
        });
    }
    static getExampleValueForColumn(columnType, isSecondValue = false) {
        switch (columnType) {
            case TableColumnType.ObjectID:
                return isSecondValue
                    ? "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
                    : "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
            case TableColumnType.Email:
                return isSecondValue ? "jane@example.com" : "john@example.com";
            case TableColumnType.Phone:
                return isSecondValue ? "+1-555-987-6543" : "+1-555-123-4567";
            case TableColumnType.Number:
            case TableColumnType.PositiveNumber:
                return isSecondValue ? 100 : 50;
            case TableColumnType.SmallNumber:
                return isSecondValue ? 20 : 10;
            case TableColumnType.SmallPositiveNumber:
                return isSecondValue ? 25 : 15;
            case TableColumnType.BigNumber:
                return isSecondValue ? 2000000 : 1000000;
            case TableColumnType.BigPositiveNumber:
                return isSecondValue ? 2500000 : 1500000;
            case TableColumnType.Date:
                return isSecondValue
                    ? "2023-12-31T23:59:59.000Z"
                    : "2023-01-01T00:00:00.000Z";
            case TableColumnType.Boolean:
                return !isSecondValue;
            case TableColumnType.ShortText:
            case TableColumnType.Name:
                return isSecondValue ? "Jane Doe" : "John Doe";
            case TableColumnType.Description:
                return isSecondValue
                    ? "Second example description"
                    : "Example description";
            case TableColumnType.Domain:
                return isSecondValue ? "example.org" : "example.com";
            default:
                return isSecondValue ? "example_value_2" : "example_value_1";
        }
    }
    static getQuerySchemaExample(modelType) {
        const model = new modelType();
        const columns = getTableColumns(model);
        const example = {};
        let exampleCount = 0;
        const maxExamples = 3;
        for (const key in columns) {
            if (exampleCount >= maxExamples) {
                break;
            }
            const column = columns[key];
            if (!column) {
                continue;
            }
            const validOperators = this.getValidOperatorsForColumnType(column.type);
            if (validOperators.length === 0) {
                continue;
            }
            // Add example based on column type and available operators
            if (column.type === TableColumnType.ShortText ||
                column.type === TableColumnType.Name) {
                if (validOperators.includes("EqualTo")) {
                    example[key] = { _type: "EqualTo", value: "Example Text" };
                    exampleCount++;
                }
                else if (validOperators.includes("Search")) {
                    example[key] = { _type: "Search", value: "example" };
                    exampleCount++;
                }
            }
            else if (column.type === TableColumnType.Email &&
                validOperators.includes("EqualTo")) {
                example[key] = { _type: "EqualTo", value: "user@example.com" };
                exampleCount++;
            }
            else if (column.type === TableColumnType.Date &&
                validOperators.includes("GreaterThan")) {
                example[key] = {
                    _type: "GreaterThan",
                    value: "2023-01-01T00:00:00.000Z",
                };
                exampleCount++;
            }
            else if (column.type === TableColumnType.Boolean &&
                validOperators.includes("EqualTo")) {
                example[key] = { _type: "EqualTo", value: true };
                exampleCount++;
            }
            else if ((column.type === TableColumnType.Number ||
                column.type === TableColumnType.PositiveNumber) &&
                validOperators.includes("GreaterThan")) {
                example[key] = { _type: "GreaterThan", value: 10 };
                exampleCount++;
            }
            else if (validOperators.includes("EqualTo")) {
                example[key] = this.getExampleValueForColumn(column.type);
                exampleCount++;
            }
        }
        // If no examples were added, add a generic one
        if (exampleCount === 0) {
            example["_id"] = {
                _type: "EqualTo",
                value: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            };
        }
        return example;
    }
    static getSelectSchemaExample(modelType) {
        if (!modelType) {
            throw new BadDataException("Model type is required to generate select schema example.");
        }
        const model = new modelType();
        const columns = getTableColumns(model);
        const example = {};
        // Add common fields that most models have
        const commonFields = ["_id", "createdAt", "updatedAt"];
        for (const field of commonFields) {
            if (columns[field]) {
                example[field] = true;
            }
        }
        // Add first few non-common fields as examples
        let fieldCount = 0;
        const maxFields = 5;
        for (const key in columns) {
            if (fieldCount >= maxFields) {
                break;
            }
            const column = columns[key];
            if (!column || commonFields.includes(key)) {
                continue;
            }
            // Prioritize fields that are likely to be commonly selected
            if (column.type === TableColumnType.ShortText ||
                column.type === TableColumnType.Name ||
                column.type === TableColumnType.Email ||
                column.type === TableColumnType.Description) {
                example[key] = true;
                fieldCount++;
            }
        }
        return example;
    }
    static getGroupBySchemaExample(modelType) {
        if (!modelType) {
            throw new BadDataException("Model type is required to generate group by schema example.");
        }
        const model = new modelType();
        const columns = getTableColumns(model);
        // Find the first suitable field for grouping
        for (const key in columns) {
            const column = columns[key];
            if (!column) {
                continue;
            }
            // Prioritize common groupable fields
            if (column.type === TableColumnType.ShortText ||
                column.type === TableColumnType.Name ||
                column.type === TableColumnType.Boolean ||
                column.type === TableColumnType.Date) {
                return { [key]: true };
            }
        }
        // Fallback to any available field
        for (const key in columns) {
            const column = columns[key];
            if (!column) {
                continue;
            }
            const isGroupable = [
                TableColumnType.ShortText,
                TableColumnType.LongText,
                TableColumnType.Name,
                TableColumnType.Email,
                TableColumnType.Slug,
                TableColumnType.ObjectID,
                TableColumnType.Boolean,
                TableColumnType.Date,
                TableColumnType.Number,
                TableColumnType.PositiveNumber,
                TableColumnType.SmallNumber,
                TableColumnType.SmallPositiveNumber,
                TableColumnType.BigNumber,
                TableColumnType.BigPositiveNumber,
            ].includes(column.type);
            if (isGroupable) {
                return { [key]: true };
            }
        }
        // Final fallback
        return { status: true };
    }
    // Shared method to build model schemas with different field exclusions
    static buildModelSchema(data) {
        var _a;
        const modelType = data.modelType;
        const model = new modelType();
        const columns = getTableColumns(model);
        const shape = {};
        // Get column access control for permission filtering
        const columnAccessControl = model.getColumnAccessControlForAllColumns();
        for (const key in columns) {
            const column = columns[key];
            if (!column) {
                continue;
            }
            if (column.hideColumnInDocumentation) {
                continue;
            }
            // Skip Entity columns but keep EntityArray columns
            if (column.type === TableColumnType.Entity) {
                continue;
            }
            // Skip excluded fields
            if (data.excludedFields && data.excludedFields.includes(key)) {
                continue;
            }
            // Only include specified fields if includedFields is provided
            if (data.includedFields && !data.includedFields.includes(key)) {
                continue;
            }
            // Skip computed fields for create and update operations
            if (column.computed &&
                (data.schemaType === "create" || data.schemaType === "update")) {
                continue;
            }
            // Filter out columns with no permissions (root-only access)
            const accessControl = columnAccessControl[key];
            if (accessControl) {
                let hasPermissions = false;
                // Check if column has any permissions defined for the current operation
                if (data.schemaType === "create" &&
                    accessControl.create &&
                    accessControl.create.length > 0) {
                    hasPermissions = true;
                }
                else if (data.schemaType === "read" &&
                    accessControl.read &&
                    accessControl.read.length > 0) {
                    hasPermissions = true;
                }
                else if (data.schemaType === "update" &&
                    accessControl.update &&
                    accessControl.update.length > 0) {
                    hasPermissions = true;
                }
                else if (data.schemaType === "delete") {
                    // For delete operations, we don't filter by column permissions
                    hasPermissions = true;
                }
                // If no permissions are defined for this operation, exclude the column
                if (!hasPermissions) {
                    continue;
                }
            }
            let zodType = this.getZodTypeForColumn(column, data.schemaType, data.disableOpenApiSchema || false);
            /*
             * Check if the column is required and make it optional if not
             * Also make columns with default values optional in create schemas
             */
            if (column.isDefaultValueColumn) {
                // should be optional
                zodType = zodType.optional();
            }
            else if (((_a = column.title) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "project id" &&
                column.type === TableColumnType.ObjectID) {
                // this is optional in the API as well as it's derived from API key
                zodType = zodType.optional();
            }
            else if (column.required) {
                // leave as is
            }
            else {
                zodType = zodType.optional();
            }
            // Make fields optional if specified (for global override)
            if (data.makeOptional) {
                zodType = zodType.optional();
            }
            // Add title and description to the schema
            let finalDescription = "";
            // Add column description first if it exists
            if (column.description) {
                finalDescription = column.description;
            }
            else if (column.title) {
                finalDescription = column.title;
            }
            // Add permissions description, prefixed to the column description
            const permissionsDescription = this.getColumnPermissionsDescription(model, key);
            if (permissionsDescription) {
                if (finalDescription) {
                    finalDescription = `${finalDescription}. ${permissionsDescription}`;
                }
                else {
                    finalDescription = permissionsDescription;
                }
            }
            // Set the final combined description
            if (finalDescription) {
                zodType = zodType.describe(finalDescription);
            }
            shape[key] = zodType;
        }
        const schema = z.object(shape).openapi({
            description: `${data.description} schema for ${model.tableName || "model"} model. ${data.description}`,
            additionalProperties: false,
            example: data.example,
        });
        return schema;
    }
    // Helper method to get Zod type for a column
    static getZodTypeForColumn(column, schemaType, disableOpenApiSchema = false) {
        let zodType;
        // Helper function to add default value to openapi schema if it exists
        const addDefaultToOpenApi = (openApiConfig) => {
            if (column.defaultValue !== undefined && column.defaultValue !== null) {
                return Object.assign(Object.assign({}, openApiConfig), { default: column.defaultValue });
            }
            return openApiConfig;
        };
        // Helper function to conditionally apply OpenAPI schema
        const applyOpenApi = (baseType, openApiConfig) => {
            if (disableOpenApiSchema) {
                return baseType;
            }
            return baseType.openapi(addDefaultToOpenApi(openApiConfig));
        };
        if (column.type === TableColumnType.ObjectID) {
            zodType = ObjectID.getSchema();
        }
        else if (column.type === TableColumnType.Port) {
            zodType = Port.getSchema();
        }
        else if (column.type === TableColumnType.MonitorSteps) {
            zodType = MonitorSteps.getSchema();
        }
        else if (column.type === TableColumnType.IP) {
            zodType = IP.getSchema();
        }
        else if (column.type === TableColumnType.Color) {
            zodType = Color.getSchema();
        }
        else if (column.type === TableColumnType.Date) {
            zodType = OneUptimeDate.getSchema();
        }
        else if (column.type === TableColumnType.VeryLongText) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "This is an example of very long text content that might be stored in this field. It can contain a lot of information, such as detailed descriptions, comments, or any other lengthy text data that needs to be stored in the database.",
            });
        }
        else if (column.type === TableColumnType.Number ||
            column.type === TableColumnType.PositiveNumber) {
            zodType = applyOpenApi(z.number(), { type: "number", example: 42 });
        }
        else if (column.type === TableColumnType.Email) {
            zodType = Email.getSchema();
        }
        else if (column.type === TableColumnType.HashedString) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "hashed_string_value",
            });
        }
        else if (column.type === TableColumnType.Slug) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "example-slug-value",
            });
        }
        else if (column.type === TableColumnType.ShortText) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "Example short text",
            });
        }
        else if (column.type === TableColumnType.LongText) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "This is an example of longer text content that might be stored in this field.",
            });
        }
        else if (column.type === TableColumnType.Phone) {
            zodType = Phone.getSchema();
        }
        else if (column.type === TableColumnType.Version) {
            zodType = Version.getSchema();
        }
        else if (column.type === TableColumnType.Password) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                format: "password",
                example: "••••••••",
            });
        }
        else if (column.type === TableColumnType.Name) {
            zodType = Name.getSchema();
        }
        else if (column.type === TableColumnType.Description) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "This is a description of the item",
            });
        }
        else if (column.type === TableColumnType.File) {
            zodType = applyOpenApi(z.any(), {
                type: "string",
                format: "binary",
            });
        }
        else if (column.type === TableColumnType.Buffer) {
            zodType = applyOpenApi(z.any(), {
                type: "string",
                format: "binary",
            });
        }
        else if (column.type === TableColumnType.ShortURL) {
            zodType = applyOpenApi(z.string().url(), {
                type: "string",
                example: "https://short.url/abc123",
            });
        }
        else if (column.type === TableColumnType.Markdown) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "# Heading\n\nThis is **markdown** content",
            });
        }
        else if (column.type === TableColumnType.Domain) {
            zodType = Domain.getSchema();
        }
        else if (column.type === TableColumnType.LongURL) {
            zodType = applyOpenApi(z.string().url(), {
                type: "string",
                example: "https://www.example.com/path/to/resource?param=value",
            });
        }
        else if (column.type === TableColumnType.OTP) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "123456",
            });
        }
        else if (column.type === TableColumnType.HTML) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "<div><h1>Title</h1><p>Content</p></div>",
            });
        }
        else if (column.type === TableColumnType.JavaScript) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "function example() { return true; }",
            });
        }
        else if (column.type === TableColumnType.CSS) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "body { color: #333; margin: 0; }",
            });
        }
        else if (column.type === TableColumnType.Array) {
            zodType = z.array(z.any()).openapi(addDefaultToOpenApi({
                type: "array",
                items: {
                    type: "string",
                },
                example: ["item1", "item2", "item3"],
            }));
        }
        else if (column.type === TableColumnType.SmallPositiveNumber) {
            zodType = z
                .number()
                .int()
                .nonnegative()
                .openapi(addDefaultToOpenApi({
                type: "integer",
                example: 5,
            }));
        }
        else if (column.type === TableColumnType.BigPositiveNumber) {
            zodType = z
                .number()
                .nonnegative()
                .openapi(addDefaultToOpenApi({
                type: "number",
                example: 1000000,
            }));
        }
        else if (column.type === TableColumnType.SmallNumber) {
            zodType = z
                .number()
                .int()
                .openapi(addDefaultToOpenApi({
                type: "integer",
                example: 10,
            }));
        }
        else if (column.type === TableColumnType.BigNumber) {
            zodType = z.number().openapi(addDefaultToOpenApi({
                type: "number",
                example: 1000000,
            }));
        }
        else if (column.type === TableColumnType.Permission) {
            zodType = applyOpenApi(z.any(), {
                type: "object",
                example: { read: true, write: false, delete: false },
            });
        }
        else if (column.type === TableColumnType.CustomFieldType) {
            zodType = applyOpenApi(z.any(), {
                type: "object",
                example: { type: "text", required: true },
            });
        }
        else if (column.type === TableColumnType.MonitorType) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "HTTP",
            });
        }
        else if (column.type === TableColumnType.WorkflowStatus) {
            zodType = applyOpenApi(z.string(), {
                type: "string",
                example: "In Progress",
            });
        }
        else if (column.type === TableColumnType.Boolean) {
            zodType = applyOpenApi(z.boolean(), { type: "boolean", example: true });
        }
        else if (column.type === TableColumnType.JSON) {
            zodType = applyOpenApi(z.any(), {
                type: "object",
                example: { key: "value", nested: { data: 123 } },
            });
        }
        else if (column.type === TableColumnType.EntityArray) {
            const entityArrayType = column.modelType;
            if (!entityArrayType) {
                return applyOpenApi(z.any(), {
                    type: "array",
                    items: { type: "object" },
                    example: [],
                });
            }
            // Use the appropriate schema method based on the operation type
            let schemaMethod;
            switch (schemaType) {
                case "create":
                    schemaMethod = ModelSchema.getCreateModelSchema;
                    break;
                case "read":
                    schemaMethod = ModelSchema.getReadModelSchema;
                    break;
                case "update":
                    schemaMethod = ModelSchema.getUpdateModelSchema;
                    break;
                case "delete":
                    schemaMethod = ModelSchema.getDeleteModelSchema;
                    break;
            }
            const arrayType = z.array(z.lazy(() => {
                return schemaMethod({
                    modelType: entityArrayType,
                });
            }));
            zodType = disableOpenApiSchema
                ? arrayType
                : arrayType.openapi(addDefaultToOpenApi({
                    type: "array",
                    items: {
                        type: "object",
                        properties: {
                            id: { type: "string" },
                        },
                    },
                    example: [{ id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }],
                }));
        }
        else if (column.type === TableColumnType.Entity) {
            const entityType = column.modelType;
            if (!entityType) {
                return applyOpenApi(z.any(), {
                    type: "object",
                    description: "Entity reference",
                    example: { _id: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" },
                });
            }
            // Use the appropriate schema method based on the operation type
            let schema;
            switch (schemaType) {
                case "create":
                    schema = ModelSchema.getCreateModelSchema({ modelType: entityType });
                    break;
                case "read":
                    schema = ModelSchema.getReadModelSchema({ modelType: entityType });
                    break;
                case "update":
                    schema = ModelSchema.getUpdateModelSchema({ modelType: entityType });
                    break;
                case "delete":
                    schema = ModelSchema.getDeleteModelSchema({ modelType: entityType });
                    break;
            }
            const lazyType = z.lazy(() => {
                return schema;
            });
            zodType = disableOpenApiSchema
                ? lazyType
                : lazyType.openapi(addDefaultToOpenApi({
                    type: "object",
                    example: { id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" },
                }));
        }
        else {
            zodType = applyOpenApi(z.any(), { type: "null", example: null });
        }
        // Apply default value if it exists in the column metadata
        if (column.defaultValue !== undefined && column.defaultValue !== null) {
            zodType = zodType.default(column.defaultValue);
        }
        // Mark computed fields as readOnly in OpenAPI spec
        if (column.computed && !disableOpenApiSchema) {
            zodType = zodType.openapi({ readOnly: true });
        }
        return zodType;
    }
    static getCreateModelSchema(data) {
        // Auto-generated fields to exclude from create schema
        const excludedFields = [
            "_id",
            "createdAt",
            "updatedAt",
            "deletedAt",
            "version",
        ];
        return this.buildModelSchema({
            modelType: data.modelType,
            excludedFields,
            schemaType: "create",
            description: "Create",
            example: this.generateDynamicExample({
                modelType: data.modelType,
                schemaType: "create",
                excludedFields,
            }),
            disableOpenApiSchema: data.disableOpenApiSchema || false,
        });
    }
    static getReadModelSchema(data) {
        // For read operations, include all fields
        return this.buildModelSchema({
            modelType: data.modelType,
            schemaType: "read",
            description: "Read",
            example: this.generateDynamicExample({
                modelType: data.modelType,
                schemaType: "read",
            }),
        });
    }
    static getUpdateModelSchema(data) {
        // Auto-generated fields to exclude from update schema (but allow _id for identification)
        const excludedFields = [
            "createdAt",
            "updatedAt",
            "deletedAt",
            "version",
        ];
        return this.buildModelSchema({
            modelType: data.modelType,
            excludedFields,
            schemaType: "update",
            description: "Update",
            example: this.generateDynamicExample({
                modelType: data.modelType,
                schemaType: "update",
                excludedFields,
            }),
            makeOptional: true, // All fields are optional for updates
            disableOpenApiSchema: data.disableOpenApiSchema || false,
        });
    }
    static getDeleteModelSchema(data) {
        // For delete, we typically only need the ID
        const includedFields = ["_id"];
        return this.buildModelSchema({
            modelType: data.modelType,
            includedFields,
            schemaType: "delete",
            description: "Delete",
            example: this.generateDynamicExample({
                modelType: data.modelType,
                schemaType: "delete",
                includedFields,
            }),
        });
    }
    static generateDynamicExample(data) {
        const model = new data.modelType();
        const columns = getTableColumns(model);
        const columnAccessControl = model.getColumnAccessControlForAllColumns();
        const example = {};
        for (const key in columns) {
            const column = columns[key];
            if (!column) {
                continue;
            }
            if (column.hideColumnInDocumentation) {
                continue;
            }
            // Skip excluded fields
            if (data.excludedFields && data.excludedFields.includes(key)) {
                continue;
            }
            // Only include specified fields if includedFields is provided
            if (data.includedFields && !data.includedFields.includes(key)) {
                continue;
            }
            // Skip default value columns in create schema examples
            if (data.schemaType === "create" && column.isDefaultValueColumn) {
                continue;
            }
            // Filter out columns with no permissions (root-only access)
            const accessControl = columnAccessControl[key];
            if (accessControl) {
                let hasPermissions = false;
                // Check if column has any permissions defined for the current operation
                if (data.schemaType === "create" &&
                    accessControl.create &&
                    accessControl.create.length > 0) {
                    hasPermissions = true;
                }
                else if (data.schemaType === "read" &&
                    accessControl.read &&
                    accessControl.read.length > 0) {
                    hasPermissions = true;
                }
                else if (data.schemaType === "update" &&
                    accessControl.update &&
                    accessControl.update.length > 0) {
                    hasPermissions = true;
                }
                else if (data.schemaType === "delete") {
                    // For delete operations, we don't filter by column permissions
                    hasPermissions = true;
                }
                // If no permissions are defined for this operation, exclude the column
                if (!hasPermissions) {
                    continue;
                }
            }
            // For create and update, only include required fields
            if ((data.schemaType === "create" || data.schemaType === "update") &&
                !column.required) {
                continue;
            }
            // Generate example value based on column type
            example[key] = this.generateExampleValueForColumn(column);
        }
        return example;
    }
    static generateExampleValueForColumn(column) {
        switch (column.type) {
            case TableColumnType.ObjectID:
                return "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
            case TableColumnType.ShortText:
                return this.getShortTextExample();
            case TableColumnType.LongText:
                return this.getLongTextExample();
            case TableColumnType.Email:
                return "user@example.com";
            case TableColumnType.Phone:
                return "+1-555-0123";
            case TableColumnType.LongURL:
                return "https://example.com";
            case TableColumnType.ShortURL:
                return "https://example.com";
            case TableColumnType.Number:
            case TableColumnType.SmallNumber:
            case TableColumnType.BigNumber:
                return 42;
            case TableColumnType.PositiveNumber:
            case TableColumnType.SmallPositiveNumber:
            case TableColumnType.BigPositiveNumber:
                return 100;
            case TableColumnType.Boolean:
                return true;
            case TableColumnType.Date:
                return {
                    _type: "DateTime",
                    value: "2023-10-01T12:00:00Z",
                };
            case TableColumnType.Color:
                return "#FF0000";
            case TableColumnType.JSON:
                return { key: "value", nested: { data: 123 } };
            case TableColumnType.Entity:
                return {
                    _id: "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
                };
            case TableColumnType.EntityArray:
                return [
                    { _id: "cccccccc-cccc-cccc-cccc-cccccccccccc" },
                    { _id: "dddddddd-dddd-dddd-dddd-dddddddddddd" },
                ];
            case TableColumnType.File:
                return {
                    _id: "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee",
                    name: "example-file.pdf",
                    type: "application/pdf",
                };
            case TableColumnType.Version:
                return 1;
            case TableColumnType.Port:
                return 8080;
            case TableColumnType.HashedString:
                return "hashed_value_here";
            case TableColumnType.Slug:
                return "example-slug";
            case TableColumnType.Permission:
                return "read";
            default:
                return null;
        }
    }
    static getShortTextExample() {
        return "Example Text";
    }
    static getLongTextExample() {
        return "This is an example of longer text content that provides detailed information.";
    }
}
//# sourceMappingURL=ModelSchema.js.map