var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { Resolver, Query, Arg, Ctx, ObjectType, Field, InputType } from "type-graphql";
import { Metadata, LogError } from "@memberjunction/core";
import { ConnectorFactory } from "@memberjunction/integration-engine";
import { SchemaBuilder, TypeMapper } from "@memberjunction/integration-schema-builder";
import { ResolverBase } from "../generic/ResolverBase.js";
// --- Output Types ---
let ExternalObjectOutput = class ExternalObjectOutput {
};
__decorate([
    Field(),
    __metadata("design:type", String)
], ExternalObjectOutput.prototype, "Name", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], ExternalObjectOutput.prototype, "Label", void 0);
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], ExternalObjectOutput.prototype, "SupportsIncrementalSync", void 0);
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], ExternalObjectOutput.prototype, "SupportsWrite", void 0);
ExternalObjectOutput = __decorate([
    ObjectType()
], ExternalObjectOutput);
let DiscoverObjectsOutput = class DiscoverObjectsOutput {
};
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], DiscoverObjectsOutput.prototype, "Success", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], DiscoverObjectsOutput.prototype, "Message", void 0);
__decorate([
    Field(() => [ExternalObjectOutput], { nullable: true }),
    __metadata("design:type", Array)
], DiscoverObjectsOutput.prototype, "Objects", void 0);
DiscoverObjectsOutput = __decorate([
    ObjectType()
], DiscoverObjectsOutput);
let ExternalFieldOutput = class ExternalFieldOutput {
};
__decorate([
    Field(),
    __metadata("design:type", String)
], ExternalFieldOutput.prototype, "Name", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], ExternalFieldOutput.prototype, "Label", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], ExternalFieldOutput.prototype, "DataType", void 0);
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], ExternalFieldOutput.prototype, "IsRequired", void 0);
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], ExternalFieldOutput.prototype, "IsUniqueKey", void 0);
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], ExternalFieldOutput.prototype, "IsReadOnly", void 0);
ExternalFieldOutput = __decorate([
    ObjectType()
], ExternalFieldOutput);
let DiscoverFieldsOutput = class DiscoverFieldsOutput {
};
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], DiscoverFieldsOutput.prototype, "Success", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], DiscoverFieldsOutput.prototype, "Message", void 0);
__decorate([
    Field(() => [ExternalFieldOutput], { nullable: true }),
    __metadata("design:type", Array)
], DiscoverFieldsOutput.prototype, "Fields", void 0);
DiscoverFieldsOutput = __decorate([
    ObjectType()
], DiscoverFieldsOutput);
let ConnectionTestOutput = class ConnectionTestOutput {
};
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], ConnectionTestOutput.prototype, "Success", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], ConnectionTestOutput.prototype, "Message", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], ConnectionTestOutput.prototype, "ServerVersion", void 0);
ConnectionTestOutput = __decorate([
    ObjectType()
], ConnectionTestOutput);
// --- Preview Data Types ---
let PreviewRecordOutput = class PreviewRecordOutput {
};
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], PreviewRecordOutput.prototype, "Data", void 0);
PreviewRecordOutput = __decorate([
    ObjectType()
], PreviewRecordOutput);
let PreviewDataOutput = class PreviewDataOutput {
};
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], PreviewDataOutput.prototype, "Success", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], PreviewDataOutput.prototype, "Message", void 0);
__decorate([
    Field(() => [PreviewRecordOutput], { nullable: true }),
    __metadata("design:type", Array)
], PreviewDataOutput.prototype, "Records", void 0);
PreviewDataOutput = __decorate([
    ObjectType()
], PreviewDataOutput);
// --- Schema Preview Types ---
let SchemaPreviewObjectInput = class SchemaPreviewObjectInput {
};
__decorate([
    Field(),
    __metadata("design:type", String)
], SchemaPreviewObjectInput.prototype, "SourceObjectName", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], SchemaPreviewObjectInput.prototype, "SchemaName", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], SchemaPreviewObjectInput.prototype, "TableName", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], SchemaPreviewObjectInput.prototype, "EntityName", void 0);
SchemaPreviewObjectInput = __decorate([
    InputType()
], SchemaPreviewObjectInput);
let SchemaPreviewFileOutput = class SchemaPreviewFileOutput {
};
__decorate([
    Field(),
    __metadata("design:type", String)
], SchemaPreviewFileOutput.prototype, "FilePath", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], SchemaPreviewFileOutput.prototype, "Content", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], SchemaPreviewFileOutput.prototype, "Description", void 0);
SchemaPreviewFileOutput = __decorate([
    ObjectType()
], SchemaPreviewFileOutput);
let SchemaPreviewOutput = class SchemaPreviewOutput {
};
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], SchemaPreviewOutput.prototype, "Success", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], SchemaPreviewOutput.prototype, "Message", void 0);
__decorate([
    Field(() => [SchemaPreviewFileOutput], { nullable: true }),
    __metadata("design:type", Array)
], SchemaPreviewOutput.prototype, "Files", void 0);
__decorate([
    Field(() => [String], { nullable: true }),
    __metadata("design:type", Array)
], SchemaPreviewOutput.prototype, "Warnings", void 0);
SchemaPreviewOutput = __decorate([
    ObjectType()
], SchemaPreviewOutput);
// --- Default Configuration Types ---
let DefaultFieldMappingOutput = class DefaultFieldMappingOutput {
};
__decorate([
    Field(),
    __metadata("design:type", String)
], DefaultFieldMappingOutput.prototype, "SourceFieldName", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], DefaultFieldMappingOutput.prototype, "DestinationFieldName", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", Boolean)
], DefaultFieldMappingOutput.prototype, "IsKeyField", void 0);
DefaultFieldMappingOutput = __decorate([
    ObjectType()
], DefaultFieldMappingOutput);
let DefaultObjectConfigOutput = class DefaultObjectConfigOutput {
};
__decorate([
    Field(),
    __metadata("design:type", String)
], DefaultObjectConfigOutput.prototype, "SourceObjectName", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], DefaultObjectConfigOutput.prototype, "TargetTableName", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], DefaultObjectConfigOutput.prototype, "TargetEntityName", void 0);
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], DefaultObjectConfigOutput.prototype, "SyncEnabled", void 0);
__decorate([
    Field(() => [DefaultFieldMappingOutput]),
    __metadata("design:type", Array)
], DefaultObjectConfigOutput.prototype, "FieldMappings", void 0);
DefaultObjectConfigOutput = __decorate([
    ObjectType()
], DefaultObjectConfigOutput);
let DefaultConfigOutput = class DefaultConfigOutput {
};
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], DefaultConfigOutput.prototype, "Success", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], DefaultConfigOutput.prototype, "Message", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", String)
], DefaultConfigOutput.prototype, "DefaultSchemaName", void 0);
__decorate([
    Field(() => [DefaultObjectConfigOutput], { nullable: true }),
    __metadata("design:type", Array)
], DefaultConfigOutput.prototype, "DefaultObjects", void 0);
DefaultConfigOutput = __decorate([
    ObjectType()
], DefaultConfigOutput);
// --- Resolver ---
/**
 * GraphQL resolver for integration discovery operations.
 * Provides endpoints to test connections, discover objects, and discover fields
 * on external systems via their registered connectors.
 */
