import { BadRequestException, ConflictException, HttpException, InternalServerErrorException, NotAcceptableException } from '@nestjs/common';
import { PcduserDto } from 'src/auth/dto/pcd-user.dto';
import { FindManyOptions, Not, Repository } from 'typeorm';
import { PcdFormGridInterface } from '../interface/pcd-form-grid.interface';
import * as config from 'config'
const oracledb = require('oracledb');
const dbConfig = config.get('db');


export class PcdFormGridService<T> implements PcdFormGridInterface {
    MAIN_KEY: string = 'id';
    CUSTID: string = 'custId';
    MANDATORY_FEILDS: string[] = [];
    DUPLICATE_CHK: string[] = [];
    FK_FEILDS: { column: string, relation: Repository<any> }[] = [];
    ROW_COUNT:string='';
    constructor(
        //    @Inject(TENANT_CONNECTION) conn:Connection,
        private repo: Repository<any>
    ) { }
    async preFindAll(filter: FindManyOptions, user: PcduserDto): Promise<boolean> {
        return true;
    }
    async findAll(filter: FindManyOptions, user: PcduserDto): Promise<any[]> {
        if (!filter.where) filter.where = {};

        if (!await this.preFindAll(filter, user))
            throw new BadRequestException(`Unepected Error Occured in findAll`);
        // console.log("====1==== filter===", filter);
        const result = await this.repo.find(filter);
        // console.log("====2==== result===", result);
        return await this.postFindAll(filter, user, result);
    }



    async postFindAll(filter: FindManyOptions, user: PcduserDto, result: T[]): Promise<T[]> {
        return result;
    }

    async preFindOne(filter: FindManyOptions, id: string | number, user: PcduserDto): Promise<boolean> {
        return true;
    }

    async findOne(filter: FindManyOptions, id: string | number, user: PcduserDto): Promise<any> {
        if (!filter.where) filter.where = {};

        filter.where[this.MAIN_KEY] = id;

        if (!await this.preFindOne(filter, id, user))
            throw new BadRequestException(`Unepected Error Occured in findAll`);

        const result = await this.repo.findOne(filter);

        return await this.postFindOne(filter, id, user, result);
    }

    async postFindOne(filter: FindManyOptions, id: string | number, user: PcduserDto, result: T): Promise<T> {
        return result;
    }
    async preCreateOne(body: T, user: PcduserDto): Promise<boolean> {
        return true;
    }


    async createOne(body: any, user: PcduserDto): Promise<any> {
        for (let colMan of this.MANDATORY_FEILDS) {
            if (!body[colMan] || body[colMan].toString().trim() == "") {
                throw new BadRequestException(`${colMan} field Cannot be Blank`);
            }
        }
        console.log("==this.DUPLICATE_CHK===", this.DUPLICATE_CHK);

        for (let dupCol of this.DUPLICATE_CHK) {
            const dupfilter = { where: {} };
            // if (!dupCol.isSl || (user.coCodeD.secondryLangauge && user.coCodeD.multiLanguageYn == 'Y' && dupCol.isSl)) {
            // if (dupCol && isArray(dupCol) && dupCol.column.length > 0) {
            // dupCol.forEach(element => {
            //     console.log("===element====", element);
            dupfilter.where[dupCol] = body[dupCol];
            // })
            // }
            // else
            //     dupfilter.where[dupCol.column] = body[dupCol.column];
            // }
            // else
            //     continue;
            console.log("===dupRec filter====", dupfilter);

            let dupRec = await this.repo.findOne(dupfilter);
            if (dupRec)
                throw new BadRequestException(`Duplication not allowed for ${dupCol}`);

        }

        console.log("==user.userId==", user);
        if (user && user.custId) {
            body["createdBy"] = user.userId;
            body["modifiedBy"] = user.userId;
            body[this.CUSTID] = user.custId;
        }

        if (!await this.preCreateOne(body, user))
            throw new BadRequestException(`Unexpected Error Occured in create`);
        for (let eagerCol of await this.getEagerColumns()) {
            delete body[eagerCol];
        }
        try {
            await this.repo.save(body);
            const filter: FindManyOptions = await this.getFilterWithPrimaryKey(body);
            const result = await this.findOne(filter, body[this.MAIN_KEY], user);

            return await this.postCreateOne(body, user, result);
        } catch (err) {
            console.log("==err==", err);
            throw new InternalServerErrorException(err);
        }
    }
    async postCreateOne(body: T, user: PcduserDto, result: T): Promise<T> {
        return result;
    }
    async preUpdateOne(body: T, id: string | number, user: PcduserDto): Promise<boolean> {
        return true;
    }
    async updateOne(body: any, id: string | number, user: PcduserDto): Promise<any> {
        console.log("====user====", user);
        for (let colMan of this.MANDATORY_FEILDS) {

            if (!body[colMan] || body[colMan].toString().trim() == "") {
                throw new BadRequestException(`${colMan} field Cannot be Blank`);
            }
        }

        for (let dupCol of this.DUPLICATE_CHK) {
            const dupfilter = { where: {} };
            dupfilter.where[this.MAIN_KEY] = Not(body[this.MAIN_KEY]);

            // if (!dupCol.isSl || (user.coCodeD.secondryLangauge && user.coCodeD.multiLanguageYn == 'Y' && dupCol.isSl)) {
            //     if (dupCol.column && isArray(dupCol.column) && dupCol.column.length > 0) {
            //         dupCol.column.forEach(element => {
            //         dupfilter.where[element] = body[element];
            //     })
            // }
            // else
            dupfilter.where[dupCol] = body[dupCol];
            // }
            // else
            //     continue;

            let dupRec = await this.repo.findOne(dupfilter);
            if (dupRec)
                throw new BadRequestException(`Duplication not allowed for ${dupCol}`);

        }
        const filter: FindManyOptions = await this.getFilterWithPrimaryKey(body);
        const updateRow = await this.getOne(filter, body[this.MAIN_KEY], user);
        console.log("====updateRow====", updateRow);

        for (let eagerCol of await this.getEagerColumns()) {
            delete body[eagerCol];
        }
        // updateRow["coCode"] = user.coCode;
        const COLUMNS = await this.getAllColumns();
        const PRIMARY_KEYS = await this.getPrimaryKeys();

        COLUMNS.forEach((row) => {
            if (!PRIMARY_KEYS.includes(row))
                updateRow[row] = body[row];
        });
        console.log("====update row 1 ====", updateRow);
        await this.repo.save(updateRow);
        // const filter: FindManyOptions = await this.getFilterWithPrimaryKey(body);
        const result = await this.findOne(filter, body[this.MAIN_KEY], user);
        return await this.postUpdateOne(body, id, user, result);
    }

