using SqlSugar;
using System.Collections;
using System.Data;
using System.Reflection;

namespace Tiobon.Core.Controllers;

/// <summary>
/// 增删改查基础服务
/// </summary>
/// <typeparam name="IServiceBase"></typeparam>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TEntityDto"></typeparam>
/// <typeparam name="TInsertDto"></typeparam>
/// <typeparam name="TEditDto"></typeparam>
public class BaseController<IServiceBase, TEntity, TEntityDto, TInsertDto, TEditDto> : Controller
{
    #region 初始化
    protected IServiceBase _service;
    /// <summary>
    /// 初始化 (注入)
    /// </summary>
    public BaseController(IServiceBase service)
    {
        _service = service;
    }
    #endregion


    #region 基础接口

    #region 查询
    /// <summary>
    /// 根据条件查询数据
    /// </summary>
    /// <param name="body">条件</param>
    /// <returns></returns>
    [HttpPost, Route("Query")]
    public virtual async Task<ServicePageResult<TEntityDto>> QueryByFilter([FromBody] QueryBody body)
    {
        var data = (await InvokeServiceAsync("QueryFilterPage", [body])) as ServicePageResult<TEntityDto>;
        return data;
    }

    /// <summary>
    /// 查询作废
    /// </summary>
    /// <param name="body"></param>
    /// <returns></returns>
    [HttpPost, Route("QueryDel")]
    public virtual async Task<ServicePageResult<TEntityDto>> QueryDel([FromBody] QueryBody body)
    {
        var data = (await InvokeServiceAsync("QueryFilterPage", [body, null, false])) as ServicePageResult<TEntityDto>;
        return data;
    }
     
    public static T ConvertTo<T>(object input)
    {
        if (input == null)
        {
            throw new ArgumentNullException(nameof(input));
        }

        // 当T是Nullable类型时，需要获取其基础类型
        Type targetType = typeof(T);
        Type nullableType = Nullable.GetUnderlyingType(targetType);
        targetType = nullableType ?? targetType;

        // 检查input是否已经是T类型
        if (input is T)
        {
            return (T)input;
        }

        // 尝试转换
        try
        {
            return (T)Convert.ChangeType(input, targetType);
        }
        catch (InvalidCastException)
        {
            throw new InvalidOperationException($"Cannot convert an instance of type {input.GetType().Name} to type {typeof(T).Name}.");
        }
    }

    /// <summary>
    ///  根据Id查询数据
    /// </summary>
    /// <param name="Id">主键ID</param>
    /// <returns></returns>
    [HttpPost("Query/{Id}")]
    public virtual async Task<ServiceResult<TEntityDto>> QueryById(long Id)
    {
        var entity = await InvokeServiceAsync("QueryById", [Id]);
        if (entity is null)
            return Failed<TEntityDto>("获取失败");
        else
            return Success<TEntityDto>(ConvertTo<TEntityDto>(entity), "获取成功");
    }
    /// <summary>
    ///  查询菜单表单信息
    /// </summary>
    /// <param name="body">body</param>
    /// <returns></returns>
    [HttpPost("QueryForm")]
    public virtual async Task<ServiceFormResult<TEntityDto>> QueryForm([FromBody] QueryForm body)
    {
        var entity = await InvokeServiceAsync("QueryForm", [body]) as ServiceFormResult<TEntityDto>;
        return entity;
    }
    #endregion

    #region 新增
    /// <summary>
    /// 新增数据
    /// </summary>
    /// <param name="insertModel"></param>
    /// <returns></returns>
    [HttpPost("Insert")]
    public virtual async Task<ServiceResult<string>> Insert([FromBody] TInsertDto insertModel)
    {
        var data = Success<string>(null, "新增成功");
        var id = Convert.ToInt64(await InvokeServiceAsync("Add", [insertModel]));
        data.Success = id > 0;
        if (data.Success)
            data.Data = id.ObjToString();
        else
            return Failed<string>("新增失败");

        return data;
    }

    /// <summary>
    /// 批量新增数据
    /// </summary>
    /// <param name="insertModels"></param>
    [HttpPost, Route("BulkInsert")]
    public virtual async Task<ServiceResult<List<long>>> BulkInsert([FromBody] List<TInsertDto> insertModels)
    {
        var data = Success<List<long>>(null, "新增成功");
        var ids = await InvokeServiceAsync("Add", [insertModels]) as List<long>;
        data.Success = ids.Any();
        if (data.Success)
            data.Data = ids;
        else
            return Failed<List<long>>("新增失败");

        return data;
    }

    #endregion

