import path = require("path");
const appRootDir = path.resolve(process.cwd());    
import * as fse from "fs-extra";
import { CONSTANT } from "../../utils/constants";
import utils from "../../utils/general-utils";
import { IConfig } from "../../interfaces/config";
import {Table} from "../classes/Table"

export const generate = (tables: Array<Table>, config: IConfig) => {
    let fileContent = "";
    fse.removeSync(path.join(config.ORM_GENERATE.path.daos))

    tables.forEach((x) => {
        let refs = [];
        fileContent = "import { Table, Column, Model, HasOne, HasMany, BelongsTo, BelongsToMany, ForeignKey, PrimaryKey, AutoIncrement, AllowNull, Default } from 'sequelize-typescript';\n" +
                 `\n\n${CONSTANT.IMPORT_REF_STATEMENT}\n\n` +
                 `@Table({tableName: "${x.tableName}"})\n` +
                 `export class ${x.tableNameUpperCase} extends Model<${x.tableNameUpperCase}>` +
                 `{\n`;

        
        x.properties.forEach((y) => {
            if (y.isRef) {
                fileContent += `\t@ForeignKey(() => ${utils.capitalizeFirstLetter(y.reference.model)})\n`;
                refs.push(utils.capitalizeFirstLetter(y.reference.model));
            }

            if (y.isAllowNull) {
                fileContent += "\t@AllowNull\n";
            }

            if (y.isPrimaryKey) {
                fileContent += "\t@PrimaryKey\n"
                       + "\t@AutoIncrement\n";
            }

            if (y.isUnique) {
                fileContent += "\t@Unique\n";
            }

            if (y.defaultValue) {
                fileContent += `\t@Default({value: '${y.defaultValue}'})\n`;
            }

            fileContent += `\t@Column({field: '${y.propName}'})\n`;

            const optional = y.isAllowNull || y.isPrimaryKey ? "?" : "";
            
            // handles lookups
            if (y.isLookup) {
                fileContent += `\tpublic ${y.lowerCasePropName}${optional}: ${y.type};\n\n`;

                fileContent += `\t@BelongsTo(() => ${utils.capitalizeFirstLetter(y.lookup.tableName)}, "${y.propName}")  // extra, non-column prop for Lookup\n`
                        + `\tpublic ${y.lookup.propName}${optional}: ${utils.capitalizeFirstLetter(y.lookup.tableName)};\n\n`;
                
                const lookupImportStatement = `import { ${config.ORM_GENERATE.lookup.tableName} } from './${config.ORM_GENERATE.lookup.tableName}';`
                if (!fileContent.includes(lookupImportStatement)) {
                    fileContent = fileContent.replace(CONSTANT.IMPORT_REF_STATEMENT, CONSTANT.IMPORT_REF_STATEMENT + "\n" + lookupImportStatement);
                }

            } else {
                const isCaseSensitiveMatch = config.ORM_GENERATE.caseSensitiveProps.find((x) => x === y.propName);
                if (isCaseSensitiveMatch) {
                    fileContent += `\tpublic ${y.propName}: ${y.type};\n\n`;
                } else {
                    fileContent += `\tpublic ${y.lowerCasePropName}${optional}: ${y.type};\n\n`;
                }
            }
        });

        fileContent += `\n\t${CONSTANT.PROPERTY_REF_STATEMENT}\n\n`;
        fileContent += "}\n";

        fse.outputFileSync(
            path.join(
                config.ORM_GENERATE.path.daos, 
                utils.capitalizeFirstLetter(x.tableName) + ".ts"
            ), fileContent
        );


        // write refs for that file
        let filename = path.join(config.ORM_GENERATE.path.daos, utils.capitalizeFirstLetter(x.tableName) + ".ts");
        refs.forEach((ref) => {
            let myImportRefStatement = `import { ${utils.capitalizeFirstLetter(ref)} } from './${utils.capitalizeFirstLetter(ref)}';\n`;
            let myPropertyRefStatement = null;
            write({myPropertyRefStatement, myImportRefStatement, filename});    
        })    
    });


    //config.shouldWriteRelations
    if (true) {

        // SECOND LOOP
        tables.forEach((x) => {
            x.properties.forEach((y) => {
                let filename = path.join(config.ORM_GENERATE.path.daos, utils.capitalizeFirstLetter(x.tableName) + ".ts");
                let myImportRefStatement = null;
                let myPropertyRefStatement = null;


                if (y.isRef && !y.reference.isToSelf) {
                    // remove 1

                    // refs that are in different files
                    // if (y.reference.isThroughTable && !y.reference.istoSelf) {
                    //     let currentPropertyName = y.propName;

                    //     // get through table imports
                    //     myImportRefStatement = `import ${utils.createModelName(utils.capitalizeFirstLetter(y.reference.model))} from './${utils.createModelName(utils.capitalizeFirstLetter(y.reference.model))}';\n`;
                    //     write({myPropertyRefStatement, myImportRefStatement, filename});

                                
                    //     if (y.isRef && y.reference.isThroughTable) {
                    //         sFilename = path.join(opts.ORM_GENERATE.path.daos, utils.createModelName(utils.capitalizeFirstLetter(y.reference.model)) + ".ts");

                    //         //@BelongsToMany() on other file
                    //         for (let throughTableProp of x.properties) {
                    //             if (throughTableProp.propName !== y.propName && throughTableProp.isRef && throughTableProp.reference.isThroughTable) {
                    //                 myImportRefStatement = `import ${utils.createModelName(utils.capitalizeFirstLetter(y.reference.model))} from './${utils.createModelName(utils.capitalizeFirstLetter(y.reference.model)}';`;
                    //                 myPropertyRefStatement = `\n\t// through table ref\n`
                    //                                         + `\t@BelongsToMany(() => ${utils.createModelName(utils.capitalizeFirstLetter(y.reference.model))}, () => ${utils.createModelName(utils.capitalizeFirstLetter(x.tableName))})\n`
                    //                                         + `\tpublic ${utils.pluralize(utils.createModelName(utils.capitalizeFirstLetter(y.reference.model)))}?: Array<${utils.createModelName(utils.capitalizeFirstLetter(y.reference.model))}>;\n`;
                    //                 sFilename = path.join(opts.ORM_GENERATE.path.daos, throughTableProp.reference.model + ".ts");

                    //                 break;
                    //             }
                    //         }
                    //         write({myPropertyRefStatement, myImportRefStatement, filename: sFilename});

                            
                    //         //@BelongsTo() on other file
                    //         myImportRefStatement = `import ${utils.createModelName(utils.capitalizeFirstLetter(x.tableName))} from './${utils.createModelName(utils.capitalizeFirstLetter(x.tableName))}';`;

                    //         myPropertyRefStatement = `\n\t// through table ref\n`
                    //                                 + `\t@BelongsTo(() => ${utils.createModelName(utils.capitalizeFirstLetter(x.tableName))}, '${opts.primaryKeyIdentifier}')`
                    //                                 + `\n\tpublic ${utils.createModelName(utils.capitalizeFirstLetter(x.tableName))}: ${utils.createModelName(utils.capitalizeFirstLetter(x.tableName))};\n`;

                    //         write({myPropertyRefStatement, myImportRefStatement, filename: sFilename});

                    //     }

                    // // `one to many` import to other file; is not ref to self, is not through table
                    // //  @BelongsTo  &&  @HasMany
                    // } else 
                    if (!y.reference.isMapTable && !y.reference.isToSelf) {

                        // // @BelongsTo on this file
                        myImportRefStatement = `import { ${utils.capitalizeFirstLetter(y.reference.model)} } from './${utils.capitalizeFirstLetter(y.reference.model) }';`;
                        myPropertyRefStatement = `\t// insert bc of one:many- so can query ${utils.capitalizeFirstLetter(x.tableName)}.find({include: [${utils.capitalizeFirstLetter(y.reference.model)}]})\n`
                                                + `\t@BelongsTo(() => ${utils.capitalizeFirstLetter(y.reference.model)}) \n`
                                                + `\tpublic ${y.reference.lowerCaseModel}: ${utils.capitalizeFirstLetter(y.reference.model)};\n`;

                        write({myImportRefStatement, myPropertyRefStatement, filename});                        
                        

                        // // @HasMany on other file
                        filename = path.join(config.ORM_GENERATE.path.daos, utils.capitalizeFirstLetter(y.reference.model)) + ".ts";
                        myImportRefStatement = `import { ${utils.capitalizeFirstLetter(x.tableName)} } from './${utils.capitalizeFirstLetter(x.tableName)}';`;
                        myPropertyRefStatement = `\t//inserted b/c of \`One:Many\` ${utils.capitalizeFirstLetter(y.reference.model)}:${utils.capitalizeFirstLetter(x.tableName)}\n`
                                                + `\t@HasMany(() => ${utils.capitalizeFirstLetter(x.tableName)})\n`
                                                + `\tpublic ${utils.pluralize(x.tableName)}: Array<${utils.capitalizeFirstLetter(x.tableName)}> \n`;
                        
                         write({myPropertyRefStatement, myImportRefStatement, filename});                             
                    }    
                }
            });
        });
    }

    // clean up any self-ref imports
    const files = fse.readdirSync(path.join(config.ORM_GENERATE.path.daos));
    files.forEach((x) => {
        let strFile = fse.readFileSync(path.join(config.ORM_GENERATE.path.daos, x), "utf8");
        let filename = x.slice(0, x.length - 3);
        let importStatement = `import { ${utils.capitalizeFirstLetter(filename)} } from './${utils.capitalizeFirstLetter(filename)}';`;

        if (strFile.indexOf(importStatement) > -1) {
            strFile = strFile.replace(importStatement, "");
            fse.outputFileSync(path.join(config.ORM_GENERATE.path.daos, x), strFile, "utf8");
        }

    });
};


const write = ({myPropertyRefStatement, myImportRefStatement, filename}) => {
    let strFile = "";
    let sFullImportRef = CONSTANT.IMPORT_REF_STATEMENT + "\n" + myImportRefStatement;
    let sFullPropertyRef = CONSTANT.PROPERTY_REF_STATEMENT + "\n" + myPropertyRefStatement;

    fse.ensureFileSync(filename);
    strFile = fse.readFileSync(filename, "utf8");

    //generate property reference
    if (myPropertyRefStatement && strFile.indexOf(myPropertyRefStatement) < 0) {
        strFile = strFile.replace(CONSTANT.PROPERTY_REF_STATEMENT, sFullPropertyRef);
    }

    //generate import reference
    if (myImportRefStatement && strFile.indexOf(myImportRefStatement) < 0) {                
        strFile = strFile.replace(CONSTANT.IMPORT_REF_STATEMENT, sFullImportRef);                    
    }

    fse.outputFileSync(filename, strFile, "utf8");
}

