/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
 */
package magic.utils

import std.database.sql.SqlException

@When[sqlite == "enable"]
foreign {
    func malloc(size: UIntNative): CPointer<Unit>
    func free(p: CPointer<Unit>): Unit
    func sqlite3_open(path: CString, ppDb: CPointer<CPointer<Unit>>): Int32
    func sqlite3_prepare_v2(pDb: CPointer<Unit>, zSql: CString, nBytes: Int32,
                            ppStmt: CPointer<CPointer<Unit>>, pzTail: CPointer<Unit>): Int32
    func sqlite3_step(pStmt: CPointer<Unit>): Int32
    func sqlite3_column_bytes(pStmt: CPointer<Unit>, i: Int32): Int32
    func sqlite3_column_text(pStmt: CPointer<Unit>, i: Int32): CString
    func sqlite3_column_type(pStmt: CPointer<Unit>, i: Int32): Int32
    func sqlite3_column_int64(pStmt: CPointer<Unit>, i: Int32): Int64
    func sqlite3_column_blob(pStmt: CPointer<Unit>, i: Int32): CPointer<Unit>
    func sqlite3_column_count(pStmt: CPointer<Unit>): Int32
    func sqlite3_column_name(pStmt: CPointer<Unit>, n: Int32): CString
    func sqlite3_column_decltype(pStmt: CPointer<Unit>, i: Int32): CString
    func sqlite3_column_double(pStmt: CPointer<Unit>, i: Int32): Float64
    func sqlite3_reset(pStmt: CPointer<Unit>): Int32
    func sqlite3_busy_timeout(pDb: CPointer<Unit>, ms: Int32): Int32
    func sqlite3_changes(pDb: CPointer<Unit>): Int32
    func sqlite3_last_insert_rowid(pDb: CPointer<Unit>): Int64
    func sqlite3_soft_heap_limit(n: Int32): Unit
    func sqlite3_bind_parameter_count(pStmt: CPointer<Unit>): Int32
    func sqlite3_bind_null(pStmt: CPointer<Unit>, i: Int32): Int32
    func sqlite3_bind_int(pStmt: CPointer<Unit>, i: Int32, iValue: Int32): Int32
    func sqlite3_bind_int64(pStmt: CPointer<Unit>, i: Int32, iValue: Int64): Int32
    func sqlite3_bind_double(pStmt: CPointer<Unit>, i: Int32, rValue: Float64): Int32
    func sqlite3_bind_text(pStmt: CPointer<Unit>, i: Int32, zData: CString,
    nData: Int32, xDel: CPointer<Unit>): Int32
    func sqlite3_clear_bindings(pStmt: CPointer<Unit>): Int32
    func sqlite3_exec(pDb: CPointer<Unit>, zSql: CString,
    xCallback: CPointer<Unit>, pArg: CPointer<Unit>, pzErrMsg: CPointer<CString>): Int32
    func sqlite3_close(pDb: CPointer<Unit>): Int32
    func sqlite3_finalize(pStmt: CPointer<Unit>): Int32
}

/* 指针类型占内存字节数 */
private let POINTER_SIZE: UIntNative = 8

@When[sqlite == "enable"]
public struct SqliteUtils {

    /* sqlite3_step() 还未完成执行 */
    public static let SQLITE_ROW: Int32 = 100

    /* sqlite3_step() 已执行完成 */
    public static let SQLITE_DONE: Int32 = 101

    /*
    * Description: 打开数据库db文件
    */
    public static func sqlOpen(path: String): CPointer<CPointer<Unit>> {
        let ppDb: CPointer<CPointer<Unit>>
        unsafe {
            let ptr = malloc(POINTER_SIZE)
            ppDb = CPointer<CPointer<Unit>>(ptr)
            let cPath : CString= LibC.mallocCString(path)
            let ret = sqlite3_open(cPath, ppDb)
            LibC.free(cPath)
            if (ret != 0) {
                throw SqlException("Failed to open database.")
            }
        }
        return ppDb
    }