let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends ResolverBase {
    /**
     * Discovers available objects/tables in the external system.
     */
    async IntegrationDiscoverObjects(companyIntegrationID, ctx) {
        try {
            const user = this.getAuthenticatedUser(ctx);
            const { connector, companyIntegration } = await this.resolveConnector(companyIntegrationID, user);
            // Cast through unknown to bridge duplicate package type declarations
            // (integration-engine resolves its own node_modules copies of core/core-entities)
            const discoverObjects = connector.DiscoverObjects.bind(connector);
            const objects = await discoverObjects(companyIntegration, user);
            return {
                Success: true,
                Message: `Discovered ${objects.length} objects`,
                Objects: objects.map(o => ({
                    Name: o.Name,
                    Label: o.Label,
                    SupportsIncrementalSync: o.SupportsIncrementalSync,
                    SupportsWrite: o.SupportsWrite
                }))
            };
        }
        catch (e) {
            return this.handleDiscoveryError(e);
        }
    }
    /**
     * Discovers fields on a specific external object.
     */
    async IntegrationDiscoverFields(companyIntegrationID, objectName, ctx) {
        try {
            const user = this.getAuthenticatedUser(ctx);
            const { connector, companyIntegration } = await this.resolveConnector(companyIntegrationID, user);
            // Cast through unknown to bridge duplicate package type declarations
            const discoverFields = connector.DiscoverFields.bind(connector);
            const fields = await discoverFields(companyIntegration, objectName, user);
            return {
                Success: true,
                Message: `Discovered ${fields.length} fields on "${objectName}"`,
                Fields: fields.map(f => ({
                    Name: f.Name,
                    Label: f.Label,
                    DataType: f.DataType,
                    IsRequired: f.IsRequired,
                    IsUniqueKey: f.IsUniqueKey,
                    IsReadOnly: f.IsReadOnly
                }))
            };
        }
        catch (e) {
            return this.handleDiscoveryError(e);
        }
    }
    /**
     * Tests connectivity to the external system.
     */
    async IntegrationTestConnection(companyIntegrationID, ctx) {
        try {
            const user = this.getAuthenticatedUser(ctx);
            const { connector, companyIntegration } = await this.resolveConnector(companyIntegrationID, user);
            // Cast through unknown to bridge duplicate package type declarations
            const testConnection = connector.TestConnection.bind(connector);
            const result = await testConnection(companyIntegration, user);
            return {
                Success: result.Success,
                Message: result.Message,
                ServerVersion: result.ServerVersion
            };
        }
        catch (e) {
            const error = e;
            LogError(`IntegrationTestConnection error: ${error}`);
            return {
                Success: false,
                Message: `Error: ${error.message}`
            };
        }
    }
    /**
     * Returns the connector's default configuration for quick setup.
     * Not all connectors provide defaults — returns Success: false if unavailable.
     */
    async IntegrationGetDefaultConfig(companyIntegrationID, ctx) {
        try {
            const user = this.getAuthenticatedUser(ctx);
            const { connector } = await this.resolveConnector(companyIntegrationID, user);
            const config = connector.GetDefaultConfiguration();
            if (!config) {
                return {
                    Success: false,
                    Message: 'This connector does not provide a default configuration'
                };
            }
            return {
                Success: true,
                Message: `Default configuration with ${config.DefaultObjects.length} objects`,
                DefaultSchemaName: config.DefaultSchemaName,
                DefaultObjects: config.DefaultObjects.map(o => ({
                    SourceObjectName: o.SourceObjectName,
                    TargetTableName: o.TargetTableName,
                    TargetEntityName: o.TargetEntityName,
                    SyncEnabled: o.SyncEnabled,
                    FieldMappings: o.FieldMappings.map(f => ({
                        SourceFieldName: f.SourceFieldName,
                        DestinationFieldName: f.DestinationFieldName,
                        IsKeyField: f.IsKeyField
                    }))
                }))
            };
        }
        catch (e) {
            const error = e;
            LogError(`IntegrationGetDefaultConfig error: ${error}`);
            return {
                Success: false,
                Message: `Error: ${error.message}`
            };
        }
    }
    /**
     * Generates a DDL preview for creating tables from discovered external objects.
     * Introspects the source schema and runs SchemaBuilder to produce migration SQL.
     */
    async IntegrationSchemaPreview(companyIntegrationID, objects, platform, ctx) {
        try {
            const user = this.getAuthenticatedUser(ctx);
            const { connector, companyIntegration } = await this.resolveConnector(companyIntegrationID, user);
            // Introspect schema from the external system
            const introspect = connector.IntrospectSchema.bind(connector);
            const sourceSchema = await introspect(companyIntegration, user);
            // Filter to only requested objects
            const requestedNames = new Set(objects.map(o => o.SourceObjectName));
            const filteredSchema = {
                Objects: sourceSchema.Objects.filter(o => requestedNames.has(o.ExternalName))
            };
            // Build target configs from user input + source schema
            const targetConfigs = this.buildTargetConfigs(objects, filteredSchema, platform);
            // Run SchemaBuilder
            const input = {
                SourceSchema: filteredSchema,
                TargetConfigs: targetConfigs,
                Platform: platform,
                MJVersion: '5.7.0',
                SourceType: companyIntegration.Integration,
                AdditionalSchemaInfoPath: 'additionalSchemaInfo.json',
                MigrationsDir: 'migrations/v2',
                MetadataDir: 'metadata',
                ExistingTables: [],
                EntitySettingsForTargets: {}
            };
            const builder = new SchemaBuilder();
            const output = builder.BuildSchema(input);
            if (output.Errors.length > 0) {
                return {
                    Success: false,
                    Message: `Schema generation failed: ${output.Errors.join('; ')}`,
                    Warnings: output.Warnings
                };
            }
            const allFiles = [
                ...output.MigrationFiles,
                ...(output.AdditionalSchemaInfoUpdate ? [output.AdditionalSchemaInfoUpdate] : []),
                ...output.MetadataFiles
            ];
            return {
                Success: true,
                Message: `Generated ${allFiles.length} files`,
                Files: allFiles.map(f => ({
                    FilePath: f.FilePath,
                    Content: f.Content,
                    Description: f.Description
                })),
                Warnings: output.Warnings.length > 0 ? output.Warnings : undefined
            };
        }
        catch (e) {
            const error = e;
            LogError(`IntegrationSchemaPreview error: ${error}`);
            return {
                Success: false,
                Message: `Error: ${error.message}`
            };
        }
    }
    /**
     * Fetches a small sample of records from an external object for preview purposes.
     * Uses the connector's FetchChanges with a small batch size and no watermark.
     */
    async IntegrationPreviewData(companyIntegrationID, objectName, limit, ctx) {
        try {
            const user = this.getAuthenticatedUser(ctx);
            const { connector, companyIntegration } = await this.resolveConnector(companyIntegrationID, user);
            const fetchChanges = connector.FetchChanges.bind(connector);
            const result = await fetchChanges({
                CompanyIntegration: companyIntegration,
                ObjectName: objectName,
                WatermarkValue: null,
                BatchSize: Math.min(limit, 10),
                ContextUser: user
            });
            return {
                Success: true,
                Message: `Fetched ${result.Records.length} preview records`,
                Records: result.Records.map(r => ({
                    Data: JSON.stringify(r.Fields)
                }))
            };
        }
        catch (e) {
            const error = e;
            LogError(`IntegrationPreviewData error: ${error}`);
            return {
                Success: false,
                Message: `Error: ${error.message}`
            };
        }
    }
    // --- Private Helpers ---
    buildTargetConfigs(objects, sourceSchema, platform) {
        const mapper = new TypeMapper();
        return objects.map(obj => {
            const sourceObj = sourceSchema.Objects.find(o => o.ExternalName === obj.SourceObjectName);
            const columns = (sourceObj?.Fields ?? []).map(f => ({
                SourceFieldName: f.Name,
                TargetColumnName: f.Name.replace(/[^A-Za-z0-9_]/g, '_'),
                TargetSqlType: mapper.MapSourceType(f.SourceType, platform, f),
                IsNullable: !f.IsRequired,
                MaxLength: f.MaxLength,
                Precision: f.Precision,
                Scale: f.Scale,
                DefaultValue: f.DefaultValue
            }));
            const primaryKeyFields = (sourceObj?.Fields ?? [])
                .filter(f => f.IsPrimaryKey)
                .map(f => f.Name.replace(/[^A-Za-z0-9_]/g, '_'));
            return {
                SourceObjectName: obj.SourceObjectName,
                SchemaName: obj.SchemaName,
                TableName: obj.TableName,
                EntityName: obj.EntityName,
                Columns: columns,
                PrimaryKeyFields: primaryKeyFields.length > 0 ? primaryKeyFields : ['ID'],
                SoftForeignKeys: []
            };
        });
    }
    getAuthenticatedUser(ctx) {
        const user = ctx.userPayload.userRecord;
        if (!user) {
            throw new Error("User is not authenticated");
        }
        return user;
    }
    /**
     * Loads the CompanyIntegration + its parent Integration, then resolves the
     * appropriate connector via ConnectorFactory.
     *
     * NOTE: Entity objects loaded here come from the MJServer's copy of core-entities.
     * The integration-engine package may resolve its own copy of core-entities, causing
     * TypeScript nominal type mismatches. At runtime the objects are structurally identical,
     * so we cast through `unknown` at the boundary calls.
     */
    async resolveConnector(companyIntegrationID, contextUser) {
        const md = new Metadata();
        // Load the CompanyIntegration record
        const companyIntegration = await md.GetEntityObject('MJ: Company Integrations', contextUser);
        const loaded = await companyIntegration.Load(companyIntegrationID);
        if (!loaded) {
            throw new Error(`CompanyIntegration with ID "${companyIntegrationID}" not found`);
        }
        // Load the parent Integration record
        const integration = await md.GetEntityObject('MJ: Integrations', contextUser);
        const integrationLoaded = await integration.Load(companyIntegration.IntegrationID);
        if (!integrationLoaded) {
            throw new Error(`Integration with ID "${companyIntegration.IntegrationID}" not found`);
        }
        // ConnectorFactory expects its own copy of core-entities types — cast through unknown
        const connector = ConnectorFactory.Resolve(integration);
        return { connector, companyIntegration };
    }
    handleDiscoveryError(e) {
        const error = e;
        LogError(`Integration discovery error: ${error}`);
        return {
            Success: false,
            Message: `Error: ${error.message}`
        };
    }
};
__decorate([
    Query(() => DiscoverObjectsOutput),
    __param(0, Arg("companyIntegrationID")),
    __param(1, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], IntegrationDiscoveryResolver.prototype, "IntegrationDiscoverObjects", null);
__decorate([
    Query(() => DiscoverFieldsOutput),
    __param(0, Arg("companyIntegrationID")),
    __param(1, Arg("objectName")),
    __param(2, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String, Object]),
    __metadata("design:returntype", Promise)
], IntegrationDiscoveryResolver.prototype, "IntegrationDiscoverFields", null);
__decorate([
    Query(() => ConnectionTestOutput),
    __param(0, Arg("companyIntegrationID")),
    __param(1, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], IntegrationDiscoveryResolver.prototype, "IntegrationTestConnection", null);
__decorate([
    Query(() => DefaultConfigOutput),
    __param(0, Arg("companyIntegrationID")),
    __param(1, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], IntegrationDiscoveryResolver.prototype, "IntegrationGetDefaultConfig", null);
__decorate([
    Query(() => SchemaPreviewOutput),
    __param(0, Arg("companyIntegrationID")),
    __param(1, Arg("objects", () => [SchemaPreviewObjectInput])),
    __param(2, Arg("platform", { defaultValue: "sqlserver" })),
    __param(3, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Array, String, Object]),
    __metadata("design:returntype", Promise)
], IntegrationDiscoveryResolver.prototype, "IntegrationSchemaPreview", null);
__decorate([
    Query(() => PreviewDataOutput),
    __param(0, Arg("companyIntegrationID")),
    __param(1, Arg("objectName")),
    __param(2, Arg("limit", { defaultValue: 5 })),
    __param(3, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String, Number, Object]),
    __metadata("design:returntype", Promise)
], IntegrationDiscoveryResolver.prototype, "IntegrationPreviewData", null);
IntegrationDiscoveryResolver = __decorate([
    Resolver()
], IntegrationDiscoveryResolver);
export { IntegrationDiscoveryResolver };
//# sourceMappingURL=IntegrationDiscoveryResolver.js.map