using Hh.Mes.Common.log;
using Hh.Mes.Common.Request;
using Hh.Mes.POJO.Entity;
using Hh.Mes.POJO.Response;
using Hh.Mes.POJO.ViewModel;
using Hh.Mes.POJO.WebEntity.tool;
using Hh.Mes.Service.Repository;
using Hh.Mes.Service.WebService.Base;
using Hh.Mes.T4;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using DbType = SqlSugar.DbType;

namespace Hh.Mes.Service.WebService.Tool
{
    public class CodeGeneratorService : RepositorySqlSugar<base_Entity>
    {
        private readonly DbType dbType;
        private readonly SysModuleService _moduleService;

        public CodeGeneratorService(SysModuleService moduleService)
        {
            dbType = Context.CurrentConnectionConfig.DbType;
            _moduleService = moduleService;
        }
        public dynamic Load(PageReq pageReq, TableInfo entity)
        {
            return ExceptionsHelp.Instance.ExecuteT(() =>
            {
                var data = GetAllTableDescription(entity.TableCode, entity.TableName);
                var result = new Response<List<TableInfo>>()
                {
                    Result = data.Skip(pageReq.limit * (pageReq.page - 1)).Take(pageReq.limit).ToList(),
                    Count = data.Count()
                };
                return result;
            }, catchRetrunValue: "list");
        }


        /// <summary>
        /// 获取代码
        /// </summary>
        /// <param name="entityName">表名称</param>
        /// <param name="areaName">区域名称</param>
        /// <returns></returns>
        public CodePreviewVM GetCode(string entityName, string areaName, IEnumerable<string> areaItems)
        {
            var jsText = string.Empty;
            var modelText = string.Empty;
            var schemaTables = GetSchemaTable(entityName);
            if (schemaTables.Any())
            {
                var jsTemplate = new JavaScriptTemplate(areaName, entityName, schemaTables);
                jsText = jsTemplate.TransformText();
                var modelTemplate = new ModelTemplate(entityName, schemaTables);
                modelText = modelTemplate.TransformText();
            }
            var controllerTemplate = new ControllerTemplate(areaName, entityName);
            //过滤部分列,用于在Service和Cshtml渲染
            var filterColumns = new List<string> { "Id", "keys", "createBy", "createTime", "updateTime", "updateBy" };
            var newColumns = schemaTables.Where(x => !filterColumns.Any(f => x.ColumnName.Equals(f, StringComparison.OrdinalIgnoreCase))).ToList();
            var serviceTemplate = new ServiceTemplate(areaName, entityName, newColumns);
            var tableDescription = GetTableDescription(entityName);
            var cshtmlTemplate = new CshtmlTemplate(areaName, entityName, newColumns, tableDescription?.TableName);
            var vm = new CodePreviewVM()
            {
                JavaScriptTemp = jsText,
                ModelTemp = modelText,
                ControllerTemp = controllerTemplate.TransformText(),
                ServiceTemp = serviceTemplate.TransformText(),
                CshtmlTemp = cshtmlTemplate.TransformText(),
                AreaItems = areaItems,
                AreaName = areaName,
                EntityName = entityName,
            };
            return vm;
        }

        /// <summary>
        /// 获取所有表名称和表注释
        /// </summary>
        /// <param name="tableCode">表名称,用于模糊查询</param>
        /// <param name="tableName">表说明/备注,用于模糊查询</param>
        /// <returns></returns>
        public List<TableInfo> GetAllTableDescription(string tableCode = "", string tableName = "")
        {
            var result = new List<TableInfo>();
            string sql = string.Empty;
            switch (dbType)
            {
                case DbType.MySql:
                    var tableSchemaName = Context.CurrentConnectionConfig.ConnectionString.Split(";")
                        .FirstOrDefault(x => x.Equals("database", StringComparison.OrdinalIgnoreCase))?.Split("=").LastOrDefault();
                    if (!string.IsNullOrWhiteSpace(tableSchemaName))
                    {
                        sql = $"select table_name as TableCode,table_comment AS TableName from information_schema.tables where table_schema='{tableSchemaName}' ";
                        if (!string.IsNullOrWhiteSpace(tableCode))
                        {
                            sql += $"And table_name Like '%{tableCode}%' ";
                        }
                        if (!string.IsNullOrWhiteSpace(tableName))
                        {
                            sql += $"And table_comment LIKE '%{tableName}%' ";
                        }
                        sql += "ORDER BY TableCode";
                        result = Context.Ado.SqlQuery<TableInfo>(sql).ToList();
                    }
                    break;
                case DbType.SqlServer:
                    sql = "SELECT * FROM vw_TableDes WHERE 1=1 ";
                    if (!string.IsNullOrWhiteSpace(tableCode))
                    {
                        sql += $"And TableCode Like '%{tableCode}%' ";
                    }
                    if (!string.IsNullOrWhiteSpace(tableName))
                    {
                        sql += $"And Cast(TableName as varchar) LIKE '%{tableName}%' ";
                    }
                    sql += "ORDER BY TableCode";
                    result = Context.Ado.SqlQuery<TableInfo>(sql).ToList();
                    break;
                case DbType.Sqlite:
                    sql = $"SELECT Name AS TableCode FROM sqlite_master WHERE type='table' ";
                    if (!string.IsNullOrWhiteSpace(tableCode))
                    {
                        sql += $"And name Like '%{tableCode}%' ";
                    }
                    sql += "ORDER BY name";
                    result = Context.Ado.SqlQuery<TableInfo>(sql).ToList();
                    break;
                case DbType.Oracle:
                    break;
                case DbType.PostgreSQL:
                    break;
                case DbType.Dm:
                    break;
                case DbType.Kdbndp:
                    break;
                default:
                    break;
            }
            return result;
        }

