package com.tradevan.gateway.client.task.upload.upCast;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Calendar;

import org.apache.commons.validator.GenericValidator;

import com.tradevan.commons.io.FileUtil;
import com.tradevan.gateway.client.dao.bean.FromConfig;
import com.tradevan.gateway.client.dao.bean.TurnkeyMessageLog;
import com.tradevan.gateway.client.dao.bean.TurnkeyMessageLogDetail;
import com.tradevan.gateway.client.dao.service.FromConfigService;
import com.tradevan.gateway.client.dao.service.TurnkeyMessageLogService;
import com.tradevan.gateway.client.einv.parse.ParserException;
import com.tradevan.gateway.client.einv.transform.TransformException;
import com.tradevan.gateway.client.einv.transport.CategoryType;
import com.tradevan.gateway.client.einv.transport.ProcessType;
import com.tradevan.gateway.client.einv.util.EncodingType;
import com.tradevan.gateway.client.einv.util.InvoiceConstant;
import com.tradevan.gateway.client.einv.validate.proc.ValidateConstant;
import com.tradevan.gateway.client.einv.validate.proc.ValidateResult;
import com.tradevan.gateway.client.einv.validate.proc.XMLVerifier;
import com.tradevan.gateway.client.exception.TurnkeyException;
import com.tradevan.gateway.client.task.info.CastInfo;
import com.tradevan.gateway.client.task.upload.UpCastProcessor;
import com.tradevan.gateway.client.util.GatewayUtil;
import com.tradevan.gateway.client.util.MessageIdentifier;
import com.tradevan.gateway.client.util.TaskEnum;
import com.tradevan.gateway.client.util.TurnkeyConstant;
import com.tradevan.gateway.client.util.TurnkeyUtil;
import com.tradevan.gateway.einv.msg.EINVPayload;
import com.tradevan.taurus.xdao.XdaoException;

public class XMLFileUpCastProcessor extends UpCaster {