    async postUpdateOne(body: T, id: string | number, user: PcduserDto, result: T): Promise<T> {
        return result;
    }

    async preDeleteOne(filter: FindManyOptions, id: string | number, user: PcduserDto): Promise<boolean> {
        for (let fkRel of this.FK_FEILDS) {
            const fkFilter = { where: {} };
            fkFilter.where[fkRel.column] = id;
            const result = await fkRel.relation.findOne(fkFilter);
            if (result)
                throw new ConflictException(`Can't delete Record. Detail Records Exists.`);
        }
        return true;
    }


    async deleteOne(filter: FindManyOptions, id: string | number, user: PcduserDto): Promise<any> {
        if (!filter.where) filter.where = {};
        filter.where[this.MAIN_KEY] = id;
        if (this.CUSTID)
            filter.where[this.CUSTID] = user.custId;
        if (!await this.preDeleteOne(filter, id, user))
            throw new BadRequestException(`Unexpected Error Occured in delete`);

        const delFilter = await this.getDeleteFilter(filter);

        if (!delFilter[this.MAIN_KEY])
            throw new NotAcceptableException(`Please select a data to delete`);
        try {
            const result = await this.repo.delete(delFilter);
            return await this.postDeleteOne(filter, id, user, result);
        } catch (err) {
            if (err.status) throw new HttpException(err.message, err.status);

            throw new InternalServerErrorException(`${err.message}`);
        }
    }

    async postDeleteOne(filter: FindManyOptions, id: string | number, user: PcduserDto, result: any): Promise<any> {
        return result;
    }

    async getDeleteFilter(whereFilter: FindManyOptions): Promise<any> {
        const filter: FindManyOptions = {};
        const PRIMARY_KEYS = await this.getPrimaryKeys();
        await PRIMARY_KEYS.forEach((column) => {
            filter[column] = whereFilter.where[column];
        });
        return filter;
    }

    async getOne(filter: FindManyOptions, id: string | number, user: PcduserDto): Promise<any> {
        if (!filter.where) filter.where = {};
        if (this.CUSTID)
            filter.where[this.CUSTID] = user.custId;
        filter.where[this.MAIN_KEY] = id;

        const result = await this.repo.findOne(filter);

        return result;
    }
    async getAllColumns(): Promise<string[]> {
        const COLUMNS: string[] = [];
        for (let columnProperty in this.repo.metadata.propertiesMap) {
            COLUMNS.push(this.repo.metadata.propertiesMap[columnProperty]);
        }
        // in the below method it will not take the forignKey columns Like modelD etc..
        // await this.repo.metadata.columns.forEach((row) => {
        //     COLUMNS.push(row.propertyName);
        // });
        return COLUMNS;
    }

    async getPrimaryKeys(): Promise<string[]> {
        const PRIMARY_KEYS: string[] = [];
        await this.repo.metadata.primaryColumns.forEach((row) => {
            PRIMARY_KEYS.push(row.propertyName);
        });
        return PRIMARY_KEYS;
    }

    async getFilterWithPrimaryKey(body: T): Promise<FindManyOptions> {
        const filter: FindManyOptions = { where: {} };
        const PRIMARY_KEYS = await this.getPrimaryKeys();
        await PRIMARY_KEYS.forEach((column) => {
            filter.where[column] = body[column];
        });
        return filter;
    }


    async getEagerColumns(): Promise<string[]> {
        /* 
        For getting Eager true columns having Many to One relations.
        We need to remove the eager columns before insertions or updations or it will not allow to save data (But save message will show, but data not updated) 
         */
        const COLUMNS: string[] = [];
        for (let columnProperty of this.repo.metadata.eagerRelations) {
            COLUMNS.push(columnProperty.propertyName);
        }
        return COLUMNS;
    }

   

}