        /// <summary>
        /// 获取单张的表名称和表注释
        /// </summary>
        /// <param name="tableCode">表名称</param>
        /// <returns></returns>
        public TableInfo GetTableDescription(string tableCode)
        {
            var table = new TableInfo()
            {
                TableCode = tableCode,
                TableName = string.Empty
            };
            string sql;
            switch (dbType)
            {
                case DbType.MySql:
                    var tableSchemaName = Context.CurrentConnectionConfig.ConnectionString.Split(";")
                        .FirstOrDefault(x => x.Equals("database", StringComparison.OrdinalIgnoreCase))?.Split("=").LastOrDefault();
                    if (!string.IsNullOrWhiteSpace(tableSchemaName))
                    {
                        sql = $"select table_name as TableCode,table_comment AS TableName from information_schema.tables where table_schema='{tableSchemaName}'";
                        table = Context.Ado.SqlQuerySingle<TableInfo>(sql);
                    }
                    break;
                case DbType.SqlServer:
                    sql = $"SELECT * FROM vw_TableDes WHERE TableCode = '{tableCode}' ORDER BY TableCode";
                    table = Context.Ado.SqlQuerySingle<TableInfo>(sql);
                    break;
                case DbType.Sqlite:
                    break;
                case DbType.Oracle:
                    break;
                case DbType.PostgreSQL:
                    break;
                case DbType.Dm:
                    break;
                case DbType.Kdbndp:
                    break;
                default:
                    break;
            }
            return table;
        }

        /// <summary>
        /// 获取表字段信息,包含字段名称、字段类型、字段长度以及字段注释
        /// </summary>
        /// <param name="tableCode">表名</param>
        public List<TableColumnInfo> GetSchemaTable(string tableCode)
        {
            var data = new List<TableColumnInfo>();
            string sql;
            switch (dbType)
            {
                case DbType.MySql:
                    var tableSchemaName = Context.CurrentConnectionConfig.ConnectionString.Split(";")
                        .FirstOrDefault(x => x.Equals("database", StringComparison.OrdinalIgnoreCase))?.Split("=").LastOrDefault();
                    sql = $@"SELECT DISTINCT
	column_name AS ColumnName,
	data_type AS TypeName,
	character_Maximum_length AS TypeLenght,
	column_comment AS ColumnDescription,
CASE
		is_nullable 
		WHEN 'NO' THEN
		0 ELSE 1 
	END AS NullAble 
FROM
	information_schema.COLUMNS 
WHERE
	table_schema = '{tableSchemaName}' 
	AND table_name = '{tableCode}'";
                    if (!string.IsNullOrWhiteSpace(tableCode))
                    {
                        data = Context.SqlQueryable<TableColumnInfo>(sql).ToList();
                    }
                    break;
                case DbType.SqlServer:
                    var objname = new SugarParameter("@objname", tableCode);
                    var result = Context.Ado.UseStoredProcedure().GetDataSetAll($"up_help", objname).Tables[1];
                    foreach (DataRow dr in result.Rows)
                    {
                        int.TryParse(dr["length"].ToString(), out int length);
                        var item = new TableColumnInfo
                        {
                            ColumnName = dr["Column_name"].ToString(),
                            TypeName = dr["Type"].ToString(),
                            ColumnDescription = dr["Column_Desc"].ToString(),
                            TypeLenght = length,
                            NullAble = dr["NullAble"].ToString().Equals("yes", StringComparison.OrdinalIgnoreCase)
                        };
                        data.Add(item);
                    }
                    break;
                case DbType.Sqlite:
                    sql = $"PRAGMA table_info('{tableCode}')";
                    var sqliteResult = Context.Ado.SqlQuery<dynamic>(sql).ToList();
                    foreach (var item in sqliteResult)
                    {
                        var typeString = $"{item.type}";
                        int.TryParse(Regex.Match(typeString, "[0-9]+").ToString(), out int lenght);
                        data.Add(new TableColumnInfo
                        {
                            ColumnName = item.name,
                            ColumnDescription = string.Empty,
                            TypeLenght = lenght,
                            NullAble = !Convert.ToBoolean(item.notnull),
                            TypeName = Regex.Match(typeString, "[A-Z,a-z]+").ToString(),
                        });
                    }
                    break;
                case DbType.Oracle:
                    break;
                case DbType.PostgreSQL:
                    break;
                case DbType.Dm:
                    break;
                case DbType.Kdbndp:
                    break;
                default:
                    break;
            }
            //数据格式化
            data.ForEach(x =>
            {
                x.ColumnName = FirstLowerCase(x.ColumnName);
                x.TypeName = SqlToCsharp(x.TypeName);
                x.ColumnDescription = Regex.Replace(x.ColumnDescription, @"[\r\n]", "");
            });
            return data;
        }

