"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generate = void 0;
const yaml = require("js-yaml");
const fs = require("fs");
const path = require("path");
const handlebars = require("handlebars");
const utils_1 = require("../../utils");
const dbt_project_1 = require("../templates/dbt_project");
const dbt_sample_profile_1 = require("../templates/dbt_sample_profile");
const extraSchemas = require("../templates/dbt_extra_schema");
const client_types_1 = require("@syftdata/common/lib/client_types");
const EXCLUDED = ['UserIdentity', 'Index', 'TrackedEvent', 'PageEvent'];
function ignoreSchema(schema) {
    if (!(schema.exported ?? false))
        return true;
    if (EXCLUDED.includes(schema.name))
        return true;
    return false;
}
function getModelName(dataset, event) {
    return `stg_${event}`;
}
function getEventModel(ast, schema) {
    const eventName = (0, utils_1.getSQLFriendlyEventName)(schema.name);
    const modelName = getModelName(ast.config.projectName, eventName);
    return {
        name: modelName,
        description: schema.documentation,
        columns: schema.fields.map((field) => {
            const column = {
                name: field.name,
                description: field.documentation,
                tests: []
            };
            if (!field.isOptional) {
                column.tests.push('not_null');
            }
            return column;
        })
    };
}
function generateConfigYaml(ast, destinationDir) {
    const models = ast.eventSchemas
        .filter((val) => !ignoreSchema(val))
        .map((val) => getEventModel(ast, val));
    fs.writeFileSync(path.join(destinationDir, 'models', 'schema.yml'), yaml.dump({
        models,
        version: 2
    }));
}
function generateColumns(schema) {
    return schema.fields.map((field) => field.name).join(',\n');
}
function generateModel(name, schema, bqConfig) {
    return `
SELECT

${generateColumns(schema)}

FROM \`${bqConfig.projectId}\`.${bqConfig.dataset}.${name}
  `;
}
function generateModels(ast, destinationDir, bqConfig) {
    ast.eventSchemas.forEach((schema) => {
        if (ignoreSchema(schema))
            return;
        const eventName = (0, utils_1.getSQLFriendlyEventName)(schema.name);
        fs.writeFileSync(path.join(destinationDir, 'models', `${getModelName(ast.config.projectName, eventName)}.sql`), generateModel(eventName, schema, bqConfig));
    });
}
function getDbtProjectTemplate() {
    return handlebars.compile(dbt_project_1.default, { noEscape: true });
}
function getSampleProfleTemplate() {
    return handlebars.compile(dbt_sample_profile_1.default, { noEscape: true });
}
function generateDbtProject(ast) {
    const template = getDbtProjectTemplate();
    return template({
        project_name: ast.config.projectName,
        schema_version: ast.config?.version ?? '0.0.1',
        profile: ast.config.projectName
    });
}
function generateSampleProfile(ast, bqConfig) {
    const template = getSampleProfleTemplate();
    return template({
        profile: ast.config.projectName,
        project: bqConfig.projectId,
        dataset: bqConfig.dataset
    });
}
function addSegmentSchemas(ast) {
    ast.eventSchemas.forEach((schema) => {
        extraSchemas.BQ_SEGMENT_SCHEMA.forEach((obj) => schema.fields.push({
            name: obj.name,
            type: { name: obj.type, zodType: 'z.string()' },
            documentation: obj.description,
            isOptional: false
        }));
    });
}
function addHeapSchemas(ast, platform) {
    ast.eventSchemas.forEach((schema) => {
        extraSchemas.BQ_HEAP_EVENT_SCHEMA_ALL.forEach((obj) => schema.fields.push({
            name: obj.name,
            type: { name: obj.type, zodType: 'z.string()' },
            documentation: obj.description,
            isOptional: false
        }));
        let extraSchema;
        if (platform === 'Web') {
            extraSchema = extraSchemas.BQ_HEAP_EVENT_SCHEMA_WEB;
        }
        else if (platform === 'iOS') {
            extraSchema = extraSchemas.BQ_HEAP_EVENT_SCHEMA_IOS;
        }
        else if (platform === 'Android') {
            extraSchema = extraSchemas.BQ_HEAP_EVENT_SCHEMA_ANDROID;
        }
        else {
            throw new Error('Platform needs to be specified for HeapIO: [Web, iOS, Android]');
        }
        extraSchema.forEach((obj) => schema.fields.push({
            name: obj.name,
            type: { name: obj.type, zodType: 'z.string()' },
            documentation: obj.description,
            isOptional: false
        }));
    });
    const users = {
        name: 'users',
        zodType: 'z.object({})',
        fields: [],
        traits: [],
        eventType: client_types_1.SyftEventType.IDENTIFY
    };
    extraSchemas.BQ_HEAP_USERS_SCHEMA.forEach((obj) => users.fields.push({
        name: obj.name,
        type: { name: obj.type, zodType: 'z.string()' },
        documentation: obj.description,
        isOptional: false
    }));
    const sessions = {
        name: 'sessions',
        zodType: 'z.object({})',
        fields: [],
        traits: [],
        eventType: client_types_1.SyftEventType.TRACK
    };
    extraSchemas.BQ_HEAP_SESSIONS_SCHEMA_ALL.forEach((obj) => sessions.fields.push({
        name: obj.name,
        type: {
            name: obj.type,
            zodType: 'z.string()'
        },
        documentation: obj.description,
        isOptional: false
    }));
    let extraSchema;
    if (platform === 'Web') {
        extraSchema = extraSchemas.BQ_HEAP_SESSIONS_SCHEMA_WEB;
    }
    else if (platform === 'iOS') {
        extraSchema = extraSchemas.BQ_HEAP_SESSIONS_SCHEMA_IOS;
    }
    else if (platform === 'Android') {
        extraSchema = extraSchemas.BQ_HEAP_SESSIONS_SCHEMA_ANDROID;
    }
    else {
        throw new Error('Platform needs to be specified for HeapIO: [Web, iOS, Android]');
    }
    extraSchema.forEach((obj) => sessions.fields.push({
        name: obj.name,
        type: {
            name: obj.type,
            zodType: 'z.string()'
        },
        documentation: obj.description,
        isOptional: false
    }));
    const pageviews = {
        name: 'pageviews',
        zodType: 'z.object({})',
        fields: [],
        traits: [],
        eventType: client_types_1.SyftEventType.PAGE
    };
    extraSchemas.BQ_HEAP_PAGEVIEWS_SCHEMA_ALL.forEach((obj) => pageviews.fields.push({
        name: obj.name,
        type: {
            name: obj.type,
            zodType: 'z.string()'
        },
        documentation: obj.description,
        isOptional: false
    }));
    if (platform === 'Web') {
        extraSchema = extraSchemas.BQ_HEAP_PAGEVIEWS_SCHEMA_WEB;
    }
    else if (platform === 'iOS') {
        extraSchema = extraSchemas.BQ_HEAP_PAGEVIEWS_SCHEMA_IOS;
    }
    else if (platform === 'Android') {
        extraSchema = extraSchemas.BQ_HEAP_PAGEVIEWS_SCHEMA_ANDROID;
    }
    else {
        throw new Error('Platform needs to be specified for HeapIO: [Web, iOS, Android]');
    }
    extraSchema.forEach((obj) => pageviews.fields.push({
        name: obj.name,
        type: {
            name: obj.type,
            zodType: 'z.string()'
        },
        documentation: obj.description,
        isOptional: false
    }));
    ast.eventSchemas.push(users, sessions, pageviews);
}
function addExtraSchemasForProvider(ast, providerConfig) {
    if (providerConfig.destination === 'Segment') {
        addSegmentSchemas(ast);
    }
    else if (providerConfig.destination === 'Heap') {
        addHeapSchemas(ast, providerConfig.platform);
    }
    else {
        throw new Error('Destination needs to be specified: [Segment, Heap]');
    }
}
function generate(ast, destinationDir, bqConfig, providerConfig) {
    (0, utils_1.createDir)(destinationDir);
    (0, utils_1.createDir)(path.join(destinationDir, 'models'));
    (0, utils_1.createDir)(path.join(destinationDir, 'target'));
    (0, utils_1.createDir)(path.join(destinationDir, 'macros'));
    (0, utils_1.createDir)(path.join(destinationDir, 'seeds'));
    (0, utils_1.createDir)(path.join(destinationDir, 'snapshots'));
    (0, utils_1.createDir)(path.join(destinationDir, 'tests'));
    (0, utils_1.createDir)(path.join(destinationDir, 'analyses'));
    addExtraSchemasForProvider(ast, providerConfig);
    (0, utils_1.logInfo)(`Generating DBT project file..`);
    fs.writeFileSync(path.join(destinationDir, 'dbt_project.yml'), generateDbtProject(ast));
    (0, utils_1.logInfo)(`Generating sample DBT profiles file..`);
    fs.writeFileSync(path.join(destinationDir, 'profiles.yml'), generateSampleProfile(ast, bqConfig));
    (0, utils_1.logInfo)(`Generating DBT models..`);
    generateModels(ast, destinationDir, bqConfig);
    (0, utils_1.logInfo)(`Generating documentation..`);
    generateConfigYaml(ast, destinationDir);
    (0, utils_1.logInfo)(`:sparkles: DBT models are generated successfully!`);
}
exports.generate = generate;
