using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;

using FMSAdmin.Data;
using FMSAdmin.Helpers;
using FMSAdmin.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using FMSAdmin.Models;
using System.Transactions;
using FMSApp.Repositories;

namespace FMSApp.Services {
    public class WorkRequestService {
        private readonly ILogger<WorkRequestService> _logger;
        private readonly FMSContext _context;

        private readonly AppSettings _appSettings;
        private readonly StorageHelper _storage;

        private readonly WorkRequestRepository _workRequestRepository;
        private readonly WorkConfigRepository _workConfigRepository;

        public WorkRequestService(
            ILogger<WorkRequestService> logger,
            FMSContext context,
            IOptions<AppSettings> appSettings,
            StorageHelper storage,
            WorkRequestRepository workRequestRepository,
            WorkConfigRepository workConfigRepository
            ) {
            _logger = logger;
            _context = context;
            _appSettings = appSettings.Value;
            _storage = storage;
            _workRequestRepository = workRequestRepository;
            _workConfigRepository = workConfigRepository;
        }


        /// <summary>
        /// 저장(작업이력추가 이전까지 수정가능)
        /// 신규프로세스 / Schedule미사용
        /// Schedule 미사용시 계획에 대한 모든 작업이 한번에 일괄등록된다.
        /// 일괄등록된 데이터가 작업지시돼고, 작업진행이 됐을때... 작업진행 화면에서 수정이 가능해 져야한다.
        /// 그래서, 작업계획, 작업대기, 작업진행 상태의 데이터 모두 수정가능해야 한다.
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public void SaveBeforeAddHistory(FmsWorkRequest data) {
            using (var trans = new TransactionScope()) {
                var persist = _context.FmsWorkRequest.FirstOrDefault(
                    x => x.SiteId == data.SiteId
                    && x.WorkRequestId == data.WorkRequestId
                );

                if (persist == null) {
                    if (data.WorkTypeId == 0) {
                        throw new ServiceException("업무구분이 입력되지 않았습니다.");
                    }

                    var detailType = _context.FmsWorkCodeDetailType.FirstOrDefault(x => x.WorkDetailTypeId == data.WorkDetailTypeId);
                    if (data.WorkTypeId == 5 || data.WorkTypeId == 6) {
                        if (detailType == null) {
                            throw new ServiceException("작업구분이 입력되지 않았습니다.");
                        }
                    }

                    // 설비 기본정보 생성되지 않도록 처리
                    foreach (var facility in data.FmsWorkRequestToFacility) {
                        facility.CmFacility = null;
                    }

                    // 사고가 아닐때 사고목록 초기화
                    if (Util.V(detailType, x => x.Code) != "ACCIDENT") {
                        data.FmsWorkRequestToAccident = new List<FmsWorkRequestToAccident>();
                    }
                    // 사고 기본정보 생성되지 않도록 처리
                    foreach (var accident in data.FmsWorkRequestToAccident) {
                        accident.FmsAccident = null;
                    }

                    // 민원이 아닐때 민원목록 초기화
                    if (Util.V(detailType, x => x.Code) != "COMPLAINT") {
                        data.FmsWorkRequestToComplaint = new List<FmsWorkRequestToComplaint>();
                    }
                    // 민원 기본정보 생성되지 않도록 처리
                    foreach (var complaint in data.FmsWorkRequestToComplaint) {
                        complaint.FmsComplaints = null;
                    }

                    if (data.CmFile != null && data.CmFile.IsUpload) {
                        data.CmFile.SiteId = data.SiteId;
                        var category = _context.CmFileCategory.First(x => x.Name == "workrequest");
                        data.CmFile.FileCategory = category;
                        data.CmFile.CreatedDate = DateTime.Now;
                    } else {
                        data.CmFile = null;
                    }

                    data.WorkProgressId = 1;
                    data.WorkScheduleId = null;
                    data.FmsWorkSchedule = null;
                    data.RequestDate = DateTime.Now;

                    _context.FmsWorkRequest.Add(data);
                    _context.SaveChanges();

                    if (data.CmFile != null && data.CmFile.IsUpload) {
                        _storage.CopyEntity(data.CmFile.Path, data.CmFile);
                    }

                } else {
                    if (_workConfigRepository.IsAutoResult(true)) {
                        // 자동지시, 자동실행 일 경우
                        // 작업이력이 등록된 데이터를 진행중으로 본다.
                        if (persist.FmsWorkResult != null
                            && persist.FmsWorkResult.FmsWorkResultCheckItem != null
                            && persist.FmsWorkResult.FmsWorkResultCheckItem.Count > 0) {
                            throw new ServiceException("진행중인 작업은 계획 변경이 불가능합니다.");
                        }
                    } else if (_workConfigRepository.IsAutoOrder(true)) {
                        // 자동지시일 경우
                        // 작업계획, 작업대기의 데이터만 변경가능하다.
                        if (persist.WorkProgressId != 1 && persist.WorkProgressId != 3) {
                            throw new ServiceException("진행중인 작업은 계획 변경이 불가능합니다.");
                        }
                    } else {
                        // 그외
                        // 작업계획일때만 변경가능하다.
                        if (persist.WorkProgressId != 1) {
                            throw new ServiceException("진행중인 작업은 계획 변경이 불가능합니다.");
                        }
                    }

                    // 작업계획의 일자 변경
                    if (persist.FmsWorkSchedule != null && Util.V(persist, x => x.FmsWorkSchedule.WorkDueDate.Date) != Util.V(data, x => x.StartWorkDate.Date)) {
                        persist.FmsWorkSchedule.WorkDueDate = data.StartWorkDate.Date;
                    }

                    // 파일 Entity 처리
                    if (data.CmFile != null && data.CmFile.IsDelete) {
                        persist.CmFile = null;
                        persist.FileId = null;
                    } else if (data.CmFile != null && data.CmFile.IsUpload) {
                        var category = _context.CmFileCategory.First(x => x.Name == "workrequest");
                        persist.CmFile = new CmFile {
                            SiteId = data.SiteId,
                            FileCategory = category,
                            CreatedDate = DateTime.Now,
                            Name = data.CmFile.Name,
                            FileSize = data.CmFile.FileSize,
                            ContentType = data.CmFile.ContentType,
                        };
                    }

                    // 입력 데이터 반영
                    persist.BusinessFieldId = data.BusinessFieldId;
                    persist.StartWorkDate = data.StartWorkDate;
                    persist.Title = data.Title;
                    persist.Content = data.Content;
                    persist.WorkDetailTypeId = data.WorkDetailTypeId;
                    persist.WorkPartnerId = data.WorkPartnerId;
                    persist.WorkPrice = data.WorkPrice;
                    //persist.WorkItem = data.WorkItem;
                    persist.WorkEndDate = data.WorkEndDate;


                    // 설비 전체 제거
                    persist.FmsWorkRequestToFacility.Clear();

                    // 추가된 설비 생성
                    foreach (var facility in data.FmsWorkRequestToFacility) {
                        persist.FmsWorkRequestToFacility.Add(new FmsWorkRequestToFacility() {
                            SiteId = persist.SiteId,
                            WorkRequestId = persist.WorkRequestId,
                            FacilityCode = facility.FacilityCode,
                        });
                    }

                    // 사고 전체 제거
                    persist.FmsWorkRequestToAccident.Clear();

                    // 추가된 사고 생성
                    foreach (var accident in data.FmsWorkRequestToAccident) {
                        persist.FmsWorkRequestToAccident.Add(new FmsWorkRequestToAccident() {
                            SiteId = persist.SiteId,
                            WorkRequestId = persist.WorkRequestId,
                            AccidentId = accident.AccidentId,
                        });
                    }


                    // 민원 전체 제거
                    persist.FmsWorkRequestToComplaint.Clear();

                    // 추가된 민원 생성
                    foreach (var complaint in data.FmsWorkRequestToComplaint) {
                        persist.FmsWorkRequestToComplaint.Add(new FmsWorkRequestToComplaint() {
                            SiteId = persist.SiteId,
                            WorkRequestId = persist.WorkRequestId,
                            ComplaintId = complaint.ComplaintId,
                        });
                    }

                    _context.FmsWorkRequest.Update(persist);
                    _context.SaveChanges();

                    // FmsWorkOrder 업데이트
                    var order = persist.FmsWorkOrder;
                    if (order != null) {
                        order.BusinessFieldId = data.BusinessFieldId;
                        order.StartWorkDate = data.StartWorkDate;
                        order.Title = data.Title;
                        order.Content = data.Content;

                        order.FmsWorkOrderToFacility.Clear();
                        foreach (var facility in data.FmsWorkRequestToFacility) {
                            order.FmsWorkOrderToFacility.Add(new FmsWorkOrderToFacility() {
                                SiteId = persist.SiteId,
                                WorkRequestId = persist.WorkRequestId,
                                FacilityCode = facility.FacilityCode,
                            });
                        }

                        _context.FmsWorkOrder.Update(order);
                        _context.SaveChanges();
                    }

                    // FmsWorkResult 업데이트
                    var result = persist.FmsWorkResult;
                    if (result != null) {
                        result.StartDate = data.StartWorkDate.Date;
                        result.EndDate = data.StartWorkDate.Date;

                        result.FmsWorkResultToFacility.Clear();
                        foreach (var facility in data.FmsWorkRequestToFacility) {
                            result.FmsWorkResultToFacility.Add(new FmsWorkResultToFacility() {
                                SiteId = persist.SiteId,
                                WorkRequestId = persist.WorkRequestId,
                                FacilityCode = facility.FacilityCode,
                            });
                        }

                        _context.FmsWorkResult.Update(result);
                        _context.SaveChanges();
                    }

                    // 파일복사
                    if (data.CmFile != null && data.CmFile.IsUpload) {
                        _storage.CopyEntity(data.CmFile.Path, persist.CmFile);
                        if (persist.CmFile != null) {
                            // 파일 삭제시 진짜 DB삭제?
                        }
                    }
                }

                // 자동지시 설정되어있을 경우 작업예정일이 오늘 포함 이전일자의 계획을 자동지시처리
                if (_workConfigRepository.IsAutoOrder()) {
                    persist = _context.FmsWorkRequest.FirstOrDefault(
                        x => x.SiteId == data.SiteId
                        && x.WorkRequestId == data.WorkRequestId
                    );

                    if (persist.StartWorkDate.Date <= DateTime.Now.Date && persist.WorkProgressId == 1) {
                        _workRequestRepository.WorkToOrder(persist.SiteId, persist.WorkRequestId, persist.RequestUserId, "", false);
                    }
                }

                trans.Complete();
            }
        }

