using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dapper;

namespace Server.DB.Sql
{
    /// <summary>
    /// SQL 构建器，支持查询、分页、插入、更新、删除，兼容 Dapper
    /// </summary>
    public class SqlBuilder
    {
        private readonly string _tableName;
        private string _select = "*";
        private StringBuilder _where = new StringBuilder("1=1");
        private DynamicParameters _parameters = new DynamicParameters();
        private readonly List<string> _joins = new List<string>();
        private readonly List<string> _orderBy = new List<string>();
        private readonly List<string> _groupBy = new List<string>();
        private int _paramCount = 0;

        public SqlBuilder(string tableName)
        {
            if (string.IsNullOrWhiteSpace(tableName))
                throw new ArgumentException("Table name cannot be null or empty");
            _tableName = tableName;
        }
        
        public SqlBuilder Restart()
        {
            _select = "*";
            _where = new StringBuilder("1=1");
            _parameters = new DynamicParameters();
            _joins.Clear();
            _orderBy.Clear();
            _groupBy.Clear();
            _paramCount = 0;
            return this;
        }

        /// <summary>
        /// 生成增长式的参数
        /// </summary>
        private string GenerateParam(string column)
        {
            _paramCount++;
            var param = column.Replace(".", "_") + "_" + _paramCount;
            return param;
        }

        #region Select

        public SqlBuilder Select(string select)
        {
            _select = select;
            return this;
        }

        #endregion

        #region Join

        public SqlBuilder InnerJoin(string table, string on)
        {
            _joins.Add($"INNER JOIN {table} ON {on}");
            return this;
        }

        public SqlBuilder LeftJoin(string table, string on)
        {
            _joins.Add($"LEFT JOIN {table} ON {on}");
            return this;
        }

        #endregion

        #region WHERE 条件

        public SqlBuilder WhereEqual(string column, object value)
        {
            if (value != null)
            {
                var param = GenerateParam(column);
                _where.Append($" AND {column} = @{param}");
                _parameters.Add(param, value);
            }

            return this;
        }
        
        public SqlBuilder WhereNotEqual(string column, object value)
        {
            if (value != null)
            {
                var param = GenerateParam(column);
                _where.Append($" AND {column} <> @{param}");
                _parameters.Add(param, value);
            }

            return this;
        }
        
        public SqlBuilder WhereBetween(string column, object value1, object value2)
        {
            if (value1 != null && value2 != null)
            {
                var param = column.Replace(".", "_");
                _where.Append($" AND {column} BETWEEN @{param}_1 AND @{param}_2");
                _parameters.Add($"{param}_1", value1);
                _parameters.Add($"{param}_2", value2);
            }

            return this;
        }

        public SqlBuilder WhereLike(string column, string value)
        {
            if (!string.IsNullOrEmpty(value))
            {
                _where.Append($" AND {column} LIKE @{column}");
                _parameters.Add(column, $"%{value}%");
            }

            return this;
        }

        public SqlBuilder WhereCustom(string condition, string paramName, object value)
        {
            if (!string.IsNullOrEmpty(condition) && value != null)
            {
                _where.Append($" AND {condition}");
                _parameters.Add(paramName, value);
            }

            return this;
        }

        public SqlBuilder WhereIn<T>(string column, IEnumerable<T> values)
        {
            if (values != null && values.Any())
            {
                var list = values.ToList();
                var paramNames = new List<string>();

                for (int i = 0; i < list.Count; i++)
                {
                    if (list[i] == null) continue;
                    string paramName = $"{column}_in{i}";
                    paramNames.Add("@" + paramName);
                    _parameters.Add(paramName, list[i]);
                }

                string inClause = string.Join(",", paramNames);
                _where.Append($" AND {column} IN ({inClause})");
            }
            else
            {
                _where.Append(" AND 1=0");
            }

            return this;
        }
        
        public SqlBuilder WhereNotIn<T>(string column, IEnumerable<T> values)
        {
            if (values != null && values.Any())
            {
                var list = values.ToList();
                var paramNames = new List<string>();

                for (int i = 0; i < list.Count; i++)
                {
                    if (list[i] == null) continue;
                    string paramName = $"{column}_in{i}";
                    paramNames.Add("@" + paramName);
                    _parameters.Add(paramName, list[i]);
                }

                string inClause = string.Join(",", paramNames);
                _where.Append($" AND {column} NOT IN ({inClause})");
            }            
            else
            {
                _where.Append(" AND 1=0");
            }

            return this;
        }

        public SqlBuilder OrderBy(string column, bool descending = false)
        {
            _orderBy.Add($"{column} {(descending ? "DESC" : "ASC")}");
            return this;
        }
        
        public SqlBuilder GroupBy(string column)
        {
            _groupBy.Add(column);
            return this;
        }

        #endregion

        #region 查询 + 分页

        public (string Sql, DynamicParameters Parameters) BuildSelect()
        {
            string order = _orderBy.Any() ? $" ORDER BY {string.Join(",", _orderBy)}" : "";
            string join = _joins.Any() ? $" {string.Join(" ", _joins)}" : "";
            string group = _groupBy.Any() ? $" GROUP BY {string.Join(",", _groupBy)}" : "";
            string sql = $"SELECT {_select} FROM {_tableName}{join} WHERE {_where}{group}{order};";
            return (sql, _parameters);
        }

