using Dapper;
using Ewide.Core;
using Ewide.Core.Extension;
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Furion.JsonSerialization;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Ewide.Application.Service
{
    /// <summary>
    /// 鉴定机构相关
    /// </summary>
    [ApiDescriptionSettings(Name = "InspectionOrg")]
    public class InspectionOrgService : IInspectionOrgService, IDynamicApiController, ITransient
    {
        private readonly IJsonSerializerProvider _jsonSerializer;
        private readonly IDapperRepository _dapperRep;

        private readonly IUserManager _userManager;

        private readonly IRepository<BsInspectionOrg> _orgRep;
        private readonly IRepository<BsInspectionOrgUpdate> _orgUpdateRep;
        private readonly IRepository<BsInspectionMemberRelation> _relationRep;
        private readonly IRepository<BsInspectionOrgSetting> _settingRep;

        public InspectionOrgService(
            IJsonSerializerProvider jsonSerializer,
            IDapperRepository dapperRep,
            IUserManager userManager,
            IRepository<BsInspectionOrg> orgRep,
            IRepository<BsInspectionOrgUpdate> orgUpdateRep,
            IRepository<BsInspectionMemberRelation> relationRep,
            IRepository<BsInspectionOrgSetting> settingRep
        )
        {
            _jsonSerializer = jsonSerializer;
            _dapperRep = dapperRep;

            _userManager = userManager;

            _orgRep = orgRep;
            _orgUpdateRep = orgUpdateRep;
            _relationRep = relationRep;
            _settingRep = settingRep;
        }

        /// <summary>
        /// 获取可登记的时间范围
        /// </summary>
        /// <returns></returns>
        private async Task<BsInspectionOrgSetting_DateRange> DateRange()
        {
            // 验证当前时间是否已在可申报时间范围内
            var setting = await _settingRep.DetachedEntities.FirstOrDefaultAsync(p => p.Type == InspectionOrgSettingType.DateRange);
            var dateRange = _jsonSerializer.Deserialize<BsInspectionOrgSetting_DateRange>(setting.Setting);
            return dateRange;
        }

        /// <summary>
        /// 获取当前用户所在机构
        /// </summary>
        /// <returns></returns>
        private async Task<string> RelationOrgId()
        {
            var relation = await _relationRep.DetachedEntities.FirstOrDefaultAsync(p => p.SysUserId.Equals(_userManager.UserId));
            if (relation == null)
            {
                return null;
            }

            return relation.InspectionOrgId;
        }

        /// <summary>
        /// 获取最新备案编号
        /// </summary>
        /// <returns></returns>
        [HttpGet("/inspectionOrg/newCode")]
        public async Task<string> NewCode()
        {
            var nowYear = DateTime.Now.Year;
            var code = await _orgRep.DetachedEntities.MaxAsync(p => p.Code);
            if (String.IsNullOrEmpty(code))
            {
                code = nowYear + "001";
            }
            else
            {
                var year = int.Parse(code.Substring(0, 4));
                var no = int.Parse(code.Substring(4, 3));
                if (year < nowYear)
                {
                    code = nowYear + "001";
                }
                else
                {
                    code = year + (no + 1).ToString().PadLeft(3, '0');
                }
            }

            return code;
        }

        /// <summary>
        /// 获取当前用户所在鉴定机构信息及登记时间范围
        /// </summary>
        /// <returns></returns>
        [HttpGet("/inspectionOrg/registerStatus")]
        public async Task<InspectionRegisterStatusOutput> RegisterStatus()
        {
            var orgId = await RelationOrgId();
            var dateRange = await DateRange();
            var output = dateRange.Adapt<InspectionRegisterStatusOutput>();
            if (String.IsNullOrEmpty(orgId))
            {
                return output;
            }

            var org = await _orgRep.DetachedEntities.FirstOrDefaultAsync(p => p.Id.Equals(orgId));
            var orgUpdate = await _orgUpdateRep.DetachedEntities.Where(p => p.OrgId.Equals(orgId)).OrderByDescending(p => p.CreatedTime).FirstOrDefaultAsync();
            output.InspectionOrg = org.Adapt<InspectionOrgDetailOutput>();
            output.InspectionOrg.UpdateStatus = orgUpdate.Status;
            output.InspectionOrg.Remark = orgUpdate.Remark;
            return output;
        }

        /// <summary>
        /// 鉴定机构登记
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost("/inspectionOrg/register")]
        [UnitOfWork]
        public async Task Register([FromBody] InspectionOrgRegisterInput input)
        {
            // 验证当前时间是否已在可申报时间范围内
            var dateRange = await DateRange();
            var now = DateTime.Now;
            if (!(now >= dateRange.BeginDate && now < dateRange.EndDate.AddDays(1)))
                throw Oops.Oh("当前时间段无法登记鉴定机构");

            // 登记时主表数据应为空, 只向记录数据中插入当前填表的内容
            var updateData = input.Adapt<BsInspectionOrgUpdate>();
            var orgId = await RelationOrgId();
            if (String.IsNullOrEmpty(orgId))
            {
                orgId = Guid.NewGuid().ToString().ToLower();
                var code = await NewCode();
                // 登记主表
                var data = new BsInspectionOrg
                {
                    Id = orgId,
                    Code = code,
                };
                await data.InsertAsync();

                // 用户关系表
                await new BsInspectionMemberRelation
                {
                    SysUserId = _userManager.UserId,
                    InspectionOrgId = orgId
                }.InsertAsync();
            }
            // 登记记录表
            updateData.OrgId = orgId;
            updateData.Type = InspectionOrgUpdateType.Register;
            updateData.Status = InspectionOrgUpdateStatus.Reviewing;
            await updateData.InsertAsync();
        }

        /// <summary>
        /// 鉴定机构申请修改
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost("/inspectionOrg/update")]
        public async Task Update([FromBody] InspectionOrgRegisterInput input)
        {
            var orgId = await RelationOrgId();
            var org = await _orgRep.DetachedEntities.FirstOrDefaultAsync(p => p.Id.Equals(orgId));
            var updateData = input.Adapt<BsInspectionOrgUpdate>();
            // 与之前的值进行对比, 没有变更则不提交
            var updated = false;
            var TSource = org.GetType();
            var TSourceProperties = TSource.GetProperties();
            var TTarget = updateData.GetType();
            var TTargetProperties = TTarget.GetProperties();
            var compareFields = new[] {
                nameof(BsInspectionOrg.Name),
                nameof(BsInspectionOrg.CreditCode),
                nameof(BsInspectionOrg.Address),
                nameof(BsInspectionOrg.Contacts),
                nameof(BsInspectionOrg.ContactsPhone),
                nameof(BsInspectionOrg.LegalPerson),
                nameof(BsInspectionOrg.OrgTelephone),
                nameof(BsInspectionOrg.ApplicationFormFiles),
                nameof(BsInspectionOrg.OrgBusinessLicenseFiles),
                nameof(BsInspectionOrg.InspectionQualificationCertificateFiles),
                nameof(BsInspectionOrg.OfficeInformationFiles),
                nameof(BsInspectionOrg.EmployeeCertificateFiles),
                nameof(BsInspectionOrg.CalibrationCertificateFiles),
                nameof(BsInspectionOrg.OtherFiles)
            };

            foreach (var fields in compareFields)
            {
                var PSource = TSourceProperties.FirstOrDefault(p => p.Name.Equals(fields));
                var VSource = PSource.GetValue(org);
                var PTarget = TTargetProperties.FirstOrDefault(p => p.Name.Equals(fields));
                var VTarget = PTarget.GetValue(updateData);
                if (
                    (VSource == null && VTarget != null)
                    ||
                    (VSource != null && !VSource.Equals(VTarget))
                    )
                {
                    updated = true;
                    break;
                }
            }

            if (!updated)
            {
                throw Oops.Oh("当前信息未修改");
            }

            // 登记记录表
            updateData.OrgId = orgId;
            updateData.Type = InspectionOrgUpdateType.Update;
            updateData.Status = InspectionOrgUpdateStatus.Reviewing;
            await updateData.InsertAsync();
        }

        /// <summary>
        /// 鉴定机构详情
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet("/inspectionOrg/detail")]
        public async Task<InspectionOrgDetailOutput> Detail([FromQuery] InspectionOrgKeyInput input)
        {
            var orgId = String.Empty;
            // 传入Id表示查看他人,否则查看自己
            if (!String.IsNullOrEmpty(input.Id))
            {
                orgId = input.Id;
            }
            else
            {
                orgId = await RelationOrgId();
            }

            if (String.IsNullOrEmpty(orgId))
                return null;

            var output = new InspectionOrgDetailOutput();

            var org = await _orgRep.DetachedEntities.FirstOrDefaultAsync(p => p.Id.Equals(orgId));
            var orgUpdate = await _orgUpdateRep.DetachedEntities.Where(p => p.OrgId.Equals(orgId)).OrderByDescending(p => p.CreatedTime).FirstOrDefaultAsync();

            if (org.Status == InspectionOrgStatus.Normal)
            {
                output.Record = org.Adapt<InspectionOrgRegisterInput>();
            }
            if (orgUpdate != null && 
                (orgUpdate.Status == InspectionOrgUpdateStatus.Reviewing || orgUpdate.Status == InspectionOrgUpdateStatus.Back))
            {
                output.UpdateRecord = orgUpdate.Adapt<InspectionOrgRegisterInput>();
            }

            // 获取最新更新时间
            var lastOrgUpdatedTime = await _orgUpdateRep.DetachedEntities
                .Where(p => p.OrgId.Equals(orgId))
                .Where(p => p.Status == InspectionOrgUpdateStatus.Approved)
                .MaxAsync(p => p.UpdatedTime);

            output.Id = orgId;
            output.Code = org.Code;
            output.Status = org.Status;
            output.UpdateStatus = orgUpdate.Status;
            output.Remark = orgUpdate.Remark;
            output.CreatedTime = org.CreatedTime;
            output.UpdatedTime = lastOrgUpdatedTime;

            return output;
        }

        private async Task<dynamic> Page(InspectionOrgPageInput input, int Type)
        {
            var sql = @"SELECT
	IO.Id,
	IO.Code,
	IOU.Name,
	IOU.CreditCode,
	IOU.Address,
	IOU.Contacts,
	IOU.ContactsPhone,
	IOU.LegalPerson,
	IOU.OrgTelephone,
	IO.Status,
	IOU.Type UpdateType,
	IOU.Status UpdateStatus,
	(SELECT COUNT(0) FROM bs_inspection_org_update WHERE OrgId = IO.Id AND Status <> 1) UpdateCount,
	IOU.UpdatedTime
FROM bs_inspection_org IO
LEFT JOIN 
(SELECT * FROM bs_inspection_org_update WHERE CreatedTime IN (SELECT MAX(CreatedTime) FROM bs_inspection_org_update WHERE Status <> -2 AND Type = @UpdateType GROUP BY OrgId))
IOU ON IOU.OrgId = IO.Id";

            return await _dapperRep.QueryPageDataDynamic(
                sql,
                input,
                param: new { UpdateType = Type },
                filterFields: new[] { "Code", "UpdateStatus" }
            );
        }

        [HttpPost("/inspectionOrg/reviewPage")]
        public async Task<dynamic> ReviewPage([FromBody] InspectionOrgPageInput input)
        {
            return await Page(input, 1);
        }

        [HttpPost("/inspectionOrg/reviewUpdatePage")]
        public async Task<dynamic> ReviewUpdatePage([FromBody] InspectionOrgPageInput input)
        {
            return await Page(input, 2);
        }

        /// <summary>
        /// 登记审核通过
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost("/inspectionOrg/reviewApproved")]
        [UnitOfWork]
        public async Task ReviewApproved([FromBody] InspectionOrgKeyRequiredInput input)
        {
            var orgUpdate = await _orgUpdateRep.DetachedEntities.Where(p => p.OrgId.Equals(input.Id)).OrderByDescending(p => p.CreatedTime).FirstOrDefaultAsync();

            orgUpdate.Status = InspectionOrgUpdateStatus.Approved;
            await orgUpdate.UpdateIncludeAsync(new [] { 
                nameof(BsInspectionOrgUpdate.Status) 
            });

            var org = orgUpdate.Adapt<BsInspectionOrg>();
            org.Id = input.Id;
            org.Status = InspectionOrgStatus.Normal;

            await org.UpdateExcludeAsync(new[] {
                nameof(BsInspectionOrg.Code)
            });
        }

        /// <summary>
        /// 登记审核退回
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost("/inspectionOrg/reviewNotApproved")]
        public async Task ReviewNotApproved([FromBody] InspectionOrgReviewNotApprovedInput input)
        {
            var orgUpdate = await _orgUpdateRep.DetachedEntities.Where(p => p.OrgId.Equals(input.Id)).OrderByDescending(p => p.CreatedTime).FirstOrDefaultAsync();

            orgUpdate.Status = InspectionOrgUpdateStatus.Back;
            orgUpdate.Remark = input.Remark;
            await orgUpdate.UpdateIncludeAsync(new[] {
                nameof(BsInspectionOrgUpdate.Status),
                nameof(BsInspectionOrgUpdate.Remark)
            });
        }

        /// <summary>
        /// 获取审核记录
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet("/inspectionOrg/history")]
        public async Task<dynamic> History([FromQuery] InspectionOrgKeyRequiredInput input)
        {
            var sql = @"SELECT
	IOU.Id,
	IOU.OrgId,
	IO.Code,
    IOU.Remark,
	IOU.Type,
	IOU.Status,
	IOU.CreatedTime,
	IOU.UpdatedTime
FROM bs_inspection_org_update IOU
LEFT JOIN bs_inspection_org IO ON IO.Id = IOU.OrgId
WHERE IO.Id = @Id 
    AND IOU.Status <> 1 -- 审核中
ORDER BY CreatedTime DESC";
            return await _dapperRep.QueryAsync(sql, new { Id = input.Id });
        }
    }
}
