package cokr.xit.fims.cmmn;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import cokr.xit.applib.Hangul;
import cokr.xit.base.file.ZIP;
import cokr.xit.foundation.Downloadable;
import cokr.xit.foundation.component.AbstractBean;
import cokr.xit.foundation.data.DataFormat;
import cokr.xit.foundation.data.DataObject;

public class OutsourcingFileMaker extends AbstractBean {
	protected String printKorName;
	protected FimsPrintOption printOption;
	protected List<DataObject> data;
	protected List<DataObject> printStngItems;

	protected String resultZipFilePath;

	public OutsourcingFileMaker(String printKorName, FimsPrintOption printOption, List<DataObject> printStngItems, List<DataObject> data){
		this.printKorName = printKorName;
		this.printOption = printOption;
		this.data = data;
		this.printStngItems = printStngItems;
	}

	/**
	 * 서식에서 사용하는 출력항목 중 사용하는 항목만 추출한다.
	 */
	public List<PredefinedSet> filterUsedArtcls() {
		List<String> otptArtclNmList = printStngItems.stream()
			.map((item) -> item.string("ARTCL_NM"))
			.toList();

		return OutsourcingFileMaker.predifinedSetForOutsourcing().stream()
			.filter((item) -> otptArtclNmList.contains(item.getArtclNm()))
			.toList();
	}

	/**
	 * 다운로드 이력에 남길 항목명을 추출한다.
	 */
	public List<String> filterDownloadDataNames() {
		return filterUsedArtcls().stream()
			.filter((item) -> item.isDownloadData())
			.map(PredefinedSet::getArtclDscrp)
			.toList();
	}

