global class CreatePassiveTaskBatch1 implements Database.Batchable<sObject>, Database.Stateful  {
	// 报错的询价ID
	public String logstr = '';
	// 报错信息
	public String errorstr = '';
	public List<String> emailMessages = new List<String>();
	public Integer AllCount = 0; // 总件数
	public Integer failedCount = 0; // 失败件数
	Integer FIELDMAX = 30;
	Boolean isAutoexec = false; //20210203 zh SWAG-BXQDHZ 是否自动执行
	String HPId;
	String dePart;
	//针对于单独某个医院
	global CreatePassiveTaskBatch1(String AccountId) {
		HPId = AccountId;
		isAutoexec = false;
	}
	//针对于单独某个战略科室
	global CreatePassiveTaskBatch1(String AccountId, boolean a) {
		dePart = AccountId;
		isAutoexec = false;
	}
	global CreatePassiveTaskBatch1() {
	}
	global CreatePassiveTaskBatch1(boolean temp) {
		isAutoexec = temp;
	}
	// opd计划月api与opd计划数的名字
	SS_Batch_Column_Mapping__c PlanMonthAndAmountName = SS_Batch_Column_Mapping__c.getValues('PlanMonthAndAmountName');
	Map<String , List<String>> PlanMonthAndAmountNameMap = new Map<String , List<String>>();
	// opd计划月api与opd计划数的api
	SS_Batch_Column_Mapping__c PlanMonthAndAmount = SS_Batch_Column_Mapping__c.getValues('PlanMonthAndAmount');
	Map<String , String> PlanMonthAndAmountMap = new Map<String , String>();

	global Database.QueryLocator start(Database.BatchableContext BC) {
		// 规定年份格式（今年四月份到下一年4月份为一年）（如：今年-1867+p 135p）
		// 这个月
		Date TodayMonth = Date.Today();
		// 下个月
		Date NextMonth = TodayMonth.addMonths(1);
		// 将其转换为integer类型
		Integer year = NextMonth.year();
		Integer month = NextMonth.month();
		// 今年四月份到下一年4月份为一年
		if (month < 4) {
			year = year - 1;
		}
		// 今年-1867+p 135p
		String ShangYear = year - 1867 + 'P';

		// 获取自定义设置opd计划月api与opd计划数的名字
		// 循环获取
		for (Integer i = 0; i <= FIELDMAX; i++ ) {
			// 拼字段
			String spil = ('00' + 'i').right(3);
			// 获取opd计划月api
			String planMonthApi = String.valueOf(PlanMonthAndAmountName.get('From_Column_' + spil + '__c')) ;
			// 获取opd计划数的名字
			String planNumberName = String.valueOf(PlanMonthAndAmountName.get('SS_Column_' + spil + '__c'));
			// 判断以上不为空
			if (String.isNotBlank(planMonthApi) && String.isNotBlank(planNumberName)) {
				// 计划数的值（名字）将其用逗号做分割符，存在
				List<String> PlanNumberNameList = planNumberName.split(',');
				// 将opd计划月api作为key，opd计划数的名字作为value，存入Map中
				PlanMonthAndAmountNameMap.put(planMonthApi, PlanNumberNameList);
			}
		}
		// 获取自定义设置opd计划月api与opd计划数的api
		for (Integer i = 0; i <= FIELDMAX ; i++) {
			String spil = ('00' + 'i').right(3);
			// 获取opd计划月api
			String planMonthApi = String.valueOf(PlanMonthAndAmountName.get('From_Column_' + spil + '__c')) ;
			// 获取opd计划数的Api
			String planNumberApi = String.valueOf(PlanMonthAndAmountName.get('SS_Column_' + spil + '__c'));
			if (String.isNotBlank(planMonthApi) && String.isNotBlank(planNumberApi)) {
				// 将opd计划月api作为key，opd计划数的api作为value，存入Map中
				PlanMonthAndAmountMap.put(planMonthApi, planNumberApi);
			}
		}
		String planMonth;
		String planNumberName;
		String planMonthStr;
		// 遍历计划月api，用来获取opd计划月和opd计划数及计划月不为空的条件(拼接opd计划月api)
		for (String apiStr : PlanMonthAndAmountNameMap.keySet()) {
			// 拼opd计划月api
			planMonth = ',' + apiStr;
			// 拼opd计划数名字
			planNumberName = ',' + PlanMonthAndAmountNameMap.get(apiStr);
			// 不为空计划月不为空的拼接
			planMonthStr = apiStr + '!=null or ';
		}
		// 计划月不为空的条件
		String planMonthNu = planMonthStr.substring(0, planMonthStr.length() - 3);
		// 检索今年全部的目标客户
		// 检索 计划月不为空，OCSM期 = 规定年份 的目标客户
		String Acc = 'select Id ,name,account__c,account__r.OwnerId,account__r.Name,account__r.ParentId';
		Acc += planMonth;
		Acc += planNumberName;
		Acc += 'From Account_Number_of_target__c where OCM_Period__c = : ThisPeriod and';
		Acc += '(';
		Acc += 'planMonthNu';
		Acc += ')';
		// 针对于单独某个医院(目标客户的母公司 = 医院)
		if (String.isNotBlank(HPId)) {
			Acc += 'and account__r.ParentId =: HPId';
		}
		// 针对于单独某个战略科室（战略科室=战略科室）
		if (String.isNotBlank(dePart)) {
			Acc += ' and account__c = : dePart';
		}
		// 额外判断，如果不是非要执行，那么如果战略科室所在医院是无效医院，那么就不针对这家战略科室下达任务
		if (String.isBlank(dePart) && String.isBlank(HPId)) {
			Acc += 'and account__r.Parent.Is_Active__c = \'有効\'  ';
		}
		// 除15号（从自定义标签中获取）外 并且 不自动执行时 检索到的目标客户为空
		Integer runday = Integer.valueOf(Label.OPD_RunTime);
		if (Date.today().day() != runday && isAutoexec == true) {
			Acc = 'select Id From Account_Number_of_target__c where Name=\'\' and Name!=\'\'';
		}
		// 初始化，得到想要处理的数据（目标客户）
		return DataBase.getQueryLocator(Acc);
	}

	global void execute(Database.BatchableContext BC, List<Account_Number_of_target__c> AccList) {
		// opd计划月api与对应的产品名
		//任务List
		List<task__c> taskList =  new List<task__c>();
		SS_Batch_Column_Mapping__c OPDPlanMonth = SS_Batch_Column_Mapping__c.getValues('OPD_PlanMonth');
		Map<String , String> OPDPlanMonthMap = new Map<String , String>();
		//获取自定义设置opd计划月api与对应的产品名，将其存入Map中
		for (Integer i = 0 ; i <= FIELDMAX ; i++) {
			String spil = ('00' + 'i').right(3);
			String planMonth = String.valueOf(OPDPlanMonth.get('From_Column_' + spil + '__c'));
			String chanPin = String.valueOf(OPDPlanMonth.get('SS_Column_' + spil + '__c'));
			if (String.isNotBlank(planMonth) && String.isNotBlank(chanPin)) {
				OPDPlanMonthMap.put(planMonth, chanPin);
			}
		}
		// 遍历目标客户   将战略科室转换成字符串（取其前15），存入List中
		List<String> anotAccList = new List<String>();
		for (Account_Number_of_target__c anot : AccList) {
			anotAccList.add(String.valueOf(anot.Account__c).substring(0, 15));
		}
		// 定义Fy开始日和结束日（作为检索opd计划的条件）
		Integer monthday = date.today().month();
		Integer yearday = date.today().year();
		if (monthday < 3) {
			yearday--;
		}
		Date Fyfirstday = date.newInstance(yearday, 4, 1);
		Date FyEndday = Fyfirstday.addMonths(12);
		// 状态为计划中或完毕未报告 并且 OPD计划实施日期大于等于Fy开始日 并且 小于 结束日 并且 每个战略科室对应的opd计划（opd计划所关联的战略科室被包含在所有战略科室中）
		List<OPDPlan__c> OPDPlanList = [select id, PlanProdDetail__c, OCM_category_Name__c,
		                                OCM_category_ID__c, OPD_Customers_Target__c,
		                                OPDPlan_ImplementDate__c from OPDPlan__c
		                                where OCM_category_ID__c in:anotAccList
		                                and OPDPlan_ImplementDate__c >= : FYfirstDay
		                                and OPDPlan_ImplementDate__c < : FyEndday
		                                and (Status__c = '计划中'  or Status__c = '完毕未报告')];
		//3.战略客户下全部的Opd计划
		Map<String, List<OPDPlan__c>> OpdMap = new Map<String, List<OPDPlan__c>>();
		// 1、创建一个存opd计划Id的list
		List<String> opdPlanIdList = new List<String>();
		// 2、遍历存战略科室Id的list
		for (String anotAcc : anotAccList) {
			// 3、遍历检索出来的opd计划
			for (OPDPlan__c opdId : OPDPlanList) {
				// 4、将 战略科室（key）检索出来的opd计划 （value）
				OpdMap.put(anotAcc, OPDPlanList);
				// 5、将 opd计划存入list（String）
				opdPlanIdList.add(opdId.Id);
			}

		}
		String producDetail;
		// 遍历目标客户
		for (Account_Number_of_target__c anot : AccList) {
			//遍历opd计划月Api，opd计划月Api（key） 产品名（value）
			for (String monthapikey : OPDPlanMonthMap.keySet()) {
				//在这里写对应的opd数是否为空
				Boolean anotFlage = true;
				boolean FlagBl = true;
				//获取客户opd月值，如果客户opd计划月值为空的话，跳出循环
				if (anot.get(monthapikey) == null) {
					continue;
				}
				//opd计划月的值（将其转换成String类型）
				String monthValue = String.valueOf(anot.get(monthapikey));
				// 如果 opd计划月的值 不等于空 将 opd计划月的值赋值给(Integer类型的变量)
				Integer monthValueIn;
				if (String.isNotBlank(monthValue)) {
					monthValueIn = Integer.valueOf(monthValue.substring(0, monthValue.length() - 1));
				}
				// 如果 opd计划月的值为空 或 opd计划月大于下个月（当前月的值+1）
				if (monthValueIn < 4) {
					monthValueIn += 12;
				}
				Integer NextMonthIn  = date.today().month() - 1;
				if (NextMonthIn < 4) {
					NextMonthIn += 12;
				}
				if (String.isBlank(monthValue) || monthValueIn > NextMonthIn) {
					// 则跳出循环
					continue;
					// 如果以上条件不满足：
				} else {
					// 获取opd计划数api，将其以‘，’为分隔符存入list中
					String opdPlanNumber = PlanMonthAndAmountMap.get(monthapikey);
					List<String> planNumberValue = opdPlanNumber.split(',');
					// 遍历 opd计划数api
					for (String planNumberApi : planNumberValue) {
						FlagBl = true;
						// 1、判断对应的opd数是否为空 如果
						// 1、不为空
						if (String.isNotBlank(String.valueOf(anot.get(planNumberApi)))) {
							// 1、boolean变量为false
							anotFlage = false;
							//2、为空
						} else {
							// 1、获取 opd计划存在List中 （如果没有，则新创一个opd）
							List<OPDPlan__c> OpPlanList = OpdMap.get(String.valueOf(anot.Account__c).substring(0, 15));
							if (OpPlanList.size() < 0) {
								OpPlanList = new List<OPDPlan__c>();
							}
							// 2、循环比对已存在的计划出借备品信息和理论上存在的计划出借备品信息
							for (OPDPlan__c bp : OpPlanList) {
								// 1、如果 有计划出借备品信息 则 将计划出借备品信息以‘；’为分隔符存入list（String）中
								if (String.isNotBlank(bp.PlanProdDetail__c)) {
									List<String> bpList = bp.PlanProdDetail__c.split(';');
									// 1、遍历计划出借备品信息
									for (String OPDbp : bpList) {
										//1、得到每一个备品
										//2、得到每一个计划数名字（通过map获取全部的计划数名字，再将其遍历获取单个计划书名字，
										// 	之后将其备品与计划数名字进行对比，如果相等（表示有对应的opd计划）则跳出循环）
										if (OPDbp.equals(planNumberApi)) {
											anotFlage = false;
											FlagBl = false;
										}
									}
								}
							}
							if (FlagBl = false) {
								continue;
							}
						}
					}
					if (anotFlage = false) {
						continue;
					}
					// 将产品名： opd计划月的值 赋值给 产品明细变量上
					//产品明细
					producDetail = OPDPlanMonthMap.get(monthapikey) ;
				}
			}
			// 如果产品明细不为空 则任务的
			if (String.isNotBlank(producDetail)) {
				String anotName = anot.Account__r.name;
				String anotdepart = anot.Account__c;
				task__c newTask = new task__c();
				// 被分配者-战略科室的所有人
				newTask.assignee__c = anot.Account__r.ownerid;
				// 所有人-所有人
				newTask.ownerid = anot.Account__r.ownerid;
				//     产品明细-产品明细
				newTask.ProductDetails__c = producDetail;
				//     记录类型-OPD
				newTask.RecordTypeId =
				    Schema.SObjectType.task__c.getRecordTypeInfosByDeveloperName().get('OPD').getRecordTypeId();
				//     任务区分-‘被动任务’
				newTask.taskDifferent__c = '被动任务';
				//     任务名称-‘用户拜访：’ + 战略科室名
				newTask.Name = '用户拜访:' + anotName;
				//     客户-战略科室
				newTask.account__c = anotdepart;
				//newTask.OpportunityId__c = opdp.Id;
				//     任务状态2-‘01 分配’
				newTask.taskStatus__c = '01 分配';
				//     将任务添加到list（task__c）
				taskList.add(newTask);
			}
		}
		// 如果list中有任务 则
		if (taskList.size() > 0) {
			// 将该任务添加到数据库中
			Database.SaveResult[] SaveResult = Database.insert(taskList, false);
			// 计数，添加了几条
			AllCount = SaveResult.size();
			//循环，任务的条数
			for (Integer i = 0 ; i < SaveResult.size() ; i++) {
				// 如果没添加成功，将‘被动任务’：该任务 存到日志中，将第几条的报错信息存到错误日志中，计数总共没添加成功几条
				if (! SaveResult.get(i).isSuccess()) {
					logstr += taskList.get(i).account__c + ',';
					errorstr += SaveResult.get(i).getErrors();
					failedCount++;
				}
			}
		}
	}
	// 创建日志（用来存添加失败的任务，以及每一条添加失败的信息）
	global void finish(DataBase.BatchableContext BC) {
		BatchIF_Log__c batchIfLog = new BatchIF_Log__c();
		batchIfLog.Type__c = 'CreateTask';
		if (logstr.length() > 60000) {
			logstr = logstr.substring(0, 60000);
		}
		batchIfLog.Log__c = logstr;
		if (errorstr.length() > 60000) {
			batchIfLog.ErrorLog__c = errorstr.substring(0, 60000);
		} else {
			batchIfLog.ErrorLog__c = errorstr.substring(0, errorstr.length());
		}
		insert batchIfLog;
		emailMessages.add('失败日志：' + batchIfLog.Id);
		sendemail();
	}
	// 发送提醒邮件
	global void sendemail() {
		// 1、如果战略科室为空 并且 医院为空 则
		if (isAutoexec) {
			PretechBatchEmailUtil db = new PretechBatchEmailUtil();
			// 定义两个数组（一个为空，一个存接收邮件者，一个存抄送人）及一个标题
			String[] Receiver = new String[] {UserInfo.getUserName()};
			List<String> CCto = new List<String>();
			String title = '创建失败任务';
			//如果邮件信息有值 并且 失败总数大于0 则
			if (System.Test.isRunningTest()) {
				db.successMail('', 1);
			}
			if (emailMessages.size() > 0 && failedCount > 0) {
				//  发送失败邮件的内容为：接收人，抄送人，标题，将全部的错误日志显示出来，总数，成功数，失败数
				db.failedMail(Receiver, CCto, title,
				              String.join(emailMessages, '/n'),
				              AllCount, AllCount - failedCount, failedCount, '', true);
				//  如果!Test.isRunningTest，则发送
				if (!Test.isRunningTest()) {
					db.send();
				}
			}
		}
	}
}