    #region 更新
    /// <summary>
    /// 更新数据
    /// </summary>
    /// <param name="Id">主键ID</param>
    /// <param name="editModel"></param>
    /// <returns></returns>
    [HttpPost("Update/{Id}")]
    public virtual async Task<ServiceResult> Put(long Id, [FromBody] TEditDto editModel)
    {
        var data = Success("更新成功");
        var flag = Convert.ToBoolean(await InvokeServiceAsync("Update", [Id, editModel]));
        if (!flag)
            return Failed("更新失败");
        return data;
    }
    /// <summary>
    ///  批量更新数据
    /// </summary>
    /// <param name="editModels"></param>
    [HttpPost, Route("BulkUpdate")]
    public virtual async Task<ServiceResult> BulkUpdate([FromBody] Dictionary<long, TEditDto> editModels)
    {
        var data = Success("更新成功");
        var flag = Convert.ToBoolean(await InvokeServiceAsync("Update", [editModels]));
        if (!flag)
            return Failed("更新失败");
        return data;
    }
    #endregion

    #region 删除
    /// <summary>
    /// 删除数据
    /// </summary>
    /// <param name="Id">主键ID</param>
    /// <returns></returns>
    [HttpPost("Delete/{Id}")]
    public virtual async Task<ServiceResult> Delete(long Id)
    {
        var data = Success("作废成功");
        var isExist = Convert.ToBoolean(await InvokeServiceAsync("AnyAsync", [Id]));
        if (!isExist)
            return Failed("作废失败");
        data.Success = Convert.ToBoolean(await InvokeServiceAsync("DeleteById1", [Id]));
        if (!data.Success)
            return Failed("作废失败");
        return data;
    }

    /// <summary>
    /// 批量删除数据
    /// </summary>
    /// <param name="Ids">主键IDs</param>
    /// <returns></returns>
    [HttpPost, Route("BulkDelete")]
    public virtual async Task<ServiceResult> BulkDelete([FromBody] long[] Ids)
    {
        var data = Success($"成功作废({Ids.Count()}笔数据)");
        data.Success = Convert.ToBoolean(await InvokeServiceAsync("DeleteByIds1", [Ids]));
        if (!data.Success)
            return Failed("作废失败");
        return data;
    }
    #endregion

    #endregion

    #region Excel导出
    /// <summary>
    /// Excel导出
    /// </summary>
    /// <param name="body"></param>
    /// <returns></returns>
    [HttpPost, Route("ExportExcel")]
    public virtual async Task<ServiceResult<ExcelData>> ExportExcel([FromBody] QueryExport body)
    {
        var data = (await InvokeServiceAsync("ExportExcel", [body])) as ServiceResult<ExcelData>;
        return data;
    }
    /// <summary>
    /// 列名按照前端显示顺序导出
    /// </summary>
    /// <param name="columns"></param>
    /// <param name="ExportFields"></param>
    /// <returns></returns>
    public static (List<string>, List<string>) Sort(Dictionary<string, string> columns, List<string> ExportFields)
    {
        List<string> fields = new List<string>();
        List<string> colunms = new List<string>();
        if (ExportFields == null || ExportFields.Count == 0)
        {
            return (columns.Keys.ToList(), columns.Values.ToList());
        }

        foreach (var field in ExportFields)
        {
            foreach (var item in columns)
            {
                if (item.Key == field)
                {
                    fields.Add(item.Key);
                    colunms.Add(item.Value);
                }
            }
        }

        return (fields, colunms);
    }

    public static DataTable ToDataTable(List<TEntityDto> list, Dictionary<string, string> fieldDescs = null, params string[] propertyName)
    {
        var (fields, colunms) = Sort(fieldDescs, null);

        List<string> propertyNameList = new List<string>();
        if (propertyName != null)
        {
            propertyNameList.AddRange(propertyName);
        }
        DataTable result = new DataTable();
        if (list.Count > 0)
        {
            PropertyInfo[] propertys = list[0].GetType().GetProperties();
            for (int i = 0; i < fields.Count; i++)
            {
                foreach (PropertyInfo pi in propertys)
                {
                    if (propertyNameList.Count == 0)
                    {
                        //if (DBNull.Value.Equals(pi.PropertyType))
                        //{
                        //   // pi.PropertyType = DateTime;
                        //}
                        Type colType = pi.PropertyType;
                        if (colType.IsGenericType && colType.GetGenericTypeDefinition() == typeof(Nullable<>))
                        {
                            colType = colType.GetGenericArguments()[0];
                        }
                        if (fields[i] == pi.Name)
                        {
                            result.Columns.Add(colunms[i], colType);
                        }
                        //result.Columns.Add(pi.Name, pi.PropertyType);
                    }
                    else
                    {
                        if (propertyNameList.Contains(pi.Name))
                        {
                            if (fields[i] == pi.Name)
                                result.Columns.Add(fields[i], pi.PropertyType);
                        }
                    }
                }
            }
            for (int i = 0; i < list.Count; i++)
            {
                ArrayList tempList = new ArrayList();
                for (int j = 0; j < fields.Count; j++)
                {
                    foreach (PropertyInfo pi in propertys)
                    {
                        if (fields[j] == pi.Name)
                        {
                            if (propertyNameList.Count == 0)
                            {
                                object obj = pi.GetValue(list[i], null);
                                tempList.Add(obj);
                            }
                            else
                            {
                                if (propertyNameList.Contains(pi.Name))
                                {
                                    object obj = pi.GetValue(list[i], null);
                                    tempList.Add(obj);
                                }
                            }
                        }
                    }
                }
                object[] array = tempList.ToArray();
                result.LoadDataRow(array, true);
            }
        }
        return result;
    }
    #endregion



