import { ConnectionConfig, createPool, MysqlError } from 'mysql';
import { FC } from 'react'
// implements
export function passwordMath(len: number): string {
    const s = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    //prompt("请输入密码的长度");
    let pass = "";
    for (let i = 0; i < len; i++) {
        const b = Math.random() * 62;
        const c = Math.floor(b);
        pass += s[c];
    }
    return pass
}
export function nxdatetime() {
    const date = new Date();
    let month: string | number = date.getMonth() + 1;
    let strDate: string | number = date.getDate();
    if (month <= 9) {
        month = "0" + month;
    }
    if (strDate <= 9) {
        strDate = "0" + strDate;
    }
    return date.getFullYear() + "-" + month + "-" + strDate + " "
        + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds()
}

type valSqlUse = string | number// | undefined
namespace TS {
    export type valApiFrom = string | number | boolean | null | undefined
    export type Tables = {
        [tablename: string]: {
            id: number
            [fieldname: string]: valApiFrom
        }
    }
    export type catchquery = {
        fun: 'catchquery',
        data: MysqlError['message']
    }
    export type catchconfig = {
        fun: 'catchconfig',
        data: ConnectionConfig
    }
    export type BaseCatch = {
        catchquery: Function,
        catchconfig: Function
    }
    export type BaseApi<
        Tables extends TS.Tables,
        Joins extends Record<keyof Tables, keyof Tables>,
        Join extends 'inner' | 'left'
        > = {
            configping: () => Promise<true>
            configset: (sqlConfigOp: ConnectionConfig) => Promise<true>
            servecolse: () => Promise<true>
            insert: <T extends keyof Tables>(c: { table: T, set: Omit<Tables[T], 'id'> }) => Promise<Required<Tables[T]>[]>,
            select: <T extends keyof Tables>(c: { table: T, join?: Join, where: { [TS in Joins[T] | T]?: Partial<Tables[TS]> } }) => Promise<Required<Tables[T]>[]>,
            update: <T extends keyof Tables>(c: { table: T, join?: Join, set: Omit<Tables[T], 'id'>, where: { [TS in Joins[T] | T]?: Partial<Tables[TS]> } }) => Promise<Required<Tables[T]>[]>,
            delete: <T extends keyof Tables>(c: { table: T, join?: Join, where: { [TS in Joins[T] | T]?: Partial<Tables[TS]> } }) => Promise<{ id: number }[]>
        }
    type GetTypeKey<C> = Extract<{ [K in keyof C]: [K, C[K]] }[keyof C], [any, (...args: any[]) => Promise<unknown>]>[0]
    type OmitNever<T> = Pick<T, { [P in keyof T]: T[P] extends (any: any) => Promise<any> ? P : never }[keyof T]>;