        /// <summary>
        /// 민원 저장
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public void SaveComplaints(FmsWorkRequest data) {
            using (var trans = new TransactionScope()) {
                var persist = _context.FmsWorkRequest.FirstOrDefault(
                    x => x.SiteId == data.SiteId
                    && x.WorkRequestId == data.WorkRequestId
                );
                if (persist == null) throw new ServiceException("존재하지 않는 데이터입니다.");

                // 사고 전체 제거
                persist.FmsWorkRequestToComplaint.Clear();

                // 추가된 사고 생성
                foreach (var complaint in data.FmsWorkRequestToComplaint) {
                    persist.FmsWorkRequestToComplaint.Add(new FmsWorkRequestToComplaint() {
                        SiteId = persist.SiteId,
                        WorkRequestId = persist.WorkRequestId,
                        ComplaintId = complaint.ComplaintId,
                    });
                }

                _context.FmsWorkRequest.Update(persist);
                _context.SaveChanges();

                trans.Complete();
            }
        }

        /// <summary>
        /// 사고 저장
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public void SaveAccidents(FmsWorkRequest data) {
            using (var trans = new TransactionScope()) {
                var persist = _context.FmsWorkRequest.FirstOrDefault(
                    x => x.SiteId == data.SiteId
                    && x.WorkRequestId == data.WorkRequestId
                );
                if (persist == null) throw new ServiceException("존재하지 않는 데이터입니다.");

                // 사고 전체 제거
                persist.FmsWorkRequestToAccident.Clear();

                // 추가된 사고 생성
                foreach (var accident in data.FmsWorkRequestToAccident) {
                    persist.FmsWorkRequestToAccident.Add(new FmsWorkRequestToAccident() {
                        SiteId = persist.SiteId,
                        WorkRequestId = persist.WorkRequestId,
                        AccidentId = accident.AccidentId,
                    });
                }

                _context.FmsWorkRequest.Update(persist);
                _context.SaveChanges();

                trans.Complete();
            }
        }