	public Map<String, Object> makeFile() {
		String currentTime = dateFormats.now();
		String year = currentTime.substring(0, 4);
		String month = currentTime.substring(4, 6);
		String day = currentTime.substring(6, 8);
		String yyyymmdd = year+month+day;

		String outsourcingResultPath = "files"+File.separator+"temp"+File.separator+"outsourcing"
			+ File.separator + year + File.separator + month + File.separator + day
			+ File.separator + currentTime;
		ensureDir(outsourcingResultPath);

		try {
			String imagesFolderName = yyyymmdd + printKorName;
			String metaFileName = imagesFolderName + "(외주파일).txt";

			String imagesFolderPath = outsourcingResultPath + File.separator + imagesFolderName;
			String metaFilePath = outsourcingResultPath + File.separator + metaFileName;

			File imagesFolderPathFile = ensureDir(imagesFolderPath);

			for (DataObject dataObject: data) {
				CmmnUtil.setOcrInfo(dataObject);
				CmmnUtil.setBarcode(dataObject);
				String sndngDtlId = dataObject.string("SNDNG_DTL_ID");
				int crdnPhotoCnt = toInt(dataObject.get("CRDN_PHOTO_CNT"));

				for (int j = 0; j < crdnPhotoCnt; j++){
					String orgnFilePath = dataObject.string("CRDN_PHOTO"+(j+1));
					String fileKey = dataObject.string("CRDN_PHOTO"+(j+1)+"KEY");
					if (isEmpty(orgnFilePath)) continue;

					if (!orgnFilePath.startsWith("files")) {
						orgnFilePath = CmmnUtil.copyStaticResource(orgnFilePath, false);
					}

					File orgnFile = new File(orgnFilePath);
					if (!orgnFile.exists() || !orgnFile.canRead()) continue;

					InputStream orgnFileInputStream = new FileInputStream(orgnFile);

					String modifiedFileName = "P"+sndngDtlId+CmmnUtil.indexToAlphabet(j, true, true)+"."+"JPG";
					String modifiedFilePath = imagesFolderPath + File.separator + modifiedFileName;
					File modifiedFile = new File(modifiedFilePath);

					if (dataObject.get("MOS"+fileKey) != null) {
						CmmnUtil.createMaskedImage(orgnFileInputStream, modifiedFile, CmmnUtil.getDataObjectListFromMap(dataObject,"MOS"+fileKey));
					} else {
						Files.copy(orgnFileInputStream, modifiedFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
					}
					dataObject.set("CRDN_PHOTO"+(j+1), modifiedFilePath);
				}

			}

			File metaFile = new File(metaFilePath);
			if (!metaFile.exists()) {
				if (!metaFile.createNewFile())
					throw new RuntimeException("외주파일생성 중 오류가 발생하였습니다.");
			}
			FileWriter fw = new FileWriter(metaFile);
			BufferedWriter writer = new BufferedWriter(fw);

			Hangul hangul = new Hangul(2);
			for (int i = 0; i < data.size(); i++) {
				DataObject dataObject = data.get(i);
				if (i != 0)
					writer.write("\n");

				String line = "";
				for (DataObject printStngItem : printStngItems){
					String artclNm = printStngItem.string("ARTCL_NM");
					String artclValue = switch (artclNm) {
					case "OTPT_YMD" -> yyyymmdd;
					case "OTPT_YEAR" -> year;
					case "OTPT_MONTH" -> month;
					case "OTPT_DAY" -> day;
					default -> dataObject.string(artclNm);
					};

					//포매팅
					if (!isEmpty(printStngItem.string("FORMATTING"))) {
						String formatting = printStngItem.string("FORMATTING");
						switch (formatting) {
						case "zip(hypen)": {
							artclValue = CmmnUtil.zipCodeFormat(artclValue);
							break;
						}
						case "rgNo(hypen)": {
							artclValue = CmmnUtil.rgNoFormat(artclValue);
							break;
						}
						case "amount(comma)": {
							artclValue = DataFormat.n_nn0(artclValue);
							break;
						}
						default:
							if (formatting.startsWith("date(")) {
								String dtlFormatting = formatting.substring(5, formatting.length()-1);

								if (dtlFormatting.contains("~") && artclValue.contains("~")) {
									String from = CmmnUtil.dateNFormat(artclValue.split("~",-1)[0], dtlFormatting.split("~",-1)[0]);
									String to = CmmnUtil.dateNFormat(artclValue.split("~",-1)[1], dtlFormatting.split("~",-1)[1]);

									artclValue = from + "~" + to;
								} else {
									artclValue = CmmnUtil.dateNFormat(artclValue, dtlFormatting);
								}
							}
						}

					}

					//공백 패딩 방향
					int byteCnt = toInt(printStngItem.get("ARTCL_SZ"));
					if (printStngItem.string("PADDING_DIRECTION").equals("left")) {
						line += hangul.lpadByte(artclValue, byteCnt, " ");
					} else {
						line += hangul.rpadByte(artclValue, byteCnt, " ");
					}

				}
				writer.write(line);
			}

			writer.close();

			String zipFileName = yyyymmdd + printKorName + ".zip";
			String zipFilePath = outsourcingResultPath + File.separator + zipFileName;

			new ZIP().compress(zipFilePath, metaFilePath, imagesFolderPath);

			metaFile.delete();
			File[] images = imagesFolderPathFile.listFiles();
			for (File image : images) {
				image.delete();
			}
			imagesFolderPathFile.delete();

			resultZipFilePath = zipFilePath;
			return Map.of(
				"download", getDownloadable().setFilename(zipFileName),
				"downloadData", data,
				"dataNames", this.filterDownloadDataNames()
			);
		} catch(FileNotFoundException e) {
			throw new RuntimeException("파일을 찾을 수 없습니다.");
		} catch (IOException e) {
			throw new RuntimeException("외주파일생성 중 오류가 발생하였습니다.");
		}
	}


	public void write(OutputStream out) {
		try (FileInputStream fis = new FileInputStream(resultZipFilePath);) {
			fis.transferTo(out);
			new File(resultZipFilePath).delete();
		} catch (Exception e) {
			throw runtimeException(e);
		}
	}

	public Downloadable getDownloadable() {
		return new Downloadable()
			.setContentType(ZIP.MIME_TYPE)
			.setWriter(this::write);
	}

	public static List<PredefinedSet> predifinedSetForOutsourcing() {
		return Arrays.asList(
			new PredefinedSet("SNDNG_DTL_ID", "발송상세아이디").offLog(),
			new PredefinedSet("VLTN_LAW_NM", "위반법명"),
			new PredefinedSet("BARCODE", "바코드").offLog(),
			new PredefinedSet("CRDN_DT", "단속일시"),
			new PredefinedSet("DUDT_YMD", "납부기한"),
			new PredefinedSet("VHRNO", "차량번호"),
			new PredefinedSet("FFNLG_CARMDL_NM", "과태료차종명"),
			new PredefinedSet("VHCL_NM", "차량명"),
			new PredefinedSet("CRDN_PLC", "단속장소"),
			new PredefinedSet("RTPYR_ADDR", "납부자기본주소"),
			new PredefinedSet("RTPYR_DTL_ADDR", "납부자상세주소"),
			new PredefinedSet("RTPYR_ZIP", "납부자우편번호"),
			new PredefinedSet("RTPYR_NM", "납부자명"),
			new PredefinedSet("OPNN_RANGE", "의견진술기간"),
			new PredefinedSet("CRDN_SE_NM", "단속구분명"),
			new PredefinedSet("ATCH_FILE_CNT", "첨부파일갯수"),
			new PredefinedSet("FFNLG_CRDN_AMT", "과태료단속금액"),
			new PredefinedSet("LAST_AMT", "최종금액"),
			new PredefinedSet("MINUS_AMT", "감경금액"),
			new PredefinedSet("RG_NO", "등기번호"),
			new PredefinedSet("OCR0", "OCR0").offLog(),
			new PredefinedSet("OCR0_1", "OCR0_1").offLog(),
			new PredefinedSet("OCR0_2", "OCR0_2").offLog(),
			new PredefinedSet("OCR_BAND1", "OCR_BAND1").offLog(),
			new PredefinedSet("OCR_BAND2", "OCR_BAND2").offLog(),
			new PredefinedSet("BANK_NM", "가상계좌은행명"),
			new PredefinedSet("VR_ACTNO", "가상계좌"),
			new PredefinedSet("EPAYNO", "전자납부번호"),
			new PredefinedSet("EMPTY", "공백").offLog()
		);
	}
}