    // export type getfunParam<FUN extends (op:any)=>Promise<any>>=()
}
export class Base<
    Tables extends TS.Tables,
    JOINS extends { [T in keyof Tables]: readonly string[] }
    >{
    constructor(JOINS: JOINS) {
        this.JOINS = JOINS
        this.cache = this.cacheInit(JOINS)
    }
    protected JOINS
    cache
    protected cacheInit = (JOINS: JOINS): { [T in keyof Tables]: Record<number, Tables[T]> } => {
        const c = {} as { [T in keyof Tables]: Record<number, Tables[T]> }
        Object.keys(JOINS).forEach((k: keyof Tables) => c[k] = {})
        return c
    }
}
export default class Serve<
    Tables extends TS.Tables,
    Joins extends Record<keyof Tables, keyof Tables>,
    Join extends 'inner' | 'left',
    JOINS extends { [T in keyof Tables]: readonly string[] }
    > extends Base<Tables, JOINS> implements TS.BaseCatch, TS.BaseApi<Tables, Joins, Join> {
    protected pool
    constructor(sqlConfigOp: ConnectionConfig, JOINS: JOINS) {
        super(JOINS)
        this.pool = createPool({ ...sqlConfigOp, "dateStrings": false })
    }
    on = <T extends keyof TS.BaseApi<Tables, Joins, Join>>(fun: T, op: any) => this[fun](op)
        .then((success) => ({ ...op, success }))
        .catch((catchop: TS.catchquery | TS.catchconfig) => ({ ...op, ...catchop }))
    private binJoin = ({ table, join }: { table: keyof Tables, join?: Join }): string => {
        const c = this.JOINS[table]
        if (c && c.length > 0) {
            return c.map(v => (join || 'inner') + ' ' + v).join(' ')
        } else {
            return ' '
        }
    }
    private binSet = <T extends keyof Tables>({ table, set }: { table: T, set: Omit<Tables[T], 'id'> }): [string, Array<valSqlUse>] => {
        let kv: { k: Array<string>, v: Array<valSqlUse> } = { k: [], v: [] }
        const uptimestamp = nxdatetime()
        set = { ...set, uptimestamp }
        Object.entries(set).forEach(([k, v]) => {
            if (v === null || v === undefined) {
                kv.k.push(`${table}.${k}=null`)
            } else if (v === false || v === undefined) {
                kv.k.push(`${table}.${k}=0`)
            } else if (v === true) {
                kv.k.push(`${table}.${k}=1`)
            } else {
                kv.k.push(`${table}.${k}=?`)
                kv.v.push(v)
            }
        })
        return [' set ' + kv.k.join(' , '), kv.v]
    }
    private binWhere<T extends keyof Tables>({ where }: { where: { [TS in Joins[T] | T]?: Partial<Tables[TS]> } }): [string, Array<valSqlUse>] {
        let kv: { k: Array<string>, v: Array<valSqlUse> } = { k: [], v: [] }
        Object.entries(where).forEach(([tname, obj]) => {
            Object.entries(obj as Record<string, TS.valApiFrom>).forEach(([k, v]) => {
                if (v === null || v === undefined) {
                    kv.k.push(`${tname}.${k} is null`)
                } else if (v === false) {
                    kv.k.push(`${tname}.${k}=0`)
                } else if (v === true) {
                    kv.k.push(`${tname}.${k}=1`)
                } else {
                    kv.k.push(`${tname}.${k}=?`)
                    kv.v.push(v)
                }
            })
        })
        const wherek = kv.k.length ? ' where ' + kv.k.join(' and ') : ''
        return [wherek, kv.v]
    }
    catchquery: (op: MysqlError) => TS.catchquery = op => {
        console.log(op)
        return { fun: 'catchquery', data: op.message }
    }
    catchconfig: (op: any) => TS.catchconfig = op => {
        console.log(op)
        return { fun: 'catchconfig', data: {} }
    }
    insert: TS.BaseApi<Tables, Joins, Join>['insert'] = (c) => new Promise((ok, err) => {
        const [sqlstr, values] = this.binSet(c)
        const sql = [
            `insert into ${c.table}`,
            sqlstr,
        ].join(' ')
        this.pool.getConnection((errMsg, conn) => errMsg ?
            err(this.catchconfig(errMsg)) :
            conn.query({ sql, values }, (errmsg, data, field) => {
                conn.release()
                if (errmsg) {
                    err(this.catchquery(errMsg))
                } else {
                    const selectsql = [
                        `select ${c.table}.* from ${c.table}`,
                        'where id=?'
                    ].join(' ')
                    conn.query({ sql: selectsql, values: [data.insertId] }, (errmsg, data, field) => {
                        conn.release()
                        if (errmsg) {
                            err(this.catchquery(errMsg))
                        } else {
                            ok(data)
                        }
                    })
                }
            }))
    })
    select: TS.BaseApi<Tables, Joins, Join>['select'] = (c) => new Promise((ok, err) => {
        const [sqlstr, values] = this.binWhere(c)
        const sql = [
            `select ${c.table}.* from ${c.table}`,
            this.binJoin(c),
            sqlstr,
        ].join(' ')
        this.pool.getConnection((errMsg, conn) => errMsg ?
            err(this.catchconfig(errMsg)) :
            conn.query({ sql, values }, (errmsg, success, field) => {
                conn.release()
                return errmsg ? err(this.catchquery(errMsg)) : ok(success)
            }))
    })
    update: TS.BaseApi<Tables, Joins, Join>['update'] = (c) => new Promise((ok, err) => {
        const [setsql, setvalues] = this.binSet(c)
        const [wheresql, wheresqlvalues] = this.binWhere(c)
        const updatesql = [
            `update ${c.table}`,
            this.binJoin(c),
            setsql,
            wheresql
        ].join(' ')
        this.pool.getConnection((errMsg, conn) => errMsg ?
            err(this.catchconfig(errMsg)) :
            conn.query({ sql: updatesql, values: setvalues.concat(wheresqlvalues) }, (errmsg, data, field) => {
                if (errmsg) {
                    err(this.catchquery(errMsg))
                } else {
                    const selectsql = [
                        `select ${c.table}.* from ${c.table}`,
                        this.binJoin(c),
                        wheresql
                    ].join(' ')
                    conn.query({ sql: selectsql, values: wheresqlvalues }, (errmsg, data, field) => {
                        conn.release()
                        if (errmsg) {
                            err(this.catchquery(errMsg))
                        } else {
                            ok(data)
                        }
                    })
                }
            }))
    })
    delete: TS.BaseApi<Tables, Joins, Join>['delete'] = (c) => new Promise((ok, err) => {
        const [wheresql, wheresqlvalues] = this.binWhere(c)
        const updatesql = [
            `delete ${c.table} from  ${c.table}`,
            this.binJoin(c),
            wheresql
        ].join(' ')
        this.pool.getConnection((errMsg, conn) => errMsg ?
            err(this.catchconfig(errMsg)) :
            conn.query({ sql: updatesql, values: wheresqlvalues }, (errmsg, data, field) => {
                conn.release()
                if (errmsg) {
                    err(this.catchquery(errMsg))
                } else {
                    const selectsql = [
                        `select ${c.table}.* from ${c.table}`,
                        this.binJoin(c),
                        wheresql
                    ].join(' ')
                    conn.query({ sql: selectsql, values: wheresqlvalues }, (errmsg, data, field) => {
                        conn.release()
                        if (errmsg) {
                            err(this.catchquery(errMsg))
                        } else {
                            ok(data)
                        }
                    })
                }
            }))
    })
    configping: TS.BaseApi<Tables, Joins, Join>['configping'] = () => new Promise((ok, err) => {
        this.pool.getConnection((errMsg) => errMsg ? err(errMsg) : ok(true))
    })
    configset: TS.BaseApi<Tables, Joins, Join>['configset'] = (sqlConfigOp) => new Promise((ok) => {
        this.pool = createPool({ ...sqlConfigOp, "dateStrings": false })
        ok(true)
    })
    servecolse: TS.BaseApi<Tables, Joins, Join>['servecolse'] = () => new Promise((ok, err) => {
        this.pool.end((error) => error ? err(error) : ok(true))
    })
}
const ccc = <
    Tables extends TS.Tables,
    Joins extends Record<keyof Tables, keyof Tables>,
    Join extends 'inner' | 'left',
    JOINS extends { [T in keyof Tables]: readonly string[] }