        /// <summary>
        /// 저장
        /// 기존프로세스 / Schedule사용
        /// Schedule 사용시 Schedule 테이블의 데이터가 수정돼고 Schedule 당 하나의 Request 만 살아있으므로,
        /// 진행중인 Request 에 대해 수정이 가해질 필요가 없으므로
        /// 기초계획등록 화면에서만 사용하며, 작업계획 상태의 데이터만 변경 가능하다.
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public void Save(FmsWorkRequest data) {
            using (var trans = new TransactionScope()) {
                var persist = _context.FmsWorkRequest.FirstOrDefault(
                    x => x.SiteId == data.SiteId
                    && x.WorkRequestId == data.WorkRequestId
                );

                if (persist == null) {
                    if (data.WorkTypeId == 0) {
                        throw new ServiceException("업무구분이 입력되지 않았습니다.");
                    }
                    var detailType = _context.FmsWorkCodeDetailType.FirstOrDefault(x => x.WorkDetailTypeId == data.WorkDetailTypeId);
                    if (data.WorkTypeId == 5 || data.WorkTypeId == 6) {
                        if (detailType == null) {
                            throw new ServiceException("작업구분이 입력되지 않았습니다.");
                        }
                    }

                    // 설비 기본정보 생성되지 않도록 처리
                    foreach (var facility in data.FmsWorkRequestToFacility) {
                        facility.CmFacility = null;
                    }

                    // 사고가 아닐때 사고목록 초기화
                    if (Util.V(detailType, x => x.Code) != "ACCIDENT") {
                        data.FmsWorkRequestToAccident = new List<FmsWorkRequestToAccident>();
                    }
                    // 사고 기본정보 생성되지 않도록 처리
                    foreach (var accident in data.FmsWorkRequestToAccident) {
                        accident.FmsAccident = null;
                    }

                    // 민원이 아닐때 민원목록 초기화
                    if (Util.V(detailType, x => x.Code) != "COMPLAINT") {
                        data.FmsWorkRequestToComplaint = new List<FmsWorkRequestToComplaint>();
                    }
                    // 민원 기본정보 생성되지 않도록 처리
                    foreach (var complaint in data.FmsWorkRequestToComplaint) {
                        complaint.FmsComplaints = null;
                    }

                    data.WorkProgressId = 1;
                    data.WorkScheduleId = null;
                    data.FmsWorkSchedule = null;
                    data.RequestDate = DateTime.Now;

                    _context.FmsWorkRequest.Add(data);
                    _context.SaveChanges();
                } else {
                    if (persist.WorkProgressId != 1) {
                        throw new ServiceException("진행중인 작업은 계획 변경이 불가능합니다.");
                    }

                    // 작업계획의 일자 변경
                    if (persist.FmsWorkSchedule != null && Util.V(persist, x => x.FmsWorkSchedule.WorkDueDate.Date) != Util.V(data, x => x.StartWorkDate.Date)) {
                        persist.FmsWorkSchedule.WorkDueDate = data.StartWorkDate.Date;
                    }

                    // 입력 데이터 반영
                    persist.BusinessFieldId = data.BusinessFieldId;
                    persist.StartWorkDate = data.StartWorkDate;
                    persist.Title = data.Title;
                    persist.Content = data.Content;
                    persist.WorkDetailTypeId = data.WorkDetailTypeId;
                    persist.WorkPartnerName = data.WorkPartnerName;
                    persist.WorkPrice = data.WorkPrice;
                    //persist.WorkItem = data.WorkItem;

                    // 설비 전체 제거
                    persist.FmsWorkRequestToFacility.Clear();

                    // 추가된 설비 생성
                    foreach (var facility in data.FmsWorkRequestToFacility) {
                        persist.FmsWorkRequestToFacility.Add(new FmsWorkRequestToFacility() {
                            SiteId = persist.SiteId,
                            WorkRequestId = persist.WorkRequestId,
                            FacilityCode = facility.FacilityCode,
                        });
                    }

                    // 사고 전체 제거
                    persist.FmsWorkRequestToAccident.Clear();

                    // 추가된 사고 생성
                    foreach (var accident in data.FmsWorkRequestToAccident) {
                        persist.FmsWorkRequestToAccident.Add(new FmsWorkRequestToAccident() {
                            SiteId = persist.SiteId,
                            WorkRequestId = persist.WorkRequestId,
                            AccidentId = accident.AccidentId,
                        });
                    }


                    // 민원 전체 제거
                    persist.FmsWorkRequestToComplaint.Clear();

                    // 추가된 민원 생성
                    foreach (var complaint in data.FmsWorkRequestToComplaint) {
                        persist.FmsWorkRequestToComplaint.Add(new FmsWorkRequestToComplaint() {
                            SiteId = persist.SiteId,
                            WorkRequestId = persist.WorkRequestId,
                            ComplaintId = complaint.ComplaintId,
                        });
                    }

                    _context.FmsWorkRequest.Update(persist);
                    _context.SaveChanges();
                }

                // 자동지시 설정되어있을 경우 작업예정일이 오늘 포함 이전일자의 계획을 자동지시처리
                if (_workConfigRepository.IsAutoOrder()) {
                    persist = _context.FmsWorkRequest.FirstOrDefault(
                        x => x.SiteId == data.SiteId
                        && x.WorkRequestId == data.WorkRequestId
                    );

                    if (persist.StartWorkDate.Date <= DateTime.Now.Date) {
                        _workRequestRepository.WorkToOrder(persist.SiteId, persist.WorkRequestId, persist.RequestUserId, "", false);
                    }
                }

                trans.Complete();
            }
        }

