package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.common.BillingDriver;
import com.bocloud.cmp.model.PublicCloudBillModel;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.GridBean;
import com.bocloud.common.model.OperateResult;
import com.bocloud.common.model.Pager;
import com.bocloud.common.model.Param;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.common.utils.ListTool;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.entity.resource.CloudBill;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.interfaces.resource.CloudBillService;
import com.bocloud.ims.repository.resource.CloudBillRepository;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.jumpmind.symmetric.csv.CsvReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Future;

/**
 * 公有云账单
 *
 * @author admin
 */
@Service("cloudBillService")
public class CloudBillServiceImpl implements CloudBillService {

	private static Logger logger = LoggerFactory.getLogger(CloudBillServiceImpl.class);

	private static OperateResult.OperateCategory operateCategory = OperateResult.OperateCategory.ResourceEvent;

	private static ThreadLocal<Integer> row = new ThreadLocal<>();

	@Autowired
	private CloudBillRepository cloudBillRepository;
	@Autowired
	private CloudVendorRepository cloudVendorRepository;
	@Autowired
	private BillingDriver billingDriver;
	@Autowired
	private ThreadPoolTaskExecutor taskExecutor;
	@Autowired
	private ResourceEventPublisher resourceEventPublisher;

	@Override
	public BocloudResult<List<Map<String, String>>> listTypes(RequestContext context, Long vendorId) {
		try {
			Assert.notNull(vendorId, "参数格式错误");
			List<Map<String, String>> typeList = new ArrayList<>();
			Map<String, String> productMap = new HashMap<String, String>();
			List<CloudBill> cloudBills = cloudBillRepository.listTypes(vendorId);
			for (CloudBill cloudBill : cloudBills) {
				productMap.put(cloudBill.getProductName(), cloudBill.getProductName());
			}
			Set<Entry<String, String>> entrySet = productMap.entrySet();
			for (Entry<String, String> entry : entrySet) {
				Map<String, String> map = new HashMap<>();
				map.put("key", entry.getKey());
				map.put("value", entry.getValue());
				typeList.add(map);
			}
			return new BocloudResult<>(true, typeList, "查询资源类型成功");
		} catch (Exception e) {
			logger.error("失败 ： ", e);
			return new BocloudResult<>(false, "查询资源类型失败:" + e.getMessage());
		}
	}

	@Override
	public BocloudResult<GridBean<CloudBill>> list(Pager pager, RequestContext context) {
		int total;
		GridBean gridBean;
		try {
			Map<String, String> sorter = Optional.ofNullable(pager.getSorter()).orElse(Maps.newHashMap());
			sorter.put("feeEndTime", Common.ONE);
			total = cloudBillRepository.count(pager.getParams());
			List<CloudBill> cloudBills = cloudBillRepository.list(pager.getPage(), pager.getRows(), pager.getParams(),
					pager.getSorter());
			gridBean = GridHelper.getBean(pager.getPage(), pager.getRows(), total, cloudBills);
			return new BocloudResult<>(true, gridBean, "查询成功");
		} catch (Exception e) {
			logger.error("list CloudBill failure:", e);
			return new BocloudResult<>(false, "查询失败");
		}

	}

	@Override
	public BocloudResult synchronizeData(RequestContext context) {
		try {
			taskExecutor.submit(() -> {
				logger.info("start sync stack event scheduler ... ");
				String content = "账单数据同步成功！";
				List<String> types = Arrays.asList(new String[] { "ALIYUN", "HUAWEI" });
				String operate = types.get(0).toLowerCase() + ".cloudBill";
				List<CloudVendor> cloudVendors = cloudVendorRepository.listByTypes(types, null);
				try {
					syncCloudBills(cloudVendors, context);
				} catch (Exception e) {
					content = "账单数据同步失败：" + e.getMessage();
					logger.error("同步云账单异常：" + e.getMessage());
				}
				resourceEventPublisher.send(new OperateResult(true, content, operate,
						MapTools.simpleMap(Common.VENDORID, cloudVendors.get(0).getId()),
						OperateResult.OperateCategory.ResourceEvent, context));
				logger.info("同步账单数据任务已下达，正在执行...");
			});
			return new BocloudResult<>(true, "同步账单数据任务已下达，正在执行...");
		} catch (Exception e) {
			logger.error("同步账单数据失败：", e);
			return new BocloudResult<>(false, "同步账单数据失败：" + e.getMessage());
		}
	}