    #region Excel导入 
    [HttpPost("ImportExcel")]
    public async Task<ServiceResult<ExcelData>> ImportExcel(IFormFile file, string menuName = null, long? MasterId = null)
    {
        var data = (await InvokeServiceAsync("ImportExcel", [file, menuName, MasterId])) as ServiceResult<ExcelData>;
        return data;
    }
    #endregion

    #region Excel下载
    [HttpGet("DownExcelTemplate"), AllowAnonymous]
    public async Task<IActionResult> DownloadExcel(string menuName)
    {
        var result = (await InvokeServiceAsync("DownloadExcel", [menuName])) as ServiceResult<string>;
        string path = result.Data;
        FileStream fs = new FileStream(path, FileMode.OpenOrCreate);
        fs.Close();
        return File(new FileStream(path, FileMode.Open), "application/octet-stream", result.Message);
    }
    #endregion

    ///// <summary>
    ///// 反射调用service方法
    ///// </summary>
    ///// <param name="methodName"></param>
    ///// <param name="parameters"></param>
    ///// <returns></returns>
    //[NonAction]
    //private object InvokeService(string methodName, object[] parameters)
    //{
    //    return _service.GetType().GetMethod(methodName).Invoke(_service, parameters);
    //}
    ///// <summary>
    ///// 反射调用service方法
    ///// </summary>
    ///// <param name="methodName"></param>
    ///// <param name="types">为要调用重载的方法参数类型：new Type[] { typeof(SaveDataModel)</param>
    ///// <param name="parameters"></param>
    ///// <returns></returns>
    //[NonAction]
    //private object InvokeService(string methodName, Type[] types, object[] parameters) => _service.GetType().GetMethod(methodName, types).Invoke(_service, parameters);


    [NonAction]
    private async Task<object> InvokeServiceAsync(string methodName, object[] parameters)
    {
        var task = _service.GetType().InvokeMember(methodName, BindingFlags.InvokeMethod, null, _service, parameters) as Task;
        if (task != null) await task;
        var result = task?.GetType().GetProperty("Result")?.GetValue(task);
        return result;
    }

    [NonAction]
    public ServiceResult<T> Success<T>(T data, string message = "成功")
    {
        return new ServiceResult<T>() { Success = true, Message = message, Data = data, };
    }

    // [NonAction]
    //public ServiceResult<T> Success<T>(T data, string msg = "成功",bool success = true)
    //{
    //    return new ServiceResult<T>()
    //    {
    //        success = success,
    //        msg = msg,
    //        response = data,
    //    };
    //}
    [NonAction]
    public ServiceResult Success(string message = "成功")
    {
        return new ServiceResult() { Success = true, Message = message, Data = null, };
    }

    [NonAction]
    public ServiceResult Failed(string message = "失败", int status = 500)
    {
        return new ServiceResult() { Success = false, Status = status, Message = message, Data = null, };
    }

    [NonAction]
    public ServiceResult<T> Failed<T>(string message = "失败", int status = 500)
    {
        return new ServiceResult<T>() { Success = false, Status = status, Message = message, Data = default, };
    }

    //Mapper.Map(data).ToANew<TEntityDto>()

    [NonAction]
    public ServiceResult<PageModel<T>> SuccessPage<T>(int page, int dataCount, int pageSize, List<T> data, int pageCount, string message = "获取成功")
    {
        return new ServiceResult<PageModel<T>>() { Success = true, Message = message, Data = new PageModel<T>(page, dataCount, pageSize, data) };
    }

    [NonAction]
    public ServiceResult<PageModel<T>> SuccessPage<T>(PageModel<T> pageModel, string message = "获取成功")
    {
        return new ServiceResult<PageModel<T>>() { Success = true, Message = message, Data = pageModel };
    }
}