        public void Delete(int siteId, int workRequestId) {
            using (var trans = new TransactionScope()) {
                var workRequestToFacilities = _context.FmsWorkRequestToFacility.Where(
                    x => x.SiteId == siteId
                    && x.WorkRequestId == workRequestId
                ).ToList();
                foreach (var item in workRequestToFacilities) {
                    _context.FmsWorkRequestToFacility.Remove(item);
                }
                _context.SaveChanges();

                var workRequestToAccidents = _context.FmsWorkRequestToAccident.Where(
                    x => x.SiteId == siteId
                    && x.WorkRequestId == workRequestId
                ).ToList();
                foreach (var item in workRequestToAccidents) {
                    _context.FmsWorkRequestToAccident.Remove(item);
                }
                _context.SaveChanges();

                //민원 삭제 추가
                var workRequestToComplaints = _context.FmsWorkRequestToComplaint.Where(
                                    x => x.SiteId == siteId
                                    && x.WorkRequestId == workRequestId
                                ).ToList();
                foreach (var item in workRequestToComplaints) {
                    _context.FmsWorkRequestToComplaint.Remove(item);
                }
                _context.SaveChanges();

                var workRequest = _context.FmsWorkRequest.Where(
                    x => x.SiteId == siteId
                    && x.WorkRequestId == workRequestId
                ).ToList();
                foreach (var item in workRequest) {
                    if (item.WorkScheduleId != null) {
                        throw new ServiceException("정기점검, 법정검사는 삭제가 불가능합니다.");
                    }
                    if (item.WorkProgressId != 1) {
                        throw new ServiceException("작업계획 단계의 데이터만 삭제 가능합니다.");
                    }

                    _context.FmsWorkRequest.Remove(item);
                }
                _context.SaveChanges();

                trans.Complete();
            }
        }