    /*
    * Description: 预处理sql语句
    */
    public static func sqlPrepare(ppDb: CPointer<CPointer<Unit>>, sql: String): CPointer<CPointer<Unit>> {
        let ppStmt: CPointer<CPointer<Unit>>
        unsafe {
            let ptr = malloc(POINTER_SIZE)
            ppStmt = CPointer<CPointer<Unit>>(ptr)
            let zSql  : CString= LibC.mallocCString(sql)
            let nullptr = CPointer<Unit>()
            let ret = sqlite3_prepare_v2(ppDb.read(), zSql, Int32(sql.size), ppStmt, nullptr)
            LibC.free(zSql)
            if (ret != 0) {
                throw SqlException("Failed to prepare statement.")
            }
        }
        return ppStmt
    }

    /**
    * Description: 执行sql语句
    */
    public static func sqlStep(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        return unsafe { sqlite3_step(ppStmt.read()) }
    }

    /**
    * Description: 获取sql结果指定列的数据类型
    */
    public static func sqlColumnType(ppStmt: CPointer<CPointer<Unit>>, i: Int32): Int32 {
        return unsafe { sqlite3_column_type(ppStmt.read(), i) }
    }

    /**
    * Description: 获取sql结果指定文本类型列的值
    */
    public static func sqlColumnText(ppStmt: CPointer<CPointer<Unit>>, i: Int32): String {
        let ret = unsafe { sqlite3_column_text(ppStmt.read(), i) }
        return ret.toString()
    }

    /**
    * Description: 获取sql结果指定列值的字节数
    */
    public static func sqlColumnBytes(ppStmt: CPointer<CPointer<Unit>>, i: Int32): Int32 {
        return unsafe { sqlite3_column_bytes(ppStmt.read(), i) }
    }

    /**
    * Description: 获取sql结果指定Int64类型列的值
    */
    public static func sqlColumnInt64(ppStmt: CPointer<CPointer<Unit>>, i: Int32): Int64 {
        return unsafe { sqlite3_column_int64(ppStmt.read(), i) }
    }

    /**
    * Description: 获取sql结果指定Float64类型列的值
    */
    public static func sqlColumnDouble(ppStmt: CPointer<CPointer<Unit>>, i: Int32): Float64 {
        return unsafe { sqlite3_column_double(ppStmt.read(), i) }
    }

    /**
    * Description: 获取sql结果blob类型列的值
    */
    public static func sqlColumnBlob(ppStmt: CPointer<CPointer<Unit>>, i: Int32): CPointer<Unit> {
        return unsafe { sqlite3_column_blob(ppStmt.read(), i) }
    }

    /**
    * Description: 获取sql结果列数
    */
    public static func sqlColumnCount(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        return unsafe { sqlite3_column_count(ppStmt.read()) }
    }

    /**
    * Description: 获取sql结果指定列名
    */
    public static func sqlColumnName(ppStmt: CPointer<CPointer<Unit>>, n: Int32): String {
        let ret = unsafe { sqlite3_column_name(ppStmt.read(), n) }
        return ret.toString()
    }

    /**
    * Description: 获取sql结果指定列声明的数据类型
    */
    public static func sqlColumnDecltype(ppStmt: CPointer<CPointer<Unit>>, i: Int32): String {
        let ret = unsafe { sqlite3_column_decltype(ppStmt.read(), i) }
        return ret.toString()
    }

    /**
    * Description: 重置sql语句
    */
    public static func sqlReset(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        return unsafe { sqlite3_reset(ppStmt.read()) }
    }

    /**
    * Description: 设置超时毫秒数
    */
    public static func sqlBusyTimeout(ppDb: CPointer<CPointer<Unit>>, ms: Int32): Int32 {
        return unsafe { sqlite3_busy_timeout(ppDb.read(), ms) }
    }