        public (string Sql, DynamicParameters Parameters) BuildPageSqlWithSearch<T>(DbSearchField<T> searchField)
        {
            var pageIndex = searchField.GetPageIndex();
            var pageSize = searchField.GetPageSize();
            // 根据注解自动构建查询条件
            searchField.BuildWhereConditions(this);

            return BuildPageSql(pageIndex, pageSize);
        }

        public (string Sql, DynamicParameters Parameters) BuildPageSql(int page, int pageSize)
        {
            int startRow = (page - 1) * pageSize + 1;
            int endRow = page * pageSize;

            var parameters = new DynamicParameters();
            foreach (var paramName in _parameters.ParameterNames)
            {
                parameters.Add(paramName, _parameters.Get<object>(paramName));
            }

            parameters.Add("StartRow", startRow);
            parameters.Add("EndRow", endRow);

            string order = _orderBy.Any() ? string.Join(",", _orderBy) : "Id DESC";
            string join = _joins.Any() ? $" {string.Join(" ", _joins)}" : "";
            string tableAlias = "";
            string tableName = _tableName;
            if (_tableName.Contains(" "))
            {
                tableName = _tableName.Split(' ')[0];
                tableAlias = $" {_tableName.Split(' ')[1]}";
            }

            string sql = $@"
WITH BaseQuery AS (
    SELECT *, ROW_NUMBER() OVER (ORDER BY {order}) AS RowNum
    FROM {tableName}
    WHERE {_where}
)
SELECT {_select}
FROM BaseQuery{tableAlias}{join}
WHERE RowNum BETWEEN @StartRow AND @EndRow;

SELECT COUNT(1) FROM {tableName} WHERE {_where};
";
            return (sql, parameters);
        }

        #endregion

        #region INSERT

        public (string Sql, DynamicParameters Parameters) BuildInsert(Dictionary<string, object> fields)
        {
            if (fields == null || fields.Count == 0)
                throw new ArgumentException("Insert fields cannot be empty");

            var columns = string.Join(",", fields.Keys);
            var values = string.Join(",", fields.Keys.Select(k => "@" + k));
            
            foreach (var kv in fields)
            {
                _parameters.Add(kv.Key, kv.Value);
            }

            string sql = $"INSERT INTO {_tableName} ({columns}) VALUES ({values});";
            if (_where.ToString() != "1=1")
            {
                sql = $"IF NOT EXISTS (SELECT 1 FROM {_tableName} WHERE {_where}) BEGIN INSERT INTO {_tableName} ({columns}) VALUES ({values}) END;";
            }

            return (sql, _parameters);
        }

        #endregion

        #region UPDATE

        public (string Sql, DynamicParameters Parameters) BuildUpdate(Dictionary<string, object> fields)
        {
            if (fields == null || fields.Count == 0)
                throw new ArgumentException("Update fields cannot be empty");

            var setList = new List<string>();
            var parameters = new DynamicParameters();

            foreach (var kv in fields)
            {
                setList.Add($"{kv.Key} = @{kv.Key}");
                parameters.Add(kv.Key, kv.Value);
            }

            // 添加 WHERE 的参数
            foreach (var paramName in _parameters.ParameterNames)
            {
                parameters.Add(paramName, _parameters.Get<object>(paramName));
            }

            string sql = $"UPDATE {_tableName} SET {string.Join(",", setList)} WHERE {_where};";

            return (sql, parameters);
        }

        /// <summary>
        /// 生成 Upsert SQL
        /// </summary>
        public (string Sql, DynamicParameters Parameters) BuildUpsert(
            Dictionary<string, object> fields,
            string primaryKey, bool isInsertKey = false)
        {
            if (fields == null || fields.Count == 0)
                throw new ArgumentException("Fields cannot be empty");

            if (string.IsNullOrWhiteSpace(primaryKey))
                throw new ArgumentException("Primary key must be provided");

            var parameters = new DynamicParameters();
            foreach (var kv in fields)
            {
                if (kv.Value != null)
                    parameters.Add(kv.Key, kv.Value);
            }

            var parameterNames = parameters.ParameterNames
                .Where(k => k != primaryKey).ToList();
            var insertSourceColumns = string.Join(",", isInsertKey ? parameters.ParameterNames : parameterNames);
            var insertValues = string.Join(",", (isInsertKey ? parameters.ParameterNames : parameterNames).Select(k => "@" + k));
            var updateList = parameterNames.Select(k => $"{k} = @{k}");

            string sql = @$"IF EXISTS (SELECT 1 FROM {_tableName} WHERE {primaryKey}=@{primaryKey})
BEGIN
    UPDATE {_tableName} SET {string.Join(",", updateList)} WHERE {primaryKey}=@{primaryKey}
END
ELSE
BEGIN
    INSERT INTO {_tableName} ({insertSourceColumns}) VALUES ({insertValues})
    SELECT CAST(SCOPE_IDENTITY() AS INT)
END";
            return (sql, parameters);
        }

        #endregion

        #region DELETE

        public (string Sql, DynamicParameters Parameters) BuildDelete()
        {
            if (_where.Length <= 0)
                throw new InvalidOperationException("Delete must have WHERE clause");

            var parameters = new DynamicParameters();
            foreach (var paramName in _parameters.ParameterNames)
            {
                parameters.Add(paramName, _parameters.Get<object>(paramName));
            }

            string sql = $"DELETE FROM {_tableName} WHERE {_where};";
            return (sql, parameters);
        }

        #endregion
    }
}