using FrontendWebApi.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Repository.BackendRepository.Interface;
using Repository.FrontendRepository.Interface;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace FrontendWebApi.ApiControllers
{
    public class DeviceManageController : MyBaseApiController<DeviceManageController>
    {
        private readonly IBackendRepository backendRepository;
        private readonly IFrontendRepository frontendRepository;
        private string deviceKindFilePath = "upload/device_icon/";

        public DeviceManageController
        (
            IBackendRepository backendRepository,
            IFrontendRepository frontendRepository
        )
        {
            this.backendRepository = backendRepository;
            this.frontendRepository = frontendRepository;
        }

        /// <summary>
        /// 系統監控列表
        /// </summary>
        /// <param name="account"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("api/Device/GetMainSub")]
        public async Task<ActionResult<ApiResult<History_MainSubBuildFloor>>> GetMainSub([FromBody] FindDevice fd)
        {
            ApiResult<History_MainSubBuildFloor> apiResult = new ApiResult<History_MainSubBuildFloor>(jwt_str);
            if (!jwtlife)
            {
                apiResult.Code = "5000";
                return BadRequest(apiResult);
            }
            else if (string.IsNullOrEmpty(fd.building_tag))
            {
                apiResult.Code = "0002";
                apiResult.Msg = "必須選擇東別";
                return apiResult;
            }

            try
            {
                var dbsub = await frontendRepository.GetAllAsync<HistoryDBMainSub>(
                    @$"select distinct v1.system_key main_name, v1.system_value main_system_tag, v2.system_key sub_name, v2.system_value sub_system_tag, v1.system_priority, v2.system_priority,
                                dk.device_normal_color, dk.device_close_color, dk.device_error_color,dk.device_normal_flashing, dk.device_close_flashing, dk.device_error_flashing
                                -- di.full_name as device_item_name, di.points as device_item_points, di.unit as device_item_unit, di.is_show_riserDiagram as device_item_is_show_riserDiagram,
                                -- di.is_controll as device_item_is_controll, di.is_bool as device_item_is_bool, di.is_link as device_item_is_link
                        from role_auth a
                        join auth_page b on a.AuthCode = b.AuthCode
                        join userinfo c on c.role_guid = a.role_guid
                        join variable v2 on b.ShowView = v2.id and v2.system_type = @sub_system_type
                        join variable v1 on v1.id = v2.system_parent_id and v1.system_type = @main_system_type
                        left join device_kind dk on v1.system_value = dk.device_system_tag and v2.system_value = dk.device_name_tag and dk.device_building_tag = @building_tag
                        -- left join device_item di on v2.system_value = di.device_name_tag and v1.system_value = di.device_system_tag and di.deleted = 0
                        where c.account = @account
                        order by v1.system_priority, v2.system_priority", new { @account = myUser.account, @sub_system_type = sub_system_type, @main_system_type = main_system_type, @building_tag = fd.building_tag });
                var mains = dbsub.GroupBy(a => a.main_system_tag).ToList();
                apiResult.Data = new History_MainSubBuildFloor();
                apiResult.Data.history_Main_Systems = new List<History_Main_system>();

                foreach (var main in mains)
                {
                    History_Main_system history_Main_System = new History_Main_system();
                    history_Main_System.main_system_tag = main.Select(a => a.main_system_tag).FirstOrDefault();
                    history_Main_System.full_name = main.Select(a => a.main_name).FirstOrDefault();
                    history_Main_System.History_Sub_systems = new List<History_Sub_system>();

                    var subs = dbsub.Where(x => x.main_system_tag == main.Select(m => m.main_system_tag).FirstOrDefault()).ToList();
                    foreach(var sub in subs)
                    {
                        History_Sub_system history_Sub_System = new History_Sub_system();
                        history_Sub_System.sub_system_tag = sub.sub_system_tag;
                        history_Sub_System.full_name = sub.sub_name;
                        history_Sub_System.device_normal_color = sub.device_normal_color;
                        history_Sub_System.device_close_color = sub.device_close_color;
                        history_Sub_System.device_error_color = sub.device_error_color;
                        history_Sub_System.device_normal_flashing = sub.device_normal_flashing;
                        history_Sub_System.device_close_flashing = sub.device_close_flashing;
                        history_Sub_System.device_error_flashing = sub.device_error_flashing;

                        history_Main_System.History_Sub_systems.Add(history_Sub_System);
                    }

                    apiResult.Data.history_Main_Systems.Add(history_Main_System);
                }

                apiResult.Code = "0000";
            }
            catch (Exception exception)
            {
                apiResult.Code = "9999";
                apiResult.Msg = "系統內部錯誤,請聯絡管理者。";
                Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message);
                return Ok(apiResult);
            }
            return Ok(apiResult);
        }

        /// <summary>
        /// 東別列表
        /// </summary>
        /// <param name="account"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("api/Device/GetBuild")]
        public async Task<ActionResult<ApiResult<List<BuildList>>>> GetBuild()
        {
            ApiResult<List<BuildList>> apiResult = new ApiResult<List<BuildList>>();
            
            try
            {
                var sqlString = $@"select building_tag, full_name, urn_3D from building where deleted = 0 order by priority";
                var bl = await backendRepository.GetAllAsync<BuildList>(sqlString);

                apiResult.Code = "0000";
                apiResult.Data = bl;
            }
            catch (Exception exception)
            {
                apiResult.Code = "9999";
                apiResult.Msg = "系統內部錯誤,請聯絡管理者。";
                Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message);
                return Ok(apiResult);
            }
            return Ok(apiResult);
        }

        /// <summary>
        /// 樓層列表
        /// </summary>
        /// <param name="account"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("api/Device/GetFloor")]
        public async Task<ActionResult<ApiResult<List<FloorList>>>> GetFloor([FromBody] FindDevice fd)
        {
            ApiResult<List<FloorList>> apiResult = new ApiResult<List<FloorList>>();

            if (string.IsNullOrEmpty(fd.building_tag))
            {
                apiResult.Code = "0002";
                apiResult.Msg = "必須選擇東別";
                return apiResult;
            }
            else if (string.IsNullOrEmpty(fd.sub_system_tag))
            {
                apiResult.Code = "0002";
                apiResult.Msg = "必須選擇小類系統";
                return apiResult;
            }

            try
            {
                var sqlString = $@"select f.full_name as floor_tag
                                   from sub_system_floor ssf
                                   join floor f on ssf.floor_tag = f.full_name and ssf.building_tag = f.building_tag and f.deleted = 0
                                   where ssf.deleted = 0 and f.building_tag = @building_tag and sub_system_tag = @sub_system_tag order by f.priority";
                var param = new { @building_tag = fd.building_tag, @sub_system_tag = fd.sub_system_tag };
                var fl = await backendRepository.GetAllAsync<FloorList>(sqlString, param);

                apiResult.Code = "0000";
                apiResult.Data = fl;
            }
            catch (Exception exception)
            {
                apiResult.Code = "9999";
                apiResult.Msg = "系統內部錯誤,請聯絡管理者。";
                Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message);
                return Ok(apiResult);
            }
            return Ok(apiResult);
        }

        /// <summary>
        /// 設備列表
        /// </summary>
        /// <param name="fd">floor_tag: null->總覽</param>
        /// <returns></returns>
        [HttpPost]
        [Route("api/Device/GetDeviceList")]
        public async Task<ActionResult<ApiResult<List<FloorList>>>> GetDeviceList([FromBody] FindDevice fd)
        {
            ApiResult<List<FloorList>> apiResult = new ApiResult<List<FloorList>>();

            if (string.IsNullOrEmpty(fd.sub_system_tag))
            {
                apiResult.Code = "0001";
                apiResult.Msg = "必須系統類別";
                return BadRequest(apiResult);
            }
            else if (string.IsNullOrEmpty(fd.building_tag))
            {
                apiResult.Code = "0002";
                apiResult.Msg = "必須選擇東別";
                return BadRequest(apiResult);
            }

            try
            {
                var sqlString = $@"select f.full_name, f.InitMapName as map_name, concat(f.floor_map_name,'.svg') as floor_map_name
                                   from sub_system_floor ssf
                                   join floor f on ssf.floor_tag = f.full_name and ssf.building_tag = f.building_tag and f.deleted = 0
                                   where ssf.deleted = 0 and ssf.building_tag = @building_tag and ssf.sub_system_tag = @sub_system_tag and ssf.floor_tag = ifnull(@floor_tag, ssf.floor_tag);";
                var param = new { @building_tag = fd.building_tag, @floor_tag = fd.floor_tag, @sub_system_tag = fd.sub_system_tag };
                var fl = await backendRepository.GetAllAsync<FloorList>(sqlString, param);

                foreach (var f in fl)
                {
                    List<DeviceLists> dl = new List<DeviceLists>();
                    sqlString = $@"select d.device_guid, d.full_name, d.device_coordinate, d.priority, dk.device_image, d.device_number, CONCAT('{baseURL}', '{deviceKindFilePath}', dk.device_image) AS device_image_url,d.status,
				                          dk.device_normal_point_id, dk.device_normal_point_guid, dk.device_normal_point_col, dk.device_normal_point_value, dk.device_normal_flashing, dk.device_normal_point_name,
				                          dk.device_close_point_id, dk.device_close_point_guid, dk.device_close_point_col, dk.device_close_point_value, dk.device_close_flashing, dk.device_close_point_name,
				                          dk.device_error_point_id, dk.device_error_point_guid, dk.device_error_point_col, dk.device_error_point_value, dk.device_error_flashing, dk.device_error_point_name
                                   from device d
                                   left join device_kind dk on d.device_building_tag = dk.device_building_tag and d.device_system_tag = dk.device_system_tag
			                                   and d.device_name_tag = dk.device_name_tag
                                   where d.deleted = 0 and d.device_name_tag = @sub_system_tag and d.device_building_tag = @building_tag and d.device_floor_tag = @floor_tag";
                    var dlParam = new { @sub_system_tag = fd.sub_system_tag, @building_tag = fd.building_tag, @floor_tag = f.full_name };
                    dl = await backendRepository.GetAllAsync<DeviceLists>(sqlString, dlParam);

                    foreach(var d in dl)
                    {
                        var sql_node = $@"SELECT
                                            dn.device_node_guid,
                                            dn.device_guid,
                                            dn.full_name AS Device_node_full_name,
                                            dn.device_node_coordinate,
                                            dn.priority
                                        FROM device_node dn
                                        WHERE dn.deleted = 0 AND dn.device_guid = @device_guid
                                        ORDER BY dn.priority ASC";
                
                        d.Device_nodes = await backendRepository.GetAllAsync<DeviceNode>(sql_node, new { device_guid = d.device_guid });
                    }
                    f.device_list = dl;
                }


                apiResult.Data = fl;
                apiResult.Code = "0000";
            }
            catch (Exception exception)
            {
                apiResult.Code = "9999";
                Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message);
                return Ok(apiResult);
            }
            return Ok(apiResult);
        }

        /// <summary>
        /// 獲取設備資料
        /// </summary>
        /// <param name="fd"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("api/Device/GetBaseDevice")]
        public async Task<ActionResult<ApiResult<DeviceBaseInfo>>> GetBaseDevice([FromBody] FindDevice fd)
        {
            ApiResult<DeviceBaseInfo> apiResult = new ApiResult<DeviceBaseInfo>();
            try
            {
                string sql = $@"select device_number, full_name, device_coordinate, device_coordinate_3d from device where deleted = 0 and device_guid = @device_guid";
                object param = new { @device_guid = fd.device_guid };
                var device = await backendRepository.GetOneAsync<DeviceBaseInfo>(sql, param);

                if(device == null)
                {
                    apiResult.Msg = "查無次設備";
                    apiResult.Code = "0001";
                    return apiResult;
                }

                apiResult.Data = device;
                apiResult.Code = "0000";
            }
            catch (Exception exception)
            {
                apiResult.Code = "9999";
                Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message);
                return Ok(apiResult);
            }
            return Ok(apiResult);
        }

        /// <summary>
        /// 獲取設備運維資料
        /// </summary>
        /// <param name="fd"></param>
        /// <returns></returns>
        [HttpGet]
        [Route("api/Device/GetOpeDevice")]
        public async Task<ActionResult<ApiResult<List<DeviceOpeRecord>>>> GetOpeDevice([FromQuery] string device_guid)
        {
            ApiResult<List<DeviceOpeRecord>> apiResult = new ApiResult<List<DeviceOpeRecord>>();
            try
            {
                string sql = $@"select orr.work_type, orr.fix_do, ui.full_name as work_person_name, orr.finish_time, orr.created_at, dn.device_node_guid, dn.full_name as device_node_name
                                from device d
                                join operation_record orr on d.device_number = orr.fix_do_code and orr.deleted = 0
                                left join userinfo ui on orr.work_person_id = ui.userinfo_guid
                                left join device_node dn on d.device_guid = dn.device_guid
                                where d.deleted = 0 and d.device_guid = @device_guid";
                object param = new { @device_guid = device_guid };
                var device = await backendRepository.GetAllAsync<DeviceOpeRecord>(sql, param);

                if (device.Count == 0)
                {
                    apiResult.Msg = "查無次設備";
                    apiResult.Code = "0001";
                    return apiResult;
                }

                apiResult.Data = device;
                apiResult.Code = "0000";
            }
            catch (Exception exception)
            {
                apiResult.Code = "9999";
                Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message);
                return Ok(apiResult);
            }
            return Ok(apiResult);
        }

        /// <summary>
        /// 取得L型選單的alarm的報警設備
        /// 由原本的obix打API方式改為此action
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Route("api/Device/MenuAlarm")]
        public async Task<ActionResult<ApiResult<List<OntimeAlarmRawData>>>> MenuAlarm(string account)
        {
            ApiResult<List<OntimeAlarmRawData>> apiResult = new ApiResult<List<OntimeAlarmRawData>>(jwt_str);
            if (!jwtlife)
            {
                apiResult.Code = "5000";
                return BadRequest(apiResult);
            }
            try
            {
                //取得發生異常的設備
                string sql = $@"
                                SELECT 
                                    unicode_decode(value) device_point
                                FROM alarmorion_orionalarmrecord a 
                                JOIN alarmorion_orionalarmfacetvalue b on a.id = b.alarm
                                WHERE a.sourceState = 1 and b.facetName = 9";

                var alarmDevicePoints = await frontendRepository.GetAllAsync<string>(sql);

                List<string> alarmDevices = new List<string>();
                foreach (var alarmDevicePoint in alarmDevicePoints)
                {
                    var alarmDevicePointSplit = alarmDevicePoint.Split("_");

                    var alarmDevice = string.Join("_", alarmDevicePointSplit.SkipLast(1));

                    if (!alarmDevices.Contains(alarmDevice))
                    {
                        alarmDevices.Add(alarmDevice);
                    }
                }

                var sqlDevice = $@"
                                SELECT
                                    d.building_guid,
                                    d.main_system_guid,
                                    d.sub_system_guid,
                                    d.floor_guid,
                                    d.device_number
                                FROM Device d
                                INNER JOIN (
                                        SELECT 
                                            ap.building_guid,
                                            ap.ShowView AS sub_system_guid
                                        FROM
                                        (
                                            SELECT *
                                            FROM role_auth ra
                                            WHERE ra.role_guid = (SELECT ui.role_guid FROM userinfo ui WHERE account = @Account)
                                        ) ra
                                        LEFT JOIN auth_page ap ON ra.AuthCode = ap.AuthCode
                                        WHERE ap.AuthType = 1
                                    ) shower ON d.building_guid = shower.building_guid AND d.sub_system_guid = shower.sub_system_guid
                                WHERE d.deleted = 0 AND d.device_number IN @DeviceNumbers
                                ";

                var menuAlarmDevices = await frontendRepository.GetAllAsync<OntimeAlarmRawData>(sqlDevice, new { Account = account, DeviceNumbers = alarmDevices });

                apiResult.Data = menuAlarmDevices;
                apiResult.Code = "0000";
            }
            catch (Exception exception)
            {
                apiResult.Code = "9999";
                Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message);
                return Ok(apiResult);
            }
            return Ok(apiResult);
        }

        /// <summary>
        /// 取得報警設備
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Route("api/Device/Getalarm")]
        public async Task<ActionResult<ApiResult<AlarmObj>>> Alarm()
        {
            ApiResult<AlarmObj> apiResult = new ApiResult<AlarmObj>(jwt_str);
            if (!jwtlife)
            {
                apiResult.Code = "5000";
                return BadRequest(apiResult);
            }
            try
            {
                AlarmObj alarmObj = new AlarmObj()
                {
                    alarmorion = new List<AlarmorionString>(),
                    buildingAlarmDeviceAmount = new List<BuildingAlarmDeviceAmount>()
                };

                // old寫法
                //string sql = $@"SELECT sl.source device_number,from_unixtime(amc.timestamp/1000,'%Y-%m-%d %H:%i:%s') alarm_timestamp FROM alarmorion_orionalarmrecord amc
                //              JOIN (
                //                 SELECT MAX(amc.alarm) ad,m.source FROM alarmorion_orionalarmsourceorder amc
                //                 JOIN (SELECT * FROM alarmorion_orionalarmsource a WHERE substring(a.source,23,5) ='Arena') m ON amc.alarmSource = m.id
                //                 GROUP BY m.source
                //            ) sl ON amc.id = sl.ad 
                //            WHERE amc.sourceState = 1
                //                            ";

                string sql = $@"
                                select * from 
                                (
                                  SELECT a.* ,from_unixtime(a.timestamp/1000,'%Y-%m-%d %H:%i:%s') alarm_timestamp,  errmsg device_point,  
                                  substring( errmsg, 1, 
                                        LENGTH(errmsg) - 
                                        LENGTH(SUBSTRING_INDEX(errmsg, '_', -1) -1) -- 最後一段的長度
                                        -1 -- 減掉最後的 _ ex:D3_B_B1F_CO_
                                       ) device_tag
                                  FROM alarmorion_orionalarmrecord a 
                                    JOIN alarmorion_orionalarmfacetvalue b on a.id = b.alarm
                                   WHERE a.sourceState = 1 and b.facetName = 9
                                 ) a 
                                 left join device b on a.device_tag = b.device_number AND b.deleted = 0
                                 left join device_disaster c on b.device_guid = c.device_guid
                                WHERE c.device_system_value IS NOT NULL";

                var alarms = await frontendRepository.GetAllAsync<AlarmorionString>(sql);

                // old寫法
                //List<AlarmorionString> Alarmorions = new List<AlarmorionString>();
                //foreach (var alarm in alarms)
                //{
                //    var source = alarm.device_number.Split('/');
                //    //if(source[2] != "H")
                //    //{
                //    //    continue;
                //    //}
                //    alarm.device_number = source[6];
                //    Alarmorions.Add(alarm);
                //}

                //alarmObj.alarmorion = Alarmorions.GroupBy(a => new { a.device_number, a.alarm_timestamp }).Select(a => a.First()).ToList();

                foreach(var alarm in alarms)
                {
                    var temp_alarm = alarmObj.alarmorion.Find(x => x.device_number == alarm.device_number);

                    if (temp_alarm == null)
                    {
                        alarmObj.alarmorion.Add(alarm);
                    }
                }


                var device_amount_sql = $@"SELECT 
                                            d.building_guid, 
                                            COUNT(*) AS device_amount
                                            FROM device d
                                            WHERE d.deleted = 0
                                            AND d.device_number IN @devices
                                            GROUP BY d.building_guid";
                alarmObj.buildingAlarmDeviceAmount = await frontendRepository.GetAllAsync<BuildingAlarmDeviceAmount>(device_amount_sql, new { devices = alarmObj.alarmorion.Select(x => x.device_number).ToList() });

                apiResult.Data = alarmObj;
                apiResult.Code = "0000";
            }
            catch (Exception exception)
            {
                apiResult.Code = "9999";
                Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message);
                return Ok(apiResult);
            }
            return Ok(apiResult);
        }

        /// <summary>
        /// 修改設備名稱(打後台API)
        /// </summary>
        /// <param name="change"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("api/Device/SaveChangeName")]
        public async Task<ActionResult<ApiResult<string>>> SaveChangeName(ChangeName change)
        {
            ApiResult<string> apiResult = new ApiResult<string>(jwt_str);
            if (!jwtlife)
            {
                apiResult.Code = "5000";
                return BadRequest(apiResult);
            }
            try
            {
                string authHeader = HttpContext.Request.Headers["Authorization"];
                var websiteurl = await frontendRepository.GetOneAsync<string>("select system_value from variable where system_type = 'website_config' and system_key = 'website_url' ");
                //傳送到後台API
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create($"{websiteurl}api/Device/SaveChangeName");
                request.Method = "POST";
                request.Headers.Add("Authorization", authHeader);
                request.ContentType = "application/x-www-form-urlencoded";
                request.PreAuthenticate = true;
                NameValueCollection postParams = System.Web.HttpUtility.ParseQueryString(string.Empty);
                postParams.Add("TagName", change.TagName);
                postParams.Add("ChangeN", change.ChangeN);
                postParams.Add("ChooseTable", change.ChooseTable.ToString());
                byte[] postData = Encoding.UTF8.GetBytes(postParams.ToString());
                using (Stream st = request.GetRequestStream())
                {
                    st.Write(postData, 0, postData.Length);
                }

                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
                var statusNumber = (int)response.StatusCode;
                if (statusNumber != 200)
                {
                    throw new NotImplementedException(responseString);
                }
                else
                {
                    //解析回傳內容
                    var final = JObject.Parse(responseString);

                    apiResult.Code = final["code"].ToString();
                    apiResult.Data = final["data"].ToString();
                }
            }
            catch (Exception exception)
            {
                apiResult.Code = "9999";
                Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message);
                return Ok(apiResult);
            }
            return Ok(apiResult);
        }

        [HttpPost]
        [Route("api/Device/GetHaveChangeNameRole")]
        public async Task<ActionResult<ApiResult<bool>>> GetHaveChangeNameRole(ChangeNameRole role)
        {
            ApiResult<bool> apiResult = new ApiResult<bool>(jwt_str);
            if (!jwtlife)
            {
                apiResult.Code = "5000";
                return BadRequest(apiResult);
            }
            try
            {
                var sql = $@"
                            select * from userinfo u
                            join role_auth a on a.role_guid = u.role_guid
                            join auth_page p on p.AuthCode = a.AuthCode
                            where u.account = N'{role.Account}'
                            and p.SubName = N'編輯設備名稱'
                            and p.building_guid = N'{role.Building_guid}'";
                var have = await backendRepository.GetOneAsync<string>(sql);
                if (have == null)
                {
                    apiResult.Data = false;
                }
                else
                {
                    apiResult.Data = true;
                }

                apiResult.Code = "0000";
            }
            catch (Exception exception)
            {
                apiResult.Code = "9999";
                Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message);
                return Ok(apiResult);
            }
            return Ok(apiResult);
        }
    }
}