        /// <summary>
        /// 数据库类型转C#数据类型,目前仅支持Sql Server、MySql、Sqlite
        /// </summary>
        /// <param name="sqlFieldType">数据表字段类型</param>
        /// <returns>相对应的C#数据类型</returns>
        private string SqlToCsharp(string sqlFieldType)
        {
            string cSharpType;
            switch (sqlFieldType.ToLower())
            {
                case "image":
                case "varbinary":
                case "timestamp":
                case "binary":
                    cSharpType = "byte[]";
                    break;
                case "text":
                case "char":
                case "nvarchar":
                case "nchar":
                case "ntext":
                case "varchar":
                    cSharpType = "string";
                    break;
                case "uniqueidentifier":
                    cSharpType = "Guid";
                    break;
                case "tinyint":
                    cSharpType = "byte";
                    break;
                case "smallint":
                    cSharpType = "Int16";
                    break;
                case "int":
                case "integer":
                    cSharpType = "int";
                    break;
                case "smalldatetime":
                case "datetime":
                case "datetime2":
                    cSharpType = "DateTime";
                    break;
                case "decimal":
                case "smallmoney":
                case "money":
                    cSharpType = "decimal";
                    break;
                case "real":
                    cSharpType = "Single";
                    break;
                case "float":
                    cSharpType = "double";
                    break;
                case "bit":
                    cSharpType = "bool";
                    break;
                case "bigint":
                    cSharpType = "Int64";
                    break;
                default:
                    cSharpType = string.Empty;
                    break;
            }
            return cSharpType;
        }

        /// <summary>
        /// 字符串首字母转小写
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string FirstLowerCase(string str)
        {
            return str.Substring(0, 1).ToLower() + str.Substring(1);
        }

        /// <summary>
        /// 保存文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="text"></param>
        public Response SaveFile(string path, string text)
        {
            var result = new Response()
            {
                Status = true
            };
            var fileName = Path.GetFileName(path);
            var basePath = Path.GetDirectoryName(path);
            try
            {
                if (!File.Exists(path))
                {
                    if (!Directory.Exists(basePath))
                    {
                        Directory.CreateDirectory(basePath);
                    }
                    File.WriteAllText(path, text);
                    result.Message = $"“{fileName}”保存成功";
                }
                else
                {
                    result.Status = false;
                    result.Message = $"保存“{fileName}”失败,文件已存在!";
                }
            }
            catch (Exception ex)
            {
                result.Status = false;
                result.Message = $"保存“{fileName}”失败,{ex.Message}";
            }
            return result;
        }

        public string ConvertToControllerName(string tableName)
        {
            var str = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(tableName);
            return string.Join("", str.Split("_"));
        }

        /// <summary>
        /// 添加菜单栏和按钮
        /// </summary>
        /// <param name="module"></param>
        /// <param name="areaName">区域名称</param>
        /// <returns></returns>
        public Response InsertModelAndButton(SysModule module, string areaName)
        {
            var result = new Response()
            {
                Status = true
            };
            try
            {
                var oldmodule = Context.Queryable<SysModule>().Any(x => x.Url.Equals(module.Url));
                if (oldmodule)
                {
                    result.Status = false;
                    result.Message = $"路径为“{module.Url}”菜单项已存在!";
                }
                else
                {
                    var peer = Context.Queryable<SysModule>().Where(x => x.Url.StartsWith($"/{areaName}/")).First();
                    if (peer != null)
                    {
                        module.ParentName = peer.ParentName;
                        module.ParentId = peer.ParentId;
                    }
                    Response<SysModule> sysModule = _moduleService.Ins(module,base.sysWebUser.Account);
                    List<SysModuleElement> btns = _moduleService.DefaultBtns.Select(x => new SysModuleElement
                    {
                        AreaMenus = "1",
                        Name = x.Text,
                        Icon = x.Icon,
                        Class = x.BtnClass,
                        DomId = x.Value,
                        ModuleId = sysModule.Result.Id,
                        Remark = x.Text,
                        Sort = x.Sort,
                    }).ToList();
                    _moduleService.AddMenu(btns, base.sysWebUser.Account);
                    result = sysModule;
                }
            }
            catch (Exception ex)
            {
                result.Status = false;
                result.Message = ex.Message;
            }
            return result;
        }
    }
}