package com.zxhl.cms.db;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.text.TextUtils;
import android.util.Log;

import com.zxhl.cms.utils.LogUtils;
import com.zxhl.cms.utils.MainLooper;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by gaoleichao on 2018/10/29.
 */

class SqlExcuters {
    static final int ACTION_INSERT = 0x01;
    static final int ACTION_DEL = 0x02;
    static final int ACTION_UPDATE = 0x03;
    static final int ACTION_SELECT = 0x04;
    static final int ACTION_INSERT_ALL = 0x05;
    static final int ACTION_INSERT_OR_UPDATE = 0x06;
    static final int ACTION_INSERT_OR_REPLACE = 0x07;

    private static SqlExcuters excuters = new SqlExcuters();

    private ExecutorService executorService = Executors.newFixedThreadPool(5);

    private SqlExcuters() {
    }

    /**
     * @param action
     * @param args
     * @return
     */
    @SuppressWarnings("unchecked,unused")
    static void excute(int action, Object... args) {
        switch (action) {
            case ACTION_INSERT:
                excuters.executorService.execute(new InsertRunnable(args));
                break;
            case ACTION_DEL:
                excuters.executorService.execute(new DelRunnable(args));
                break;
            case ACTION_SELECT:
                excuters.executorService.execute(new SearchRunnable(args));
                break;
            case ACTION_UPDATE:
                excuters.executorService.execute(new UpdateRunnable(args));
                break;
            case ACTION_INSERT_ALL:
                excuters.executorService.execute(new InsertAllRunnable(args));
                break;
            case ACTION_INSERT_OR_UPDATE:
                excuters.executorService.execute(new InsertOrUpdateRunnable(args));
                break;
            case ACTION_INSERT_OR_REPLACE:
                excuters.executorService.execute(new InsertOrReplaceRunnable(args));
                break;
        }
    }

    /**
     * 插入数据
     */
    @SuppressWarnings("unchecked")
    private static class InsertRunnable implements Runnable {
        private String tableName;
        private SQLiteDatabase db;
        private ContentValues contentValues;
        private DBCallback callback;

        InsertRunnable(Object... args) {
            tableName = (String) args[0];
            db = (SQLiteDatabase) args[1];
            contentValues = (ContentValues) args[3];
            callback = (DBCallback) args[4];
        }

        @Override
        public void run() {
            final long result = db.insert(tableName, null, contentValues);
            MainLooper.Companion.get().post(new Runnable() {
                @Override
                public void run() {
                    if (callback != null)
                        callback.onResult(result != -1 ? 0 : (int) result, "");
                }
            });

        }
    }

    /**
     * 插入数据
     */
    @SuppressWarnings("unchecked")
    private static class InsertAllRunnable implements Runnable {
        private String tableName;
        private SQLiteDatabase db;
        private ContentValues[] contentValues;
        private DBCallback callback;
        private SqlCondition condition;


        InsertAllRunnable(Object... args) {
            tableName = (String) args[0];
            db = (SQLiteDatabase) args[1];
            condition = (SqlCondition) args[2];
            contentValues = (ContentValues[]) args[3];
            callback = (DBCallback) args[4];
        }

        @Override
        public void run() {
            if (condition != null) {
                db.delete(tableName, condition.getClause(), condition.getArgs());
            } else {
                db.delete(tableName, "", null);
            }

            String sql = "REPLACE INTO " + tableName + " (%s) VALUES (%s)";
            db.beginTransaction();
            SQLiteStatement stmt = null;
            List<String> keys = null;
            /* 批量插入值 */
            for (ContentValues contentValue : contentValues) {
                Set<String> keySet = contentValue.keySet();
                if (keys == null) {
                    /* 构建SQL模板 */
                    keys = new ArrayList<>();
                    keys.addAll(keySet);
                    sql = buildSql(keys, sql);
                    stmt = db.compileStatement(sql);
                }
                /* 插入值 */
                int index = 1;
                for (String key : keySet) {
                    String value = contentValue.getAsString(key);
                    if (!TextUtils.isEmpty(value)) {
                        stmt.bindString(index, value);
                    }
                    index++;
                }
                stmt.executeInsert();
                stmt.clearBindings();
            }

            db.setTransactionSuccessful();
            db.endTransaction();

            MainLooper.Companion.get().post(new Runnable() {
                @Override
                public void run() {
                    if (callback != null)
                        callback.onResult(0, "");
                }
            });

        }