    /*
    * Description: 获取最近执行的sql语句的修改数量
    */
    public static func sqlChanges(ppDb: CPointer<CPointer<Unit>>): Int32 {
        return unsafe { sqlite3_changes(ppDb.read()) }
    }

    /**
    * Description: 获取最近插入行的ID
    */
    public static func sqlLastInsertRowid(ppDb: CPointer<CPointer<Unit>>): Int64 {
        return unsafe { sqlite3_last_insert_rowid(ppDb.read()) }
    }

    /**
    * Description: 设置堆内存上限
    */
    public static func sqlSoftHeapLimit(n: Int32): Unit {
        unsafe { sqlite3_soft_heap_limit(n) }
    }

    /**
    * Description: 获取sql语句绑定参数数量
    */
    public static func sqlBindParameterCount(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        return unsafe { sqlite3_bind_parameter_count(ppStmt.read()) }
    }

    /**
    * Description: 绑定空值到sql语句指定参数
    */
    public static func sqlBindNull(ppStmt: CPointer<CPointer<Unit>>, i: Int32): Int32 {
        return unsafe { sqlite3_bind_null(ppStmt.read(), i) }
    }

    /**
    * Description: 绑定Int32值到sql语句指定参数
    */
    public static func sqlBindInt(ppStmt: CPointer<CPointer<Unit>>, i: Int32, iValue: Int32): Int32 {
        return unsafe { sqlite3_bind_int(ppStmt.read(), i, iValue) }
    }

    /**
    * Description: 绑定Int64值到sql语句指定参数
    */
    public static func sqlBindInt64(ppStmt: CPointer<CPointer<Unit>>, i: Int32, iValue: Int64): Int32 {
        return unsafe { sqlite3_bind_int64(ppStmt.read(), i, iValue) }
    }

    /**
    * Description: 绑定Float64值到sql语句指定参数
    */
    public static func sqlBindDouble(ppStmt: CPointer<CPointer<Unit>>, i: Int32, rValue: Float64): Int32 {
        return unsafe { sqlite3_bind_double(ppStmt.read(), i, rValue) }
    }

    /**
    * Description: 绑定文本值到sql语句指定参数
    */
    public static func sqlBindText(ppStmt: CPointer<CPointer<Unit>>, i: Int32, data: String) {
        let ret: Int32
        unsafe {
            let zData : CString= LibC.mallocCString(data)
            let nullptr = CPointer<Unit>()
            ret = sqlite3_bind_text(ppStmt.read(), i, zData, Int32(data.size), nullptr)
            LibC.free(zData)
        }
        return ret
    }

    /**
    * Description: 解除sql语句绑定参数
    */
    public static func sqlClearBindings(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        return unsafe { sqlite3_clear_bindings(ppStmt.read()) }
    }

    /**
    * Description: 指定数据库执行sql命令
    */
    public static func sqlExec(ppDb: CPointer<CPointer<Unit>>, sql: String) {
        unsafe {
            let zSql : CString= LibC.mallocCString(sql)
            let nullptr = CPointer<Unit>()
            let ptr = malloc(POINTER_SIZE)
            let pzErrMsg = CPointer<CString>(ptr)
            let ret = sqlite3_exec(ppDb.read(), zSql, nullptr, nullptr, pzErrMsg)
            let errMsg = pzErrMsg.read().toString()
            free(ptr)
            LibC.free(zSql)
            if (ret != 0) {
                throw SqlException(errMsg)
            }
        }
    }

    /**
    * Description: 关闭数据库连接
    */
    public static func sqlClose(ppDb: CPointer<CPointer<Unit>>): Int32 {
        let ret: Int32
        unsafe {
            ret = sqlite3_close(ppDb.read())
            free(CPointer<Unit>(ppDb))
        }
        return ret
    }

    /**
    * Description: 销毁sql语句对象,释放内存
    */
    public static func sqlFinalize(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        let ret: Int32
        unsafe {
            ret = sqlite3_finalize(ppStmt.read())
            free(CPointer<Unit>(ppStmt))
        }
        return ret
    }
}