        // 반려 (실행계획 화면에서 빠지고 작업진행관리 화면에서 반려하도록 변경됨)
        public void Reject(FmsWorkRequest data, bool useSchedule, DateTime? confirmDate) {
            using (var trans = new TransactionScope()) {
                var persist = _context.FmsWorkRequest.FirstOrDefault(
                    x => x.SiteId == data.SiteId
                    && x.WorkRequestId == data.WorkRequestId
                );
                if (persist == null) throw new ServiceException("존재하지 않는 데이터 입니다.");

                persist.WorkProgressId = 2; // 반려
                persist.RejectUserId = data.RejectUserId;
                persist.RejectReason = data.RejectReason;
                persist.RejectDate = DateTime.Now;

                _context.FmsWorkRequest.Update(persist);
                _context.SaveChanges();

                // 작업계획 처리
                if (persist.WorkScheduleId != null && persist.WorkScheduleId != 0) {
                    var schedule = _context.FmsWorkSchedule.FirstOrDefault(
                        x => x.SiteId == data.SiteId
                        && x.WorkScheduleId == persist.WorkScheduleId
                    );

                    if (useSchedule) {
                        if (confirmDate == null) throw new Exception("반복작업 반려시 확정일자는 필수입력사항입니다.");

                        // 차기작업 확정일자 저장
                        schedule.IsUse = useSchedule;
                        schedule.WorkDueDate = confirmDate.Value;
                        _context.FmsWorkSchedule.Update(schedule);
                        _context.SaveChanges();

                        // 실행계획(Request) 생성
                        if (!_workRequestRepository.IsCreatedRequest(schedule)) {
                            _workRequestRepository.CreateFromWorkSchedule(schedule);
                        }

                    } else {
                        schedule.IsUse = useSchedule;
                        _context.FmsWorkSchedule.Update(schedule);
                        _context.SaveChanges();
                    }
                }

                trans.Complete();
            }
        }

        // 작업대기로 넘김
        public void Order(FmsWorkOrder data) {
            _workRequestRepository.WorkToOrder(data.SiteId, data.WorkRequestId, data.OrderUserId, data.Content, true);
        }

        public IQueryable<FmsWorkRequest> GetAll() {
            var query = _context.FmsWorkRequest;
            return query;
        }
    }
}