        String buildSql(List<String> keys, String tempSql) {
            StringBuilder keyStr = new StringBuilder();
            StringBuilder valueStr = new StringBuilder();
            for (String key : keys) {
                if (keyStr.length() > 0) keyStr.append(",");
                keyStr.append(key);

                if (valueStr.length() > 0) valueStr.append(",");
                valueStr.append("?");
            }
            String sqlResult = String.format(tempSql, keyStr.toString(), valueStr.toString());
            LogUtils.e("sqlExcutor", "buildsql = " + sqlResult);
            return sqlResult;
        }
    }

    /**
     * 更新数据
     */
    @SuppressWarnings("unchecked")
    public static class UpdateRunnable implements Runnable {
        private String tableName;
        private SQLiteDatabase db;
        private ContentValues contentValues;
        private SqlCondition condition;
        private DBCallback callback;

        UpdateRunnable(Object... args) {
            tableName = (String) args[0];
            db = (SQLiteDatabase) args[1];
            condition = (SqlCondition) args[2];
            contentValues = (ContentValues) args[3];
            callback = (DBCallback) args[4];
        }

        @Override
        public void run() {
            final int result = db.update(tableName, contentValues, condition.getClause(), condition
                    .getArgs());
            MainLooper.Companion.get().post(new Runnable() {
                @Override
                public void run() {
                    if (callback != null)
                        callback.onResult(result != -1 ? 0 : result, "");
                }
            });

        }
    }

    /**
     * 删除数据
     */
    @SuppressWarnings("unchecked")
    public static class DelRunnable implements Runnable {
        private String tableName;
        private SQLiteDatabase db;
        private SqlCondition condition;
        private DBCallback callback;

        DelRunnable(Object... args) {
            tableName = (String) args[0];
            db = (SQLiteDatabase) args[1];
            condition = (SqlCondition) args[2];
            callback = (DBCallback) args[4];
        }

        @Override
        public void run() {
            final int result = db.delete(tableName, "", null);
            MainLooper.Companion.get().post(new Runnable() {
                @Override
                public void run() {
                    if (callback != null)
                        callback.onResult(result != -1 ? 0 : result, "");
                }
            });

        }
    }

    public static class InsertOrReplaceRunnable implements Runnable {
        private static final String INSERT_OR_REPLACT_SQL =
                "INSERT OR REPLACE INTO %s (%s) VALUES (%s)";
        private String tableName;
        private SQLiteDatabase db;
        private ContentValues contentValues;
        private SqlCondition condition;
        private DBCallback callback;

        InsertOrReplaceRunnable(Object... args) {
            tableName = (String) args[0];
            db = (SQLiteDatabase) args[1];
            condition = (SqlCondition) args[2];
            contentValues = (ContentValues) args[3];
            callback = (DBCallback) args[4];
        }


        @Override
        public void run() {
            Set<String> keys = contentValues.keySet();
            String[] values = new String[keys.size()];
            StringBuilder keyStr = new StringBuilder();
            StringBuilder valueStr = new StringBuilder();
            int index = 0;
            String id = contentValues.getAsString(DBConfig.CONTENT);
            for (String key : keys) {
                if (keyStr.length() > 0) keyStr.append(",");
                keyStr.append(key);
                if (valueStr.length() > 0) valueStr.append(",");
                values[index] = contentValues.getAsString(key);
                valueStr.append("?");
                index++;
            }
            String sqlResult = String.format(Locale.getDefault(), INSERT_OR_REPLACT_SQL,
                    tableName, keyStr.toString(),
                    valueStr.toString());
            db.execSQL(sqlResult, values);
            MainLooper.Companion.get().post(new Runnable() {
                @Override
                public void run() {
                    if (callback != null)
                        callback.onResult(0, "数据更新成功");
                }
            });

        }
    }