@When[sqlite != "enable"]
public struct SqliteUtils {

    /* sqlite3_step() 还未完成执行 */
    public static let SQLITE_ROW: Int32 = 100

    /* sqlite3_step() 已执行完成 */
    public static let SQLITE_DONE: Int32 = 101

    public static func sqlOpen(path: String): CPointer<CPointer<Unit>> {
        throw UnsupportedException()
    }

    public static func sqlPrepare(ppDb: CPointer<CPointer<Unit>>, sql: String): CPointer<CPointer<Unit>> {
        throw UnsupportedException()
    }

    public static func sqlStep(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        throw UnsupportedException()
    }

    public static func sqlColumnType(ppStmt: CPointer<CPointer<Unit>>, i: Int32): Int32 {
        throw UnsupportedException()
    }

    public static func sqlColumnText(ppStmt: CPointer<CPointer<Unit>>, i: Int32): String {
        throw UnsupportedException()
    }

    public static func sqlColumnBytes(ppStmt: CPointer<CPointer<Unit>>, i: Int32): Int32 {
        throw UnsupportedException()
    }

    public static func sqlColumnInt64(ppStmt: CPointer<CPointer<Unit>>, i: Int32): Int64 {
        throw UnsupportedException()
    }

    public static func sqlColumnDouble(ppStmt: CPointer<CPointer<Unit>>, i: Int32): Float64 {
        throw UnsupportedException()
    }

    public static func sqlColumnBlob(ppStmt: CPointer<CPointer<Unit>>, i: Int32): CPointer<Unit> {
        throw UnsupportedException()
    }

    public static func sqlColumnCount(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        throw UnsupportedException()
    }

    public static func sqlColumnName(ppStmt: CPointer<CPointer<Unit>>, n: Int32): String {
        throw UnsupportedException()
    }

    public static func sqlColumnDecltype(ppStmt: CPointer<CPointer<Unit>>, i: Int32): String {
        throw UnsupportedException()
    }

    public static func sqlReset(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        throw UnsupportedException()
    }

    public static func sqlBusyTimeout(ppDb: CPointer<CPointer<Unit>>, ms: Int32): Int32 {
        throw UnsupportedException()
    }

    public static func sqlChanges(ppDb: CPointer<CPointer<Unit>>): Int32 {
        throw UnsupportedException()
    }

    public static func sqlLastInsertRowid(ppDb: CPointer<CPointer<Unit>>): Int64 {
        throw UnsupportedException()
    }

    public static func sqlSoftHeapLimit(n: Int32): Unit {
        throw UnsupportedException()
    }

    public static func sqlBindParameterCount(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        throw UnsupportedException()
    }

    public static func sqlBindNull(ppStmt: CPointer<CPointer<Unit>>, i: Int32): Int32 {
        throw UnsupportedException()
    }

    public static func sqlBindInt(ppStmt: CPointer<CPointer<Unit>>, i: Int32, iValue: Int32): Int32 {
        throw UnsupportedException()
    }

    public static func sqlBindInt64(ppStmt: CPointer<CPointer<Unit>>, i: Int32, iValue: Int64): Int32 {
        throw UnsupportedException()
    }

    public static func sqlBindDouble(ppStmt: CPointer<CPointer<Unit>>, i: Int32, rValue: Float64): Int32 {
        throw UnsupportedException()
    }

    public static func sqlBindText(ppStmt: CPointer<CPointer<Unit>>, i: Int32, data: String) {
        throw UnsupportedException()
    }

    public static func sqlClearBindings(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        throw UnsupportedException()
    }

    public static func sqlExec(ppDb: CPointer<CPointer<Unit>>, sql: String) {
        throw UnsupportedException()
    }

    public static func sqlClose(ppDb: CPointer<CPointer<Unit>>): Int32 {
        throw UnsupportedException()
    }

    public static func sqlFinalize(ppStmt: CPointer<CPointer<Unit>>): Int32 {
        throw UnsupportedException()
    }
}