import {
    Entity, PrimaryGeneratedColumn, Column, Index, Tree, TreeParent, TreeChildren, CreateDateColumn, UpdateDateColumn, VersionColumn,
    BeforeInsert, BeforeRemove, BeforeUpdate, AfterInsert, AfterRemove, AfterUpdate, FindManyOptions, FindOneOptions,
    OneToOne, OneToMany, ManyToOne, JoinColumn, ManyToMany, JoinTable
} from 'typeorm'
import * as __M__ from 'typeorm'
import { SelectQueryBuilder } from './type'

declare global {
    // 数据模型的全局基类修饰符
    abstract class XBaseModel {
        id: any //number
        uuid: any // string
        created_at: any // Date
        updated_at: any // Date
        version: any // number
    }

    // // TypeORM相关的修饰符统一定义（仅仅暴露开发必要的修饰符）
    // const M: {
    //     Entity: typeof Entity
    //     // PrimaryGeneratedColumn: typeof PrimaryGeneratedColumn
    //     Column: typeof Column
    //     Index: typeof Index
    //     // Tree: typeof Tree
    //     // CreateDateColumn: typeof CreateDateColumn
    //     // UpdateDateColumn: typeof UpdateDateColumn
    //     // VersionColumn: typeof VersionColumn
    //     BeforeInsert: typeof BeforeInsert
    //     BeforeRemove: typeof BeforeRemove
    //     BeforeUpdate: typeof BeforeUpdate
    //     AfterInsert: typeof AfterInsert
    //     AfterRemove: typeof AfterRemove
    //     AfterUpdate: typeof AfterUpdate
    //     // OneToOne: typeof OneToOne
    //     // OneToMany: typeof OneToMany
    //     // ManyToOne: typeof ManyToOne
    //     // TreeParent: typeof TreeParent
    //     // TreeChildren: typeof TreeChildren
    //     // JoinColumn: typeof JoinColumn
    //     // ManyToMany: typeof ManyToMany
    //     // JoinTable: typeof JoinTable
    // }

    // Model定义相关的特殊修饰符统一规范定义
    const M: {
        // Model定义中的各种修饰符统一封装
        // Entity: typeof __M__.Entity
        // Column: typeof __M__.Column  // 仅仅暴露必须内容符合外包中台深度定制的开发流程规范（减少学习范围以及增强编码强约束标准化）
        Column: (options?: {
            /**
             * Column name in the database.
             */
            name?: string;

            /**
             * Column type. Must be one of the value from the ColumnTypes class.
             * mysql基本数据类型定义：https://www.runoob.com/mysql/mysql-data-types.html
             * 互联网项目mysql是经历过阿里等大型互联网验证过的架构方案并且有成熟的云产品在项目中首选（serverless项目直接用mysql8.0）
             * decimal 钱相关的小数字段定义教程 https://www.cnblogs.com/owenma/p/7097602.html
             * json 非结构化数据取代mongodb的字段类型详细教程 https://www.jianshu.com/p/25161add5e4b 
             */
            type?: 'char'/* 8位固定长度字符串（仅用于枚举类型以及需要索引优化的字段）*/
            | 'varchar'  /* 16位字节而非字符长度的变长长度字符串（存储效率高），默认的string_t类型转换结果无需用户填写。*/
            // | 'tinytext' /* 8位文本数据，text存储是字符数。太短了毫无意义可以被varchar兼容掉无需定义。 */ 
            | 'text' /* 16位文本数据 */ | 'mediumtext' /* 24位文本数据 */ | 'longtext'/* 32位文本数据 */
            | 'tinyint'  /* 8位整数 */ | 'smallint' /* 16位整数 */ | 'mediumint' /* 24位整数 */
            // | 'int' /* 32位整数，默认的integer_t类型无需用户定义。 */ | 'bigint' /* 64位整数，底层处理逻辑有问题暂不开放尚未想好如何实现，等遇到了需求再考虑。 */
            // ==== 下面的类型都可以在预处理器中自动识别转换为正确的mysql底层数据类型无需用户填写 =====
            // | 'decimal'  /* 可精确计算的小数（各种资金钱的存储需要用这个展现形式）*/ | 'float' /* 32位浮点数 */ | 'double' /* 64位浮点数 */
            // | 'datetime' /* 年-月-日 时:分:秒[.微妙] */ | 'date' /* 年-月-日 */
            // | 'json'     /* JSON数据类型（存储非结构化的数据） */
            // ==>> 对于枚举类型系统约定必须是最长4个字符的char[4]进行表达简化实现过程
            // TODO 对于地理位置数据的扩展支持底层实现（等到下个项目用到了再设计对接支持）
            ;

            /**
             * Puts UNSIGNED attribute on to numeric column. Works only for MySQL.
             */
            unsigned?: boolean;

            /**
             * Column type's length. Used only on some column types.
             * For example type = "string" and length = "100" means that ORM will create a column with type varchar(100).
             */
            length?: string | number;

            /**
             * Column type's display width. Used only on some column types in MySQL.
             * For example, INT(4) specifies an INT with a display width of four digits.
             */
            width?: number;

            /**
             * The precision for a decimal (exact numeric) column (applies only for decimal column), which is the maximum
             * number of digits that are stored for the values.
             */
            precision?: number | null;

            /**
             * The scale for a decimal (exact numeric) column (applies only for decimal column), which represents the number
             * of digits to the right of the decimal point and must not be greater than precision.
             */
            scale?: number;

            /**
             * Defines a column character set.
             * Not supported by all database types.
             */
            charset?: string;

            /**
             * Defines a column collation.
             */
            collation?: string;

            /**
             * Indicates if column is always selected by QueryBuilder and find operations.
             * Default value is "true".
             */
            select?: boolean;

            /**
             * Indicates if column is inserted by default.
             * Default value is "true".
             */
            insert?: boolean;

            /**
             * Indicates if column value is updated by "save" operation.
             * If false, you'll be able to write this value only when you first time insert the object.
             * Default value is "true".
             */
            update?: boolean;

            /**
             * Specifies a value transformer that is to be used to (un)marshal
             * this column when reading or writing to the database.
             */
            transformer?: __M__.ValueTransformer | __M__.ValueTransformer[];

            // /**
            //  * Indicates if column's value can be set to NULL.
            //  */
            // nullable?: boolean;

            /**
             * Default database value.
             */
            default?: any;
        }, type?: string /* 预处理器自动识别的字段类型元信息，此参数禁止用户填写 */) => Function
        Index: typeof __M__.Index
        Tree: typeof __M__.Tree
        TreeChildren: typeof __M__.TreeChildren
        TreeParent: typeof __M__.TreeParent
        // BeforeInsert: typeof __M__.BeforeInsert
        // BeforeUpdate: typeof __M__.BeforeUpdate
        // BeforeRemove: typeof __M__.BeforeRemove
        // 按照laravel与sequelize-typescript的标准风格定义始终关系与关联查询规范，
        // BelongsToOne，BelongsToMany，HasOne，HasMany 显性定义各种关系并且规范好外键字段。自动翻译为typeorm的底层实现完成语义定义风格统一。
        // 将FindOption的功能增强统一使用这种方法，取代掉query builder的方法，与sequlize-typescript保持风格一致。
        // 强制规定所有的数据库的操作都要定义Repository的接口进行统一的封装，限定各种数据库操作优化方法的适配范围。
        //   以业务视角定义业务主对象进行封装实现。基于TXxxx等各种业务数据类型进行业务扩展转换处理，严禁将MXxxx暴露到上层使用。
        //   方便数据库的统一管理与各种优化实施（需要啥子数据与接口就是实现啥子数据与接口，接口与业务逻辑无关 API First 模式进行设计文档规范约定）
        // OneToOne: typeof __M__.OneToOne // TYPEORM 底层的扩展需要被屏蔽掉，仅仅暴露更上层的抽象给到业务层开发使用。
        OneToOne: <T>(TYPE: new () => T, rel: (type: T) => any) => Function
        // OneToMany: typeof __M__.OneToMany
        // ManyToOne: typeof __M__.ManyToOne
        OneToMany: <T>(TYPE: new () => T, rel: (type: T) => any) => Function
        ManyToOne: <T>(TYPE: new () => T, rel: (type: T) => any) => Function
        // JoinColumn: typeof __M__.JoinColumn // 仅仅预处理器使用禁止用户使用，用户需要手动定义外键字段，预处理器根据外键规则，自动补上对应的JoinColumn修饰符满足TypeORM底层框架需要。
        // ManyToMany: typeof __M__.ManyToMany // 暂不支持多对多的关系定义，等下次遇到实际Case之后再针对性优化。
        // JoinTable: typeof __M__.JoinTable
        // ManyToMany: <T>(TYPE?: new () => T, rel?: (type: T) => any, is_main_table?: boolean) => Function
        ManyToMany: <T>(TYPE: new () => T, rel: (type: T) => any) => Function // 根据两张表的名字的字母顺序不同由小到大的顺序定义为主表（根据此约定避免一个额外参数使用并且保持逻辑一致性）

        FindTrees: <T>(TYPE: new () => T) => Function
        // find条件中的操作符统一定义  https://typeorm.io/#/find-options
        opNot: typeof __M__.Not
        opLessThan: typeof __M__.LessThan
        opLessThanOrEqual: typeof __M__.LessThanOrEqual
        opMoreThan: typeof __M__.MoreThan
        opMoreThanOrEqual: typeof __M__.MoreThanOrEqual
        opEqual: typeof __M__.Equal
        opLike: typeof __M__.Like
        opBetween: typeof __M__.Between
        opIn: typeof __M__.In
        opIsNull: typeof __M__.IsNull
        opRaw: typeof __M__.Raw
    }

    // FIXME 兼容老代码的接口调用
    function xnew<T>(TYPE: new () => T, param?: { [P in keyof T]?: any }): T
    function xtransaction(
        callback: () => Promise<any>,
        isolation?: 'READ UNCOMMITTED' | 'READ COMMITTED' | 'REPEATABLE READ' | 'SERIALIZABLE'
    ): Promise<any>
    function xquery<T>(
        TYPE: new () => T, alias: any
    ): SelectQueryBuilder<any>

    namespace xdb {
        // function xnew<T>(TYPE: new () => T, param?: Object): T

        // function connect(callback: () => Promise<void>, cfg?): Promise<void>

        // // nodejs 单线程运行机制 + 上下文闭包函数的线程变量模拟特性，线程局部全局变量特性处理。
        // function transaction(
        //     callback: () => Promise<any>,
        //     isolation?: 'READ UNCOMMITTED' | 'READ COMMITTED' | 'REPEATABLE READ' | 'SERIALIZABLE'
        // ): Promise<any>

        // 插入或更新数据（获取事务上下文动态配置）
        function save<T>(model: T | T[]): Promise<void>

        // delete
        function remove<T>(model: T | T[]): Promise<void>

        // 原始SQL的书写方法实现 execute => 独立出去单独进行额外处理实现，5%的非常定制层面上的实现
        // sql = 'select * from xxx where a = ? and b = ?', param = [1, 2]
        function execute(sql: string, param?: any[]): Promise<any>

        /**
         * 查询构造器的封装逻辑
         * 1. Model全局符号约定（自动代码生成的时候产生如下特殊符号约定）
         *      $user => 'user' (模型的别名)
         *      $user._photos => 'user._photos' (外键关系字符串)
         *      $user.id => 'user.id' (关系属性字符串)
         * 2. webpack的loader中配置预处理逻辑将字符串进正确的映射处理
         * 3. 改进查询构造器中的as any部分代码的表达式设计兼容自动化的辅助代码提示
         */
        // function query<T>(
        //     TYPE: new () => T, alias: any
        // ): SelectQueryBuilder<any>

        function find<T>(TYPE: new () => T, callback: FindManyOptions<T> | { [P in keyof T]?: any } | ((obj: T & { ASC: any, DESC: any }) => FindManyOptions<T>)): Promise<T[]>

        function findAndCount<T>(TYPE: new () => T, callback: FindManyOptions<T> | { [P in keyof T]?: any } | ((obj: T & { ASC, DESC }) => FindManyOptions<T>)): Promise<[T[], number]>

        function findByIds<T>(TYPE: new () => T, ids: any[], callback?: (FindManyOptions<T>) | ((obj: T & { ASC, DESC }) => FindManyOptions<T>)): Promise<T[]>

        function findOne<T>(TYPE: new () => T, callback: FindOneOptions<T> | { [P in keyof T]?: any } | ((obj: T & { ASC, DESC }) => FindOneOptions<T>)): Promise<T>

        function findOneById<T>(TYPE: new () => T, id: any, callback?: (FindOneOptions<T>) | ((obj: T & { ASC, DESC }) => FindOneOptions<T>)): Promise<T>

        function findLastOne<T>(TYPE: new () => T): Promise<T>
    }

    // 数据通道
    // 1.以Sys作为约定导出插件命名空间，前缀R表示Repository层的暴露的导出模块，这样保持与业务层的语义保持一致性。
    // TODO: 2.也支持业务上自己扩展重写掉底层或第三方插件的不合理实现。
    class $RSysDataTunnel<T> {
        constructor(param: {
            name: enum_t // 约定必须要在业务系统上定义$ESysDataTunnelName枚举值才能正常使用
            query: () => SelectQueryBuilder<any> // xquery查询构造器的实现
            alias: alias_t // xquery查询构造器中主表别名
            is_order_by_id_or_updated_at?: boolean_t // 缺省值为true按照id排序(等同于创建时间排序)，false为按照updated_at进行排序。
            // 如果按照update_at排序，需要业务上将此字段设置索引加速排序效率，需要考虑删除场景的数据同步问题（创建删除日志表即可）。
            is_auto_reset?: boolean_t // 是否自动重置查询列表（默认值为false，仅仅对id排序有效，update_at排序仅仅先全量再增量排序处理）
            every_step_count?: integer_t // 每个步长数据切分总数（默认值为100，需要在FC的执行周期一般10分钟之内可以处理的数量进行配置，如果在定时器中调用需要在超时时间内可以处理完的数量）
            every_step_timeout?: integer_t // 每一步执行最大超时毫秒时间（默认值为10分钟 10*60*1000），等待init完成的最大超时重试时间。 
        })

        // 如果没有可执行任务返回值为空
        get_new_task<T>(): Promise<{
            id: id_t // 任务id
            list: T[] // 当前任务对应的查询构造器返回的数据列表
        }>

        commit_task_done(id: id_t): Promise<void>

        commit_task_failed(id: id_t): Promise<void>
    }
}