    /**
     * 删除数据
     */
    @SuppressWarnings("unchecked")
    public static class InsertOrUpdateRunnable implements Runnable {
        private String tableName;
        private SQLiteDatabase db;
        private ContentValues contentValues;
        private SqlCondition condition;
        private DBCallback callback;

        InsertOrUpdateRunnable(Object... args) {
            tableName = (String) args[0];
            db = (SQLiteDatabase) args[1];
            condition = (SqlCondition) args[2];
            contentValues = (ContentValues) args[3];
            callback = (DBCallback) args[4];
        }

        @Override
        public void run() {

            Cursor cursor = db.query(condition.getDistinct(), tableName, condition.getSelect(),
                    condition.getClause(),
                    condition.getArgs(),
                    condition.getGroupBy(), null, condition.getOrderBy(), condition.getLimit());

            try {
                if (cursor != null && cursor.getCount() > 0) {
                    //更新操作
                    final int result = db.update(tableName, contentValues, condition.getClause(), condition.getArgs());
                    MainLooper.Companion.get().post(new Runnable() {
                        @Override
                        public void run() {
                            if (callback != null) {
                                callback.onResult(result != -1 ? 0 : result, result != -1 ? "更新成功" : "更新失败");
                            }
                        }
                    });
                } else {
                    long id = db.insertWithOnConflict(tableName, null, contentValues, SQLiteDatabase
                            .CONFLICT_IGNORE);
                    if (id != -1) {
                        MainLooper.Companion.get().post(new Runnable() {
                            @Override
                            public void run() {
                                if (callback != null)
                                    callback.onResult(0, "写入成功");
                            }
                        });
                    }
                }

            } catch (Exception e) {
                MainLooper.Companion.get().post(new Runnable() {
                    @Override
                    public void run() {
                        if (callback != null) {
                            callback.onResult(-1, "操作失败");
                        }
                    }
                });
            } finally {
                if (null != cursor) {
                    cursor.close();
                }
            }
        }
    }

    /**
     * 搜索数据
     */
    @SuppressWarnings("unchecked")
    public static class SearchRunnable implements Runnable {
        private String tableName;
        private SQLiteDatabase db;
        private SqlCondition condition;
        private DBCallback<String> callback;

        SearchRunnable(Object... args) {
            tableName = (String) args[0];
            db = (SQLiteDatabase) args[1];
            condition = (SqlCondition) args[2];
            callback = (DBCallback<String>) args[4];
        }

        @Override
        public void run() {
            Cursor cursor = null;
            if (condition != null) {
                cursor = db.query(condition.getDistinct(), tableName, condition.getSelect(),
                        condition.getClause(),
                        condition.getArgs(),
                        condition.getGroupBy(), null, condition.getOrderBy(), condition.getLimit());
            } else {
                cursor = db.query(false, tableName, null, null, null, null, null, null,
                        null);
            }
            JSONArray videoInfos = null;
            if (cursor != null) {
                cursor.moveToFirst();
                if (cursor.getCount() > 0) {
                    videoInfos = new JSONArray();
                    do {
                        videoInfos.put(translateCursor(cursor));
                    } while (cursor.moveToNext());
                }
                cursor.close();
            }

            final JSONArray finalVideoInfos = videoInfos;
            MainLooper.Companion.get().post(new Runnable() {
                @Override
                public void run() {
                    if (callback != null)
                        callback.onResult(0, finalVideoInfos != null ? finalVideoInfos.toString() : "");
                }
            });

        }

        private JSONObject translateCursor(Cursor cursor) {
            JSONObject videoInfo = null;
            for (String name : cursor.getColumnNames()) {
                int index = cursor.getColumnIndex(name);
                int type = cursor.getType(index);
                switch (type) {
                    case Cursor.FIELD_TYPE_INTEGER:
                        int intValue = cursor.getInt(index);
//                        videoInfo.addProperty(name, intValue);
                        break;
                    default:
                        String strValue = cursor.getString(index);
//                        videoInfo.addProperty(name, TextUtils.isEmpty(strValue) ? null :
//                                strValue);
                        if (TextUtils.equals(name, DBConfig.CONTENT)) {
                            try {
                                videoInfo = new JSONObject(strValue);
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }

                        break;
                }
            }
            return videoInfo;
        }

    }
}