    public void processXML(String seqNo, File invoiceFile, CastInfo info) throws TurnkeyException, XdaoException, IOException {

        String methodName = "processXML";

        String payload = "";
        // 一次讀取筆數
        int recordCnt = 0;
        MessageIdentifier messageIdentifier = new MessageIdentifier();
        Class<? extends EINVPayload> clazz = info.getInvoiceClass();
//        String seqNo = "";
        String subSeqNo = "";
        String partyId = "";

        
//        seqNo = TurnkeySequenceService.INSTANCE.getNextSequence();

        Calendar calender = Calendar.getInstance();
        String splitedFolder = GatewayUtil.getDate() + File.separator
                + String.valueOf(calender.get(Calendar.HOUR_OF_DAY));

        // loop 直至讀取完畢

        // 讀取檔案
        EINVPayload einvPayload = null;
        try {
            einvPayload = null;
            payload = FileUtil.readFileToString(invoiceFile, EncodingType.UTF8.getValue());
            // 取得每筆訊息編碼後的subSeqNo
            subSeqNo = messageIdentifier.getMessageSubId(recordCnt + 1);

            if (GenericValidator.isBlankOrNull(payload)) {
                logger.showConsoleError(UpCastProcessor.TASK, TurnkeyUtil.getMessage("UTL_M0038") + " " + invoiceFile, methodName);
                throw new TurnkeyException("file is empty");
            }

            TurnkeyMessageLog msgLog = new TurnkeyMessageLog();
            msgLog.setCategoryType(info.getCategoryType());
            msgLog.setSeqno(seqNo);
            msgLog.setSubseqno(subSeqNo);
            msgLog.setMessageType(info.getMessageType());
            msgLog.setProcessType(info.getProcessType().getValue());
            msgLog.setMessageDts(GatewayUtil.getDateTime());
            msgLog.setStatus(TurnkeyConstant.STATUS_PROCESS);
            msgLog.setInOutBound(TurnkeyConstant.OUTBOUND);
            msgLog.setCharacterCount(String.valueOf(payload.length()));
            TurnkeyMessageLogService.INSTANCE.insUpdMessageLog(msgLog); // 先寫

            TurnkeyMessageLogDetail msgLogDetail = new TurnkeyMessageLogDetail();
            msgLogDetail.setSeqno(seqNo);
            msgLogDetail.setSubseqno(subSeqNo);
            msgLogDetail.setProcessDts(msgLog.getMessageDts());
            msgLogDetail.setTask(TaskEnum.UpCast.getValue());
            msgLogDetail.setStatus(TurnkeyConstant.STATUS_PROCESS);
            msgLogDetail.setFilename(invoiceFile.getAbsolutePath()); // 先寫目前檔名
            TurnkeyMessageLogService.INSTANCE.updMessageLogDetail(msgLogDetail);

            // 如果是發票才對訊息進行序列化，若是訊息檔(E0401,E0402)則直接驗
            String xml = null;
            ValidateResult vr =null ;
            String key = "";
            String fromBan = "";
                    
            //2013.6.26 新增B2P訊息處理(不做unmarshal)
            if(CategoryType.B2P.equals(info.getCategoryType())){                
                //xml由檔案直接讀成字串
                xml = payload ;                
                //直接驗證
                vr = XMLVerifier.validate(payload.getBytes(), clazz);
                   
            }else {  //非B2P訊息處理
                einvPayload = (EINVPayload) parserHelper.unmarshalFromXML(payload, clazz);
                einvPayload = chkVersion(einvPayload, info.getCategoryType()==CategoryType.B2C);
                
                parserHelper.setXMLPaddingEncoding(false);
                xml = parserHelper.marshalToXML(einvPayload);
                vr = validateHelper.validateXML(xml, einvPayload, einvPayload.getClass());
                fromBan = einvPayload.getFromBan();
                if(GenericValidator.isBlankOrNull(einvPayload.getFromBan())){ 
                    logger.showConsoleError(UpCastProcessor.TASK, TurnkeyUtil.getMessage("UTL_M0056") + " " + invoiceFile, vr.getException(), methodName);
                    throw new TurnkeyException("valid fail! can not decide from ban or to ban");
                }
            }
                       
            //驗證錯誤就直接拋出錯誤
            if (!vr.getErrorCode().equals(ValidateConstant.SUCESS[0])) {
                logger.showConsoleError(UpCastProcessor.TASK, TurnkeyUtil.getMessage("UTL_M0039") + " " + invoiceFile, vr.getException(), methodName);
                logger.error("valid fail! " + vr.getErrorCode(), vr.getException(), methodName);
                throw new TurnkeyException("valid fail", vr.getException());
            }
           
            //查詢送方資訊
            FromConfig fromCfg = FromConfigService.INSTANCE.queryByPKey(fromBan);
                    
//            if(info.getProcessType()==ProcessType.EXCHANGE){// 交換時，確認FromPartyId是否存在及內容是否合法
//                if(!TurnkeyUtil.chkFromConfig(fromCfg)){
//                    logger.showConsoleError(UpCastProcessor.TASK, TurnkeyUtil.getMessage("CanNotFindSenderPartyId")+ TurnkeyUtil.getMessage("UTL_M0025") +fromCfg, methodName);
//                    fromCfg=null;
//                    throw new TurnkeyException("From PartyId("+fromBan+") is no found or FromConfig is invaild");
//                }
//            }else{
            //如果送方不存在->檢查代理人->沒代理人就拋錯
                if(!TurnkeyUtil.chkFromConfig(fromCfg)){  //先檢查是否有設定
                    if(!TurnkeyUtil.chkFromConfig(agentFromCfg)){ //沒設定的話，檢查是否有代理人
                        logger.showConsoleError(UpCastProcessor.TASK, TurnkeyUtil.getMessage("CanNotFindSenderPartyId")+ TurnkeyUtil.getMessage("UTL_M0025") +fromCfg, methodName);
                        fromCfg=null;
                        throw new TurnkeyException("From PartyId("+fromBan+") is no found or FromConfig is invaild");
                    }else{
                        fromCfg = agentFromCfg;
                        fromBan = fromCfg.getPartyId();
                    }
                }
//            }
            
            //Store時, key=送方_00000000
            //Message時, key=送方_PLATFORM
            String toBan = "";
            
            //2013.6.19 新增MiG3.0.3訊息類別處理
            if(ProcessType.MESSAGE.equals(info.getProcessType())){
                //key: E0401_送方_PLATFORM or E0402_送方_PLATFORM
                key = clazz.getSimpleName() + "_" + fromBan + "_" + TurnkeyConstant.INVOICE_B2P_TOPARTY_ID;
                toBan = TurnkeyConstant.INVOICE_B2C_TOPARTY_ID; //toBan還是用"00000000"
            }else if (ProcessType.STORAGE.equals(info.getProcessType())){
                key = einvPayload.getFormat() + "_" + fromBan + "_" + TurnkeyConstant.INVOICE_B2C_TOPARTY_ID;
                toBan = TurnkeyConstant.INVOICE_B2C_TOPARTY_ID;
            }else{
                //B2B 的key
                if( GenericValidator.isBlankOrNull(einvPayload.getToBan())){
                    logger.showConsoleError(UpCastProcessor.TASK, TurnkeyUtil.getMessage("UTL_M0056") + " " + invoiceFile, vr.getException(), methodName);
                    throw new TurnkeyException("valid fail! can not decide from ban or to ban");
                }    
                key = einvPayload.getFormat() + "_" + fromBan + "_" + einvPayload.getToBan() ;
                toBan = einvPayload.getToBan();
            }

            CastBean castBean = null;
            
            if(xmlMap.containsKey(key)){
                castBean = xmlMap.get(key);
            }else{
                //因為B2P沒有unmarshal，用另一種方式取得format
                if(einvPayload != null){
                    castBean = createCastBean(invoiceFile, einvPayload.getFormat(), info, fromBan, toBan, 0);                    
                }else{
                    castBean = createCastBean(invoiceFile, clazz.getSimpleName(), info, fromBan, toBan, 0);                                      
                }
            }
            // 更新castBean
            xmlMap.put(key, castBean);
            
//            xml = xml.replace((InvoiceConstant.XML_HEADER + "\r\n"), "");
//            xml = xml.replace((InvoiceConstant.XML_HEADER), ""); // 預防換行不是\r\n的情況
            byte[] xmlByte = (xml + "\r\n").getBytes(EncodingType.UTF8.getValue());
            FileUtil.write(castBean.getXmlPath(), xmlByte, true);
            
//            castBean.addSrcFilePath(seqNo+"."+subSeqNo, invoiceFile);
            
            castBean.addSrcFilePair(invoiceFile, seqNo+"."+subSeqNo); 
            castBean.addCnt();  //發票數+1
            castBean.addFileSize(xmlByte.length); //累計cast檔案大小
            
            String[] nameInfo = castBean.getEINVEnvelopeFileNameInfo();
            msgLog.setUuid(nameInfo[3]);
            // 更新處理時間為UUID時間
            msgLog.setMessageDts(nameInfo[2].replace("-", ""));
            // 更新發票資訊
            if(einvPayload != null){
                msgLog.setInvoiceIdentifier(einvPayload.getInvoiceIdentifier());                
            }else {
                //B2P訊息 此欄位僅紀錄訊息類別
                msgLog.setInvoiceIdentifier(clazz.getSimpleName());
            }
            // 更新收送方
            msgLog.setFromPartyId(fromBan);

            if (info.getCategoryType().equals(CategoryType.B2B)){
                msgLog.setToPartyId(einvPayload.getToBan());
            }else if(info.getCategoryType().equals(CategoryType.B2P)){
                msgLog.setToPartyId(TurnkeyConstant.INVOICE_B2P_TOPARTY_ID);
            }else{
                msgLog.setToPartyId(TurnkeyConstant.INVOICE_B2C_TOPARTY_ID);
            }
            
            TurnkeyMessageLogService.INSTANCE.insUpdMessageLog(msgLog);
            
//            System.out.println(castBean.getFileSize() + " " + TurnkeyConstant.MAX_INVOICE_SIZE);
           
            //2013.6.19MESSAGE的打包處理，因沒有unmarshal, format以clazz.getSimpleName()取得
            if( ProcessType.MESSAGE.equals(info.getProcessType())){
                xmlMap.put(key + "." + castBean.getTotalCnt(), castBean);
                castBean = createCastBean(invoiceFile, clazz.getSimpleName(), info, fromBan, toBan, castBean.getTotalCnt() + 1);
                xmlMap.remove(key);
            }
            //有附檔、交換、訊息(MESSAGE)、檔案大小超過最大size(單筆15MB, file 20MB)、檔案發票超過1000包，包成一包
            else if(einvPayload.isAttachment() || ProcessType.EXCHANGE.equals(info.getProcessType())|| castBean.getFileSize() > TurnkeyConstant.MAX_INVOICE_SIZE || castBean.getCnt()>=TurnkeyConstant.MAX_INVOICE_NUM){
                xmlMap.put(key + "." + castBean.getTotalCnt(), castBean);
                castBean = createCastBean(invoiceFile, einvPayload.getFormat(), info, fromBan, toBan, castBean.getTotalCnt() + 1);
                xmlMap.remove(key);
            }
            
            xmlMap.put(key, castBean);
            // 更新uuid
//            msgLogDetail.setUuid(nameInfo[3]);
            // 更新為UUID時間
//            msgLogDetail.setProcessDts(nameInfo[2].replace("-", ""));
//            TurnkeyMessageLogService.INSTANCE.updMessageLogDetail(msgLogDetail);
            recordCnt++;
            totalCnt++;
        } catch (TurnkeyException e) {
            logger.error("TurnkeyException", e, methodName);
            if(e.getCause()!=null){
                processXMLErrorHandle(info, methodName, partyId, seqNo, subSeqNo, e.getCause(),TurnkeyConstant.ErrorCodeEnum.INVOICE_INVAILD.getValue(),
                        splitedFolder, payload, invoiceFile,"Sys001", einvPayload);
            }else{
                processXMLErrorHandle(info, methodName, partyId, seqNo, subSeqNo, e, TurnkeyConstant.ErrorCodeEnum.INFO.getValue(),
                        splitedFolder, payload, invoiceFile,"CanNotFindSenderPartyId", einvPayload);
            }
        } catch (ParserException e) {
            logger.showConsoleError(UpCastProcessor.TASK, TurnkeyUtil.getMessage("UTL_M0019") + invoiceFile, e, methodName);
            logger.error("ParserException:" + e.getMessage(), e, methodName);
            processXMLErrorHandle(info, methodName, partyId, seqNo, subSeqNo, e, TurnkeyConstant.ErrorCodeEnum.PARSER.getValue(),
                    splitedFolder, payload, invoiceFile,"Sys002", einvPayload);
        } catch (TransformException e) {
            logger.showConsoleError(UpCastProcessor.TASK, TurnkeyUtil.getMessage("UTL_M0020") + invoiceFile, e, methodName);
            logger.error("TransformException:" + e.getMessage(), e, methodName);
            processXMLErrorHandle(info, methodName, partyId, seqNo, subSeqNo, e, TurnkeyConstant.ErrorCodeEnum.TRANSFORM.getValue(),
                    splitedFolder, payload, invoiceFile,"Sys003", einvPayload);
        }catch (IOException e) {
            logger.error("IOException:" + e.getMessage(), e, methodName);
            //跳脫FileNotFound
            if(!(e instanceof FileNotFoundException))
            	throw e;
        } catch (XdaoException e) {
            logger.error("XdaoException:" + e.getMessage(), e, methodName);
            throw e;
        }
    }

