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 { Arg, Ctx, Field, InputType, ObjectType, Query } from 'type-graphql';
import { LogError, LogStatus } from '@memberjunction/core';
import { RequireSystemUser } from '../directives/RequireSystemUser.js';
import { v4 as uuidv4 } from 'uuid';
import { GetReadOnlyDataSource, GetReadOnlyProvider } from '../util.js';
import sql from 'mssql';
let GetDataInputType = class GetDataInputType {
    Token;
    Queries;
};
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], GetDataInputType.prototype, "Token", void 0);
__decorate([
    Field(() => [String]),
    __metadata("design:type", Array)
], GetDataInputType.prototype, "Queries", void 0);
GetDataInputType = __decorate([
    InputType()
], GetDataInputType);
export { GetDataInputType };
let GetDataOutputType = class GetDataOutputType {
    Success;
    Queries;
    ErrorMessages;
    Results;
};
__decorate([
    Field(() => Boolean),
    __metadata("design:type", Boolean)
], GetDataOutputType.prototype, "Success", void 0);
__decorate([
    Field(() => [String]),
    __metadata("design:type", Array)
], GetDataOutputType.prototype, "Queries", void 0);
__decorate([
    Field(() => [String], { nullable: 'itemsAndList' }),
    __metadata("design:type", Array)
], GetDataOutputType.prototype, "ErrorMessages", void 0);
__decorate([
    Field(() => [String], { nullable: 'itemsAndList' }),
    __metadata("design:type", Array)
], GetDataOutputType.prototype, "Results", void 0);
GetDataOutputType = __decorate([
    ObjectType()
], GetDataOutputType);
export { GetDataOutputType };
let SimpleEntityResultType = class SimpleEntityResultType {
    Success;
    ErrorMessage;
    Results;
};
__decorate([
    Field(() => Boolean),
    __metadata("design:type", Boolean)
], SimpleEntityResultType.prototype, "Success", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], SimpleEntityResultType.prototype, "ErrorMessage", void 0);
__decorate([
    Field(() => [SimpleEntityOutputType]),
    __metadata("design:type", Array)
], SimpleEntityResultType.prototype, "Results", void 0);
SimpleEntityResultType = __decorate([
    ObjectType()
], SimpleEntityResultType);
export { SimpleEntityResultType };
let SimpleEntityOutputType = class SimpleEntityOutputType {
    ID;
    Name;
    Description;
    SchemaName;
    BaseView;
    BaseTable;
    CodeName;
    ClassName;
    Fields;
};
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], SimpleEntityOutputType.prototype, "ID", void 0);
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], SimpleEntityOutputType.prototype, "Name", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], SimpleEntityOutputType.prototype, "Description", void 0);
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], SimpleEntityOutputType.prototype, "SchemaName", void 0);
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], SimpleEntityOutputType.prototype, "BaseView", void 0);
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], SimpleEntityOutputType.prototype, "BaseTable", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], SimpleEntityOutputType.prototype, "CodeName", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], SimpleEntityOutputType.prototype, "ClassName", void 0);
__decorate([
    Field(() => [SimpleEntityFieldOutputType]),
    __metadata("design:type", Array)
], SimpleEntityOutputType.prototype, "Fields", void 0);
SimpleEntityOutputType = __decorate([
    ObjectType()
], SimpleEntityOutputType);
export { SimpleEntityOutputType };
let SimpleEntityFieldOutputType = class SimpleEntityFieldOutputType {
    ID;
    Name;
    Description;
    Type;
    AllowsNull;
    MaxLength;
};
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], SimpleEntityFieldOutputType.prototype, "ID", void 0);
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], SimpleEntityFieldOutputType.prototype, "Name", void 0);
__decorate([
    Field(() => String, { nullable: true }),
    __metadata("design:type", String)
], SimpleEntityFieldOutputType.prototype, "Description", void 0);
__decorate([
    Field(() => String),
    __metadata("design:type", String)
], SimpleEntityFieldOutputType.prototype, "Type", void 0);
__decorate([
    Field(() => Boolean),
    __metadata("design:type", Boolean)
], SimpleEntityFieldOutputType.prototype, "AllowsNull", void 0);
__decorate([
    Field(() => Number),
    __metadata("design:type", Number)
], SimpleEntityFieldOutputType.prototype, "MaxLength", void 0);
SimpleEntityFieldOutputType = __decorate([
    ObjectType()
], SimpleEntityFieldOutputType);
export { SimpleEntityFieldOutputType };
export class GetDataResolver {
    async GetData(input, context) {
        try {
            LogStatus(`GetDataResolver.GetData() ---- IMPORTANT - temporarily using the same connection as rest of the server, we need to separately create a READ ONLY CONNECTION and pass that in 
                       the AppContext so we can use that special connection here to ensure we are using a lower privileged connection for this operation to prevent mutation from being possible.`);
            LogStatus(`${JSON.stringify(input)}`);
            if (!isTokenValid(input.Token)) {
                throw new Error(`Token ${input.Token} is not valid or has expired`);
            }
            const readOnlyDataSource = GetReadOnlyDataSource(context.dataSources, { allowFallbackToReadWrite: false });
            if (!readOnlyDataSource) {
                throw new Error('Read-only data source not found');
            }
            const results = await Promise.allSettled(input.Queries.map(async (query) => {
                try {
                    const request = new sql.Request(readOnlyDataSource);
                    const result = await request.query(query);
                    return { result: result.recordset, error: null };
                }
                catch (err) {
                    let errorMessage = err instanceof Error ? err.message : String(err);
                    if (err && typeof err === 'object') {
                        const sqlError = err;
                        if (sqlError.precedingErrors && sqlError.precedingErrors.length > 0) {
                            const precedingError = sqlError.precedingErrors[0];
                            if (precedingError.message) {
                                errorMessage = precedingError.message;
                            }
                        }
                        if (sqlError.lineNumber && sqlError.lineNumber > 0) {
                            errorMessage = `${errorMessage} (Line ${sqlError.lineNumber})`;
                        }
                    }
                    const firstLine = errorMessage.split('\n')[0];
                    if (firstLine) {
                        errorMessage = firstLine.trim();
                    }
                    return { result: null, error: errorMessage };
                }
            }));
            const processedResults = results.map((res) => res.status === "fulfilled" ? res.value.result : null);
            const errorMessages = results.map((res) => res.status === "fulfilled" ? res.value.error : res.reason);
            const returnVal = { Success: errorMessages.filter((e) => e !== null).length === 0,
                Results: processedResults.map((r) => JSON.stringify(r)),
                Queries: input.Queries,
                ErrorMessages: errorMessages };
            recordTokenUse(input.Token, { request: input, results: returnVal });
            return returnVal;
        }
        catch (err) {
            LogError(err);
            return { Success: false, ErrorMessages: [typeof err === 'string' ? err : err.message], Results: [], Queries: input.Queries };
        }
    }
    async GetAllEntities(context) {
        try {
            const md = GetReadOnlyProvider(context.providers);
            const result = md.Entities.map((e) => {
                return {
                    ID: e.ID,
                    Name: e.Name,
                    Description: e.Description,
                    SchemaName: e.SchemaName,
                    BaseView: e.BaseView,
                    BaseTable: e.BaseTable,
                    CodeName: e.CodeName,
                    ClassName: e.ClassName,
                    Fields: e.Fields.map((f) => {
                        return {
                            ID: f.ID,
                            Name: f.Name,
                            Description: f.Description,
                            Type: f.Type,
                            AllowsNull: f.AllowsNull,
                            MaxLength: f.MaxLength,
                        };
                    })
                };
            });
            return { Success: true, Results: result };
        }
        catch (err) {
            LogError(err);
            return { Success: false, ErrorMessage: typeof err === 'string' ? err : err.message, Results: [] };
        }
    }
}
__decorate([
    RequireSystemUser(),
    Query(() => GetDataOutputType),
    __param(0, Arg('input', () => GetDataInputType)),
    __param(1, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [GetDataInputType, Object]),
    __metadata("design:returntype", Promise)
], GetDataResolver.prototype, "GetData", null);
__decorate([
    RequireSystemUser(),
    Query(() => SimpleEntityResultType),
    __param(0, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], GetDataResolver.prototype, "GetAllEntities", null);
export class TokenUseLog {
    Token;
    UsedAt;
    UsePayload;
}
export class GetDataAccessToken {
    Token;
    ExpiresAt;
    RequstedAt;
    RequestorPayload;
    TokenUses;
}
const __accessTokens = [];
const __defaultTokenLifeSpan = 1000 * 60 * 5;
export function registerAccessToken(token, lifeSpan = __defaultTokenLifeSpan, requestorPayload) {
    const tokenToUse = token || uuidv4();
    if (tokenExists(tokenToUse)) {
        throw new Error(`Token ${tokenToUse} already exists`);
    }
    const newToken = new GetDataAccessToken();
    newToken.Token = tokenToUse;
    newToken.ExpiresAt = new Date(new Date().getTime() + lifeSpan);
    newToken.RequstedAt = new Date();
    newToken.RequestorPayload = requestorPayload;
    __accessTokens.push(newToken);
    return newToken;
}
export function deleteAccessToken(token) {
    const index = __accessTokens.findIndex((t) => t.Token === token);
    if (index >= 0) {
        __accessTokens.splice(index, 1);
    }
    else {
        throw new Error(`Token ${token} does not exist`);
    }
}
export function tokenExists(token) {
    return __accessTokens.find((t) => t.Token === token) !== undefined;
}
export function isTokenValid(token) {
    const t = __accessTokens.find((t) => t.Token === token);
    if (t) {
        return t.ExpiresAt > new Date();
    }
    return false;
}
export function recordTokenUse(token, usePayload) {
    const t = __accessTokens.find((t) => t.Token === token);
    if (t) {
        if (!t.TokenUses) {
            t.TokenUses = [];
        }
        t.TokenUses.push({ Token: token, UsedAt: new Date(), UsePayload: usePayload });
    }
    else {
        throw new Error(`Token ${token} does not exist`);
    }
}
//# sourceMappingURL=GetDataResolver.js.map