Promise = require('bluebird')
_ = require('lodash')
mysql = require('mysql')
PoolConnection = require('mysql/lib/PoolConnection')

log = require('./log')
config = require('./config')
mysqlConfig = config.mysql

pool = mysql.createPool
    connectionLimit: 50
    host: mysqlConfig.host
    user: mysqlConfig.user
    password: mysqlConfig.password
    database: mysqlConfig.database

pEndPool = Promise.promisify pool.end.bind(pool)
exports.pConnection = Promise.promisify(pool.getConnection.bind(pool))

query = Promise.promisify PoolConnection.prototype.query
beginTransaction = Promise.promisify PoolConnection.prototype.beginTransaction
commit = Promise.promisify PoolConnection.prototype.commit
rollback = Promise.promisify PoolConnection.prototype.rollback

class Store
    constructor: (@conn)-> true
    toString: -> 'Store'
    pQuery: -> query.apply @conn, arguments
    pBeginTransaction: -> beginTransaction.apply @conn, arguments
    pCommit: -> commit.apply @conn, arguments
    pRollback: -> rollback.apply @conn, arguments
    release: -> @conn.release()
    gSelect: (sql, args)->
        r = yield @pQuery(sql, args)
        return r
    gSelectOne: (sql, args)->
        rows = yield from @gSelect(sql, args)
        if rows and rows.length then rows[0] else null
    pInsertAllFields: (table, object)->
        keys = _.keys(object)
        placeholders = []
        values = []
        for key in keys
            placeholders.push '?'
            values.push object[key]
        sql = "insert into #{table}(#{keys.join(',')}) values(#{placeholders.join(',')})"
        # log.system.debug sql
        # log.system.debug values
        @pQuery(sql, values)
    pInsertFields: (table, object, keys)->
        # log.system.debug keys
        placeholders = []
        values = []
        for key in keys
            placeholders.push '?'
            values.push object[key]
        sql = "insert into #{table}(#{keys.join(',')}) values(#{placeholders.join(',')})"
        # log.system.debug sql
        # log.system.debug values
        @pQuery(sql, values)

exports.gStore = ->
    connection = yield exports.pConnection()
    new Store(connection)

exports.autoRelease = (work)->
    return (args...)->
        store = yield from exports.gStore()
        try
            args.splice(0, 0, store)
            # log.system.debug args
            r = yield from work(args...)
            return r
        finally
            store.release()

exports.withTransaction = (work)->
    return (args...)->
        store = yield from exports.gStore()
        try
            yield store.pBeginTransaction()
            args.splice(0, 0, store)
            r = yield from work(args...)
            yield store.pCommit()
            return r
        catch e
            yield store.pRollback()
            throw e
        finally
            store.release()

# 在事务中执行操作,不仅回滚事务,也执行业务回滚操作. 业务回滚操作通过 context.rollbacks 指定.
exports.atomic = (work)->
    return (args...)->
        store = yield from exports.gStore()
        context = {rollbacks: []}
        try
            yield store.pBeginTransaction()
            args.splice(0, 0, store, context)
            r = yield from work(args...)
            yield store.pCommit()
            return r
        catch e
            log.system.info 'Rollback transaction'

            try
                yield store.pRollback()
            catch e
                log.system.error e

            for r in context.rollbacks
                try
                    if r.generator == true
                        yield from r(store, context)
                    else
                        r(store, context)
                catch e
                    log.system.error e
            # 抛出原来的异常
            throw e
        finally
            store.release()

exports.gEnd = ->
    try
        yield pEndPool()
    catch e
        log.system.error e, 'gEnd'

test = ->
    connection = yield exports.pConnection()
    pQuery = exports.liftQuery connection
    [rows] = yield pQuery('select 1+1 as result')
    log.system.debug rows

    [rows] = yield pQuery('select id, step, createdOn from sale_order where step <= ' + 510)
    log.system.debug rows

    connection.release()
    yield from exports.gEnd()

# require('co')(test).catch (e)-> log.system.error e