    /**
     * 負責錯誤處理，將檔案搬到指定folder
     * 
     * @param info
     * @param methodName
     * @param partyId
     * @param seqNo
     * @param subSeqNo
     * @param e
     * @param errorCode
     * @param splitFolder
     *            yyyyMMdd/hh
     * @throws XdaoException
     * @throws IOException
     */
    private void processXMLErrorHandle(CastInfo info, String methodName, String partyId, String seqNo, String subSeqNo,
            Throwable e, String errorCode, String splitFolder, String payload, File invoiceFile,String propertyCode, EINVPayload einvPayload) throws XdaoException, IOException {
        
        TurnkeyUtil.insertSysEvent(methodName, partyId, seqNo, subSeqNo, e, errorCode, this.getClass().getName(),this.getClass().getPackage().getName(),propertyCode);

        String targetFolder = info.getErrPath() + File.separator + splitFolder;
        File targetFileFolder = new File(targetFolder);
        if(!targetFileFolder.exists())
            targetFileFolder.mkdirs();
        
         
        String errPath = TurnkeyUtil.moveFileToFolderWithDayTime(invoiceFile, info.getErrPath(), null, true);
        logger.showConsoleError_MoveToErr(UpCastProcessor.TASK, invoiceFile, new File(errPath+File.separator+invoiceFile.getName()), methodName);
        
        logger.info("move " + invoiceFile + " to " + errPath+File.separator+invoiceFile.getName(), methodName);
        TurnkeyMessageLog msgLog = new TurnkeyMessageLog();
        msgLog.setSeqno(seqNo);
        msgLog.setSubseqno(subSeqNo);
        msgLog.setStatus(TurnkeyConstant.STATUS_ERROR);
        if(einvPayload!=null){
            msgLog.setInvoiceIdentifier(einvPayload.getInvoiceIdentifier());
        }
        TurnkeyMessageLogService.INSTANCE.insUpdMessageLog(msgLog); // 先寫
        
        TurnkeyMessageLogDetail msgLogDetail = new TurnkeyMessageLogDetail();
        msgLogDetail.setSeqno(seqNo);
        msgLogDetail.setSubseqno(subSeqNo);
        msgLogDetail.setStatus(TurnkeyConstant.STATUS_ERROR);
        msgLogDetail.setTask(TaskEnum.UpCast.getValue());
        msgLogDetail.setFilename(errPath+File.separator+invoiceFile.getName()); // 先寫目前檔名
        TurnkeyMessageLogService.INSTANCE.updMessageLogDetail(msgLogDetail);
    }

    @Override
    public void processFlatFile(String seqNo, File invoiceFile, CastInfo info) throws TurnkeyException, XdaoException {
    }
}