>(JOINS: JOINS, catchquery: FC<TS.catchquery>, catchconfig: FC<TS.catchconfig>): {
    cache: { [T in keyof Tables]: Record<number, Tables[T]> },
    catchquery: FC<TS.catchquery>,
    catchconfig: FC<TS.catchconfig>
} => {
    //const c: TS.BaseApi<Tables, Joins, Join>
    return {
        cache: new Base<Tables, JOINS>(JOINS).cache,
        catchquery,
        catchconfig
    }
}
// abstract class WebDemo<
//     Tables extends TS.Tables,
//     Joins extends Record<keyof Tables, keyof Tables>,
//     Join extends 'inner' | 'left',
//     JOINS extends { [T in keyof Tables]: readonly string[] }
//     > extends Base<Tables, JOINS> implements TS.BaseCatch {
//     constructor(JOINS: JOINS, catchquery: FC<TS.catchquery>, catchconfig: FC<TS.catchconfig>) {
//         super(JOINS)
//     }
//     api: TS.BaseApi<Tables, Joins, Join>
//     abstract catchquery: FC<TS.catchquery>
//     abstract catchconfig: FC<TS.catchconfig>
//     // on = <fun extends GetTypeKey<Serve>>(param: { fun: fun } & Parameters<Serve[fun]>[0]) => {
//     //     return this[param.fun](...param)
//     //         .then((success: any) => ({ ...param, success }))
//     //         .catch((catchop: TS.catchOp) => ({ ...param, ...catchop }))
//     // }
//     // abstract on: { [K in keyof Serve]: Serve[K] extends (args?: infer P) => Promise<infer R> ? (param: { fun: K } & P) => Promise<{ fun: K, success: R } & P> : never }[keyof Serve]
// }