	@Override
	public BocloudResult importBill(MultipartFile file, CloudBill cloudBill, RequestContext context) {
		try {
			Long vendorId = cloudBill.getVendorId();
			CloudVendor vendor = cloudVendorRepository.query(vendorId);
			Assert.notNull(vendor, "平台验证失败");
			taskExecutor.execute(() -> {
				String operate = vendor.getType().toLowerCase() + ".bill";
				String message = "账单导入失败";
				try {
					message = "账单导入成功";
					// InputStream inputStream = file.getInputStream();
					CsvReader csvReader = new CsvReader(file.getInputStream(), Charset.forName("UTF-8"));
					csvReader.readHeaders();
					List<CloudBill> cloudBills = new ArrayList<>();
					String str = null;// 每行数据
					while (csvReader.readRecord()) {
						str = csvReader.getRawRecord();// 读取一行数据
						String[] split = str.split(",");
						CloudBill entity = new CloudBill();
						entity.setVendorId(vendor.getId());
						entity.setVendorType(vendor.getType());
						// 加入导入账单的信息
						entity.setOrderSn(split[0]);
						entity.setMonth(split[1]);
						entity.setProductName(split[2]);
						entity.setProductDetail(split[3]);
						SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
						entity.setFeeBeginTime(sdf.parse(split[4]));
						entity.setFeeEndTime(sdf.parse(split[5]));
						entity.setMoney(new BigDecimal(split[6]));
						entity.setChargeMode("PostCharge"); // 后付费
						entity.setStatus("paid"); // 已支付
						cloudBills.add(entity);
					}
					// save 到数据库
					cloudBillRepository.batchSave(cloudBills, cloudBills.size());
					resourceEventPublisher.send(new OperateResult(true, message, operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				} catch (Exception e) {
					logger.error("Azure BILL fail:", e);
					resourceEventPublisher.send(new OperateResult(false, message, operate,
							MapTools.simpleMap("vendorId", vendor.getId()), operateCategory, context));
				}
			});
			return new BocloudResult<>(true, "导入任务已下发，正在执行...");
		} catch (Exception e) {
			logger.error("import AzureCloudBill failure:", e);
			return new BocloudResult<>(false, "导入失败");
		}
	}

	private void syncCloudBills(List<CloudVendor> cloudVendors, RequestContext context) throws Exception {
		if (ListTool.isEmpty(cloudVendors)) {
			return;
		}
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM");
		String month = format.format(new Date());
		for (CloudVendor vendor : cloudVendors) {
			// 平台配置信息
			JSONObject authentication = JSONObject.parseObject(vendor.getAuthentication());
			String regions = authentication.getString("regions");
			List<String> currentRegions = JSONObject.parseArray(regions, String.class);
			if (ListTool.isEmpty(currentRegions)) {
				logger.info(vendor.getName() + " 平台 获取区域信息异常，账单同步失败！");
				continue;
			}
			logger.info("开始同步" + vendor.getName() + "账单");
			BocloudResult bocloudResult = billingDriver.billDetailList(vendor.getUuid(), month, currentRegions.get(0));
			if (bocloudResult.isSuccess()) {
				@SuppressWarnings("unchecked")
				List<PublicCloudBillModel> billModels = (List<PublicCloudBillModel>) bocloudResult.getData();
				// System.out.println(billModels);
				if (ListTool.isEmpty(billModels)) {
					continue;
				}
				syncBills(billModels, vendor, month);
			} else {
				String operate = vendor.getType().toLowerCase() + ".cloudBill";
				resourceEventPublisher.send(new OperateResult(false, vendor.getName() + "同步失败：" +bocloudResult.getMessage(), operate,
						MapTools.simpleMap(Common.VENDORID, cloudVendors.get(0).getId()),
						OperateResult.OperateCategory.ResourceEvent, context));
			}
		}

	}

	private void syncBills(List<PublicCloudBillModel> billModels, CloudVendor vendor, String month) throws Exception {
		if (ListTool.isEmpty(billModels)) {
			cloudBillRepository.deleteByVendor(vendor.getId(), null);
			return;
		}
		List<CloudBill> cloudBills = cloudBillRepository.listByVendor(vendor.getId(), null);
		Map<String, CloudBill> cloudBillMap = Maps.newHashMap();
		for (CloudBill cloudBill : cloudBills) {
			cloudBillMap.put(cloudBill.getOrderSn(), cloudBill);
		}
		for (PublicCloudBillModel model : billModels) {
			if (cloudBillMap.containsKey(model.getOrderSn())) {
				cloudBillMap.remove(model.getOrderSn());
			} else {
				CloudBill cloudBill = JSONObject.parseObject(JSONObject.toJSONString(model), CloudBill.class);
				cloudBill.setVendorId(vendor.getId());
				cloudBill.setMonth(StringUtils.isEmpty(model.getMonth()) ? month : model.getMonth());
				cloudBill.setVendorType(vendor.getType());
				cloudBill.setOrderSn(cloudBill.getOrderSn());
				cloudBill.setStatus("paid"); // 已支付
				cloudBill.setChargeMode("PostCharge"); // 后付费
				cloudBill.setSubscriptionId(model.getSubscriptionId());
				cloudBill.setConsumedService(model.getConsumedService());
				cloudBill.setUnitPrice(model.getUnitPrice());

				// 公有云账单系数
				BigDecimal originalMoney = cloudBill.getMoney();
				BigDecimal billFactor = vendor.getBillFactor();
				cloudBill.setOriginalMoney(originalMoney);
				cloudBill.setMoney(billFactor.multiply(originalMoney));
				cloudBillRepository.save(cloudBill);
			}
		}
		for (Map.Entry<String, CloudBill> entry : cloudBillMap.entrySet()) {
			cloudBillRepository.delete(entry.getValue().getId());
		}

	}

	/**
	 * 创建标题
	 *
	 * @param sheet
	 * @param style
	 */
	public void createVmReport(SXSSFSheet sheet, CellStyle style, String vendorType) {
		// 模板标题处理
		String[] titles = new String[] { "账期", "所属平台", "流水号", "资源", "资源详情", "项目名称", "开始时间", "结束时间", "账单金额(元)", "计费类型", "账单状态" };
		int number = -1;
		SXSSFRow row = sheet.createRow(0);
		for (String title : titles) {
			number++;
			row.createCell(number).setCellValue(title);
			sheet.getRow(0).getCell(number).setCellStyle(style);
			sheet.setColumnWidth(number, 30 * 256);
			sheet.addMergedRegion(new CellRangeAddress(0, 1, number, number));
		}
	}

	@Override
	public BocloudResult download(Pager pager, RequestContext context) {
		SXSSFWorkbook workbook = new SXSSFWorkbook();
		try {
			SXSSFSheet sheet = workbook.createSheet("外部账单");
			sheet.setRandomAccessWindowSize(-1);
			// 样式一，标题数据蓝色填充
			CellStyle style = workbook.createCellStyle();
			style.setFillForegroundColor(IndexedColors.ROYAL_BLUE.getIndex());
			style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
			style.setBorderBottom(BorderStyle.THIN); // 下边框
			style.setBorderLeft(BorderStyle.THIN);// 左边框
			style.setBorderTop(BorderStyle.THIN);// 上边框
			style.setBorderRight(BorderStyle.THIN);// 右边框
			style.setAlignment(HorizontalAlignment.CENTER);
			// 样式二，内容样式
			CellStyle cellStyle = workbook.createCellStyle();
			cellStyle.setBorderTop(BorderStyle.THIN);// 上边框
			cellStyle.setBorderBottom(BorderStyle.THIN); // 下边框
			cellStyle.setBorderLeft(BorderStyle.THIN);// 左边框
			cellStyle.setBorderRight(BorderStyle.THIN);// 右边框
			cellStyle.setAlignment(HorizontalAlignment.CENTER);// 水平对齐方式
			cellStyle.setVerticalAlignment(VerticalAlignment.TOP);// 垂直对齐方式
			// 标题写入
			createVmReport(sheet, style, null);
			// 读写数据
			Map<String, String> sorter = Maps.newHashMap();
			sorter.put("feeEndTime", Common.ONE);
			List<Param> pageParams = pager.getParams();
			List<CloudBill> cloudBills = cloudBillRepository.list(1, Integer.MAX_VALUE, pager.getParams(), sorter);
			Integer total = cloudBills.size();
			// 每个线程处理的数量
			int numbers = 200;
			if (total < numbers) {
				excelExporter(sheet, cellStyle, 1, new Pager(1, numbers, pageParams, sorter), context, null);
			} else {
				int pages = total / numbers;
				int lastNumers = total % numbers;
				int rowNumber = 1;
				List<Future<Boolean>> futures = new ArrayList<>();
				for (int i = 1; i <= pages; i++) {
					int page = i;
					int finalRowNumber = rowNumber;
					futures.add(taskExecutor.submit(() -> excelExporter(sheet, cellStyle, finalRowNumber,
							new Pager(page, numbers, pageParams, sorter), context, null)));
					rowNumber += numbers;
				}
				// 获取结果
				for (Future<Boolean> future : futures) {
					Boolean successful = future.get();
					if (successful) {
						// 收到写入完成提示
						continue;
					}
				}
				// 整页数据的异步查询写入完成，处理最后一页不足整页的数据
				if (0 != lastNumers) {
					excelExporter(sheet, cellStyle, pages * numbers + 1,
							new Pager(pages + 1, numbers, pageParams, sorter), context, null);
				}
			}
			String fileName = "账单数据统计";
			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
			workbook.write(byteArrayOutputStream);
			byte[] ba = byteArrayOutputStream.toByteArray();
			Map<String, Object> resultMap = Maps.newHashMap();
			resultMap.put("fileName", fileName + System.currentTimeMillis() + ".xlsx");
			resultMap.put("data", Base64.getEncoder().encodeToString(ba));
			return new BocloudResult<>(true, resultMap, "下载成功");
		} catch (Exception e) {
			logger.error("Get error message：{}", e.getMessage());
			return new BocloudResult<>(false, e.getMessage());
		} finally {
			if (null != workbook) {
				try {
					workbook.close();
				} catch (IOException e) {
					logger.error("error message：", e);
				}
			}
		}
	}

	/**
	 * excel导出
	 *
	 * @param sheet
	 * @param cellStyle
	 * @param rowNumber
	 * @param pager
	 * @param context
	 * @return
	 */
	public Boolean excelExporter(SXSSFSheet sheet, CellStyle cellStyle, int rowNumber, Pager pager,
			RequestContext context, String vendorType) {
		int number = rowNumber;
		try {
			BocloudResult result = this.list(pager, context);
			if (result.isFailed()) {
				logger.error("获取账单数据失败", result.getMessage());
				return false;
			}
			String projectName;
			List<CloudBill> cloudBills = JSONObject.parseObject(JSON.toJSONString(result.getData()))
					.getJSONArray("rows").toJavaList(CloudBill.class);
			SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			int rowNum;
			for (CloudBill cloudBill : cloudBills) {
				row.set(number += 1);
				// 创建数据初始化行
				rowNum = row.get();
				SXSSFRow sheetRow = sheet.createRow(rowNum);
				// "账期", "所属平台", "流水号", "资源", "资源详情", "项目名称", "开始时间", "结束时间",
				// "账单金额(元)", "计费类型", "账单状态"
				sheetRow.createCell(0).setCellValue(cloudBill.getMonth());
				sheet.getRow(rowNum).getCell(0).setCellStyle(cellStyle);
				sheetRow.createCell(1).setCellValue(cloudBill.getVendorName());
				sheet.getRow(rowNum).getCell(1).setCellStyle(cellStyle);
				sheetRow.createCell(2).setCellValue(cloudBill.getOrderSn());
				sheet.getRow(rowNum).getCell(2).setCellStyle(cellStyle);
				sheetRow.createCell(3).setCellValue(cloudBill.getProductName());
				sheet.getRow(rowNum).getCell(3).setCellStyle(cellStyle);
				sheetRow.createCell(4).setCellValue(cloudBill.getProductDetail());
				sheet.getRow(rowNum).getCell(4).setCellStyle(cellStyle);
				// 项目名称 sxp:2022/12/13
				projectName = cloudBill.getProjectName();
				if (projectName == null) {
					projectName = "";
				}
				sheetRow.createCell(5).setCellValue(projectName);
				sheet.getRow(rowNum).getCell(5).setCellStyle(cellStyle);

				sheetRow.createCell(6).setCellValue(null == cloudBill.getFeeBeginTime()?"":simpleDateFormat.format(cloudBill.getFeeBeginTime()));
				sheet.getRow(rowNum).getCell(6).setCellStyle(cellStyle);
				sheetRow.createCell(7).setCellValue(null == cloudBill.getFeeEndTime()?"":simpleDateFormat.format(cloudBill.getFeeEndTime()));
				sheet.getRow(rowNum).getCell(7).setCellStyle(cellStyle);
				sheetRow.createCell(8).setCellValue(cloudBill.getMoney().doubleValue());
				sheet.getRow(rowNum).getCell(8).setCellStyle(cellStyle);
				String chargeMode = cloudBill.getChargeMode();
				if ("PostCharge".equalsIgnoreCase(chargeMode)) {
					chargeMode = "后付费";
				} else if ("PreCharge".equalsIgnoreCase(chargeMode)){
					chargeMode = "预付费";
				}else {
					chargeMode = "未知";
				}
				sheetRow.createCell(9).setCellValue(chargeMode);
				sheet.getRow(rowNum).getCell(9).setCellStyle(cellStyle);
				String status = cloudBill.getStatus();
				if ("paid".equalsIgnoreCase(status)) {
					status = "已支付";
				} else {
					status = "未知";
				}
				sheetRow.createCell(10).setCellValue(status);
				sheet.getRow(rowNum).getCell(10).setCellStyle(cellStyle);
			}
			return true;
		} catch (Exception e) {
			logger.error("账单数据【" + row.get() + "行至" + (row.get() + 20) + "行】写入失败", e);
			return false;
		}
	}
}