package sign

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"mtp20access/global"
	"mtp20access/model/account"
	"mtp20access/model/account/request"
	"mtp20access/model/account/response"
	"mtp20access/service/asign"
	"mtp20access/utils"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/gofrs/uuid"
	"go.uber.org/zap"
)

// QueryUserESignRecord 查询用户电子签记录表
func QueryUserESignRecord(userId int) (rsp []account.Useresignrecord, err error) {
	rsp = make([]account.Useresignrecord, 0)
	if err = global.M2A_DB.Where("USERID = ?", userId).Find(&rsp); err != nil {
		return
	}
	for i := range rsp {
		data := &rsp[i]
		if data.TEMPLATETYPE == 2 && data.RECORDSTATUS == 2 {
			// 去爱签同步合同状态
			rspCTStatus, e := asign.ContractStatus(data.CONTRACTNO)
			if e != nil {
				err = e
				global.M2A_LOG.Error("【QueryUserESignRecord】 查询合同状态失败", zap.Error(err))
				return
			}
			// 下载合同并修改合同状态
			e = modifyContractStatus(data.CONTRACTNO, strconv.Itoa(rspCTStatus.Data.Status))
			if e != nil {
				// 从新获取记录
				recordId := data.RECORDID
				data = new(account.Useresignrecord)
				has, e := global.M2A_DB.Where("RECORDID = ?", recordId).Get(data)
				if e != nil || !has {
					err = e
					global.M2A_LOG.Error("【QueryUserESignRecord】 获取用户电子签记录失败", zap.Error(err))
					return
				}
			}
		}
	}

	return
}

// AddUser 添加用户
func AddUser(req request.AddUserReq, userId int) (err error) {
	// 获取用户电子签记录
	useresignrecord := new(account.Useresignrecord)
	has, _ := global.M2A_DB.Where("USERID = ? AND TEMPLATETYPE = 1 AND RECORDSTATUS = 3", userId).Get(useresignrecord)
	if has {
		err = errors.New("用户已同步")
		return
	}

	// 调用爱签API-添加个人用户(https://{host}/user/addPersonalUser)
	rsp, err := asign.AddPersonalUserBy(
		strconv.Itoa(userId),
		req.Name,
		req.IdCard,
		req.Mobile,
		req.IdCardType,
	)
	if err != nil {
		return
	}
	if rsp.Code != 100000 {
		err = errors.New(strconv.Itoa(rsp.Code))
		global.M2A_LOG.Error("【AddUser】 接口调用失败", zap.Error(err))
		return
	}

	// 更新用户电子签记录-实名认证状态
	authinfo, err := json.Marshal(req)
	if err != nil {
		global.M2A_LOG.Error("【AddUser】 构建AUTHINFO失败", zap.Error(err))
		return
	}
	sql := fmt.Sprintf(`
		UPDATE useresignrecord
		SET RECORDSTATUS = 3,
			UPDATETIME = SYSDATE,
			AUTHINFO = '%v'
		WHERE USERID = %v AND TEMPLATETYPE = 1 
	`, string(authinfo), userId)
	if _, err = global.M2A_DB.Exec(sql); err != nil {
		global.M2A_LOG.Error("【AddUser】 更新用户电子签记录失败", zap.Error(err))
		return
	}

	return
}

// CreateContractAndAddSigner 上传待签署文件和添加签署方
func CreateContractAndAddSigner(req request.CreateContractAndAddSignerReq, userId int) (rsp response.CreateContractAndAddSignerRsp, err error) {
	// 获取用户电子签记录
	useresignrecord := new(account.Useresignrecord)
	has, err := global.M2A_DB.Where("USERID = ? AND TEMPLATENO = ?", userId, req.TemplateNo).Get(useresignrecord)
	if err != nil || !has {
		global.M2A_LOG.Error("【CreateContractAndAddSigner】 获取用户电子签记录失败", zap.Error(err))
		return
	}

	// 判断是否需要创建合同（上传待签署文件）
	if useresignrecord.CONTRACTNO == "" {
		// 生成合同编号
		// #{userid} || '_' || to_char(sysdate, 'yyyyMMddhh24miss') || '_' ||              seq_useresignrecord.currval,
		contractNo := fmt.Sprintf("%d_%s_%v", userId, time.Now().Format("20060102150405"), useresignrecord.RECORDID)
		// 调用爱签API-上传待签署文件(https://{host}/contract/createContract)
		r, e := asign.CreateContract(
			contractNo,
			useresignrecord.TEMPLATENAME,
			useresignrecord.TEMPLATENO,
		)
		if e != nil {
			err = e
			return
		}
		if r.Code != 100000 {
			err = errors.New(strconv.Itoa(r.Code))
			global.M2A_LOG.Error("【CreateContractAndAddSigner】 上传待签署文件接口调用失败", zap.Error(err))
			return
		}
		// 将返回的合同编号写入数据库
		useresignrecord.CONTRACTNO = contractNo
		sql := fmt.Sprintf(`
			UPDATE useresignrecord 
			SET contractNo = '%v',
				UPDATETIME = SYSDATE
			WHERE RECORDID = %v
		`, useresignrecord.CONTRACTNO, useresignrecord.RECORDID)
		if _, err = global.M2A_DB.Exec(sql); err != nil {
			global.M2A_LOG.Error("【CreateContractAndAddSigner】 写入合同编号失败", zap.Error(err))
			return
		}
	}

	// 判断是否需要添加签署方（获取合同签约地址）
	if useresignrecord.SIGNURL != "" {
		err = errors.New("合同签署链接已存在")
	} else {
		// 调用爱签API-添加签署方(https://{host}/contract/addSigner)
		r, e := asign.AddSigner(
			useresignrecord.CONTRACTNO,
			strconv.Itoa(userId),
		)
		if e != nil {
			err = e
			return
		}
		if r.Code != 100000 {
			err = errors.New(strconv.Itoa(r.Code))
			global.M2A_LOG.Error("【CreateContractAndAddSigner】 添加签署方接口调用失败", zap.Error(err))
			return
		}
		if len(r.Data.SignUser) > 0 {
			useresignrecord.SIGNURL = r.Data.SignUser[0].SignUrl
			// 将返回的合同编号写入数据库
			sql := fmt.Sprintf(`
				UPDATE useresignrecord 
				SET RECORDSTATUS = 2, 
					SIGNURL = '%v', 
					UPDATETIME = SYSDATE
				WHERE RECORDID = %v
			`, useresignrecord.SIGNURL, useresignrecord.RECORDID)
			if _, err = global.M2A_DB.Exec(sql); err != nil {
				global.M2A_LOG.Error("【CreateContractAndAddSigner】 写入合同签署链接失败", zap.Error(err))
				return
			}
			// 给客户端返回合同签署地址
			rsp.SignUrl = useresignrecord.SIGNURL
		}
	}

	return
}

// SignCompleted
func SignCompleted(userId int) (err error) {
	// 获取用户电子签记录
	datas := make([]account.Useresignrecord, 0)
	err = global.M2A_DB.Where("USERID = ?", userId).Find(&datas)
	if err != nil {
		global.M2A_LOG.Error("[SignCompleted] 获取用户电子签记录失败", zap.Error(err))
		return
	}

	var record *account.Useresignrecord
	for i, item := range datas {
		if item.TEMPLATETYPE == 1 {
			record = &datas[i]
		}
		switch item.TEMPLATETYPE {
		case 1: // 实名认证
			if item.RECORDSTATUS != 3 {
				err = errors.New("未完成用户添加")
				return
			}
		case 2: // 合同签署
			// 如果是签署中状态，则查询一下合同状态
			if item.RECORDSTATUS == 2 {
				rspCTStatus, e := asign.ContractStatus(item.CONTRACTNO)
				if e != nil {
					err = e
					global.M2A_LOG.Error("[SignCompleted] 查询合同状态失败", zap.Error(err))
					return
				}
				// 下载合同并修改合同状态
				err = modifyContractStatus(rspCTStatus.Data.ContractNo, strconv.Itoa(rspCTStatus.Data.Status))
				return
			} else if (item.RECORDSTATUS != 3 && item.RECORDSTATUS != 2) && item.TEMPLATETYPE == 2 {
				err = errors.New("未完成所有合同签署")
				global.M2A_LOG.Error(err.Error())
				return
			}
		case 3: // 个人意愿
			if item.RECORDSTATUS != 3 {
				err = errors.New("未完成个人意愿核身认证")
				return
			}
		}
	}

	// 获取临时存储的用户信息
	if record == nil || record.AUTHINFO == "" {
		global.M2A_LOG.Error("[SignCompleted] 获取实名认证信息失败", zap.Error(err))
		return
	}
	// {"name":"甘肃碳交","idCard":"360428200007287603","idCardType":1,"idCardPhoto":"./uploadFile/20230812/202308121518494929.png","idCardPhotoBackURL":"./uploadFile/20230812/202308121518529223.png","mobile":"15914012151"}
	cacheMap := make(map[string]interface{})
	err = json.Unmarshal([]byte(record.AUTHINFO), &cacheMap)
	if err != nil {
		global.M2A_LOG.Error("[SignCompleted] 反序列化临时存储用户信息失败", zap.Error(err))
		return
	}

	// 调用JAVA实名认证接口
	reqParam := make(map[string]interface{})
	reqParam["userid"] = userId
	reqParam["cardnum"] = cacheMap["idCard"]
	reqParam["username"] = cacheMap["name"]
	reqParam["cardtype"] = 0 // 目前写死证件类型为身份证 - 0
	reqParam["cardfrontphotourl"] = cacheMap["idCardPhoto"]
	reqParam["cardbackphotourl"] = cacheMap["idCardPhotoBackURL"]
	reqParam["userinfotype"] = 1 // 目前写死为个人 - 1
	jsonParam, err := json.Marshal(&reqParam)
	if err != nil {
		global.M2A_LOG.Error("[SignCompleted] 反序列化JAVA实名认证入参失败", zap.Error(err))
		return
	}

	// 构建请求
	javaUrl := global.M2A_CONFIG.Asign.OpenApiUrl + "/onlineopen/userInfo/addAuth"
	req, err := http.NewRequest("POST", javaUrl, bytes.NewReader(jsonParam))
	// 设置请求头
	req.Header.Set("Content-Type", "application/json; charset=utf-8")

	client := &http.Client{}
	rsp, err := client.Do(req)
	if err != nil {
		global.M2A_LOG.Error("[SignCompleted] 请求失败", zap.Error(err))
		return
	}
	defer rsp.Body.Close()
	body, err := io.ReadAll(rsp.Body)
	if err != nil {
		global.M2A_LOG.Error("[SignCompleted] 获取body失败", zap.Error(err))
		return
	}
	// rspData: map[code:0 hasAuth:1 message:认证成功]
	rspData := make(map[string]interface{})
	if err = json.Unmarshal(body, &rspData); err != nil {
		global.M2A_LOG.Error("[SignCompleted] 反序列化java body失败", zap.Error(err))
		return
	}
	global.M2A_LOG.Info("[SignCompleted] 调用JAVA实名认证接口返回", zap.Any("rspData", rspData))
	code, ok := rspData["code"]
	if !ok {
		err = errors.New("实名认证失败，请稍后重试")
		return
	}
	if code != "0" {
		err = fmt.Errorf("实名认证失败，%v", rspData["message"])
		global.M2A_LOG.Error("[SignCompleted]", zap.Error(err))
		return
	}

	return
}

/*
handleASignCompleted 处理爱签合同签署完成后回调通知

req 异步推送参数
*/
func HandleASignCompleted(contractNo, status string) (err error) {
	/*
		// 合同签署完成后回调通知示例
		String publickey = "MFwwDQcccccxxxxmEz/nw27Ln6AP90ZCMPi+iNF1m9mhNECAwEAAQ==";
		String remark = ""; // 若被拒签则会返回拒签原因，拒签原因不参与签名
		Map <String, String> map = new HashMap<>();
		map.put("action", "signCompleted");
		map.put("contractNo", "20221114142140345");
		map.put("status", "2");
		map.put("signTime", "2022-11-14 14:22:00");
		map.put("timestamp", "1668406920005");
		map.put("validityTime", "2022-11-24 23:59:59");
		String json = JSONObject.toJSONString(map, SerializerFeature.MapSortField);
		System.out.println("数据:" + json);
		// 计算签名
		try {
		String sign = "feFfcprGjdmDDqRmxK5qlWlMncX0mc6LJ5agebOGIx2QiAern+6ZRg/SBHOgvHp/+1ywVRdyKNUKxPneETwKPw==";
		System.out.println(RSAUtils.rsaSignCheck(sign, json, publickey));
		} catch (Exception e) {
			e.printStackTrace();
		}
	*/

	// 获取合同编号
	if contractNo == "" {
		global.M2A_LOG.Error("【HandleASignCompleted】 获取合同编号失败")
		return
	}
	// 获取合同状态
	if status == "" {
		global.M2A_LOG.Error("【HandleASignCompleted】 获取获取合同状态失败")
		return
	}
	// 下载合同并修改合同状态
	err = modifyContractStatus(contractNo, status)

	return
}

// modifyContractStatus 爱签异步推送和查询合同状态后调用
// 注意传入的status是爱签的合同状态
// 合同状态：
// 0：等待签约
// 1：签约中
// 2：已签约
// 3：过期
// 4：拒签
// 6：作废
// -2：状态异常
func modifyContractStatus(contractNo, status string) (err error) {
	if status == "2" {
		// 获取用户电子签记录
		useresignrecord := new(account.Useresignrecord)
		has, e := global.M2A_DB.Where("CONTRACTNO = ?", contractNo).Get(useresignrecord)
		if e != nil || !has {
			err = e
			global.M2A_LOG.Error("【modifyContractStatus】 获取用户电子签记录失败", zap.Error(err))
			return
		}
		if useresignrecord.RECORDSTATUS == 3 {
			// 已完成签署，不需要再下载和修改数据库状态
			return
		}

		// 已签约
		// 下载合同
		r, e := asign.DownloadContract(contractNo)
		if e != nil {
			err = e
			return
		}
		if r.Code != 100000 {
			err = errors.New(strconv.Itoa(r.Code))
			global.M2A_LOG.Error("【modifyContractStatus】 下载合同接口调用失败", zap.Error(err))
			return
		}
		if r.Data.Data != "" {
			// 获取网上开户地址（用于客户端下载文件）
			openconfig := account.Wskhopenaccountconfig{CONFIGID: 6}
			has, e := openconfig.Get()
			if e != nil || !has {
				err = e
				global.M2A_LOG.Error("【modifyContractStatus】 获取网上开户地址失败", zap.Error(err))
				return
			}
			// 将Base64写入目标文件
			uid, _ := uuid.NewV4()
			fileName := fmt.Sprintf("%v.pdf", uid.String())
			// openconfig.CONFIGVALUE = "./" // FIXME: - 测试代码
			folderPath := "sign/" + time.Now().Format("20060102")
			savePath := openconfig.CONFIGVALUE + "/uploadFile/" + folderPath
			if exist, _ := utils.PathExists(savePath); !exist {
				os.MkdirAll(savePath, os.ModePerm)
			}
			fb, e := base64.StdEncoding.DecodeString(r.Data.Data)
			if e != nil {
				err = e
				global.M2A_LOG.Error("【modifyContractStatus】 解码pdf文件失败", zap.Error(err))
				return
			}
			if err = os.WriteFile(savePath+"/"+fileName, fb, 0666); err != nil {
				global.M2A_LOG.Error("【modifyContractStatus】 保存合同失败", zap.Error(err))
				return
			}

			// 更新数据库记录
			contractfileaddr := fmt.Sprintf("./uploadFile/%v/%v", folderPath, fileName)
			sql := fmt.Sprintf(`
				UPDATE useresignrecord
				SET RECORDSTATUS = 3,
					UPDATETIME = SYSDATE,
					CONTRACTFILEADDR = '%v'
				WHERE CONTRACTNO = '%v'  
			`, contractfileaddr, contractNo)
			if _, err = global.M2A_DB.Exec(sql); err != nil {
				global.M2A_LOG.Error("【modifyContractStatus】 更新用户电子签记录失败", zap.Error(err))
				return
			}
		}
	} else if status == "0" || status == "1" {
		err = fmt.Errorf("合同签署状态不正确, status: %v", status)
		return
	} else {
		// 其它状态一律改为4（签署拒绝）
		// 更新数据库记录
		sql := fmt.Sprintf(`
			UPDATE useresignrecord
			SET RECORDSTATUS = 4,
				UPDATETIME = SYSDATE
			WHERE CONTRACTNO = '%v'  
		`, contractNo)
		if _, err = global.M2A_DB.Exec(sql); err != nil {
			global.M2A_LOG.Error("【HandleASignCompleted】 更新用户电子签记录失败", zap.Error(err))
			return
		}
		err = fmt.Errorf("合同签署状态不正确, status: %v", status)
	}

	return
}

// WillFace 个人意愿核身认证
func WillFace(req request.WillFaceReq, userId int) (wrsp response.WillFaceRsp, err error) {
	// 获取用户电子签记录
	useresignrecord := new(account.Useresignrecord)
	has, err := global.M2A_DB.Where("USERID = ? AND TEMPLATETYPE = 3", userId).Get(useresignrecord)
	if err != nil || !has {
		global.M2A_LOG.Error("【WillFace】 获取用户电子签记录失败", zap.Error(err))
		return
	}
	if useresignrecord.RECORDSTATUS == 3 {
		err = errors.New("不可重复进行个人意愿核身认证")
		return
	}

	// 调用爱签API-个人意愿核身认证(https://{host}/auth/person/willFace)
	rsp, err := asign.WillFace(
		req.RealName,
		req.IdCardNo,
		useresignrecord.TEMPLATENO,
		useresignrecord.TEMPLATENAME,
		strconv.Itoa(int(useresignrecord.RECORDID)))
	if err != nil {
		return
	}
	if rsp.Code != 100000 {
		err = errors.New(strconv.Itoa(rsp.Code))
		global.M2A_LOG.Error("【WillFace】 接口调用失败", zap.Error(err))
		return
	}
	wrsp.FaceUrl = rsp.Data.FaceUrl

	// 更新用户电子签记录
	sql := fmt.Sprintf(`
		UPDATE useresignrecord
		SET RECORDSTATUS = 2,
			UPDATETIME = SYSDATE, 
			SIGNURL = '%v'
		WHERE RECORDID = %v 
	`, rsp.Data.FaceUrl, useresignrecord.RECORDID)
	if _, err = global.M2A_DB.Exec(sql); err != nil {
		global.M2A_LOG.Error("【WillFace】 更新用户电子签记录失败", zap.Error(err))
		return
	}

	return
}

// 个人意愿核身认证爱签回调通知处理
func HandleWillFace(sign, result, msg, recordId string) (err error) {
	// 获取用户电子签记录
	useresignrecord := new(account.Useresignrecord)
	has, e := global.M2A_DB.Where("RECORDID = ?", recordId).Get(useresignrecord)
	if e != nil || !has {
		err = e
		global.M2A_LOG.Error("【HandleWillFace】 获取用户电子签记录失败", zap.Error(err))
		return
	}

	// 人脸认证结果
	// 1：认证成功
	// 2：认证失败
	if result == "1" {
		// FIXME: - 这里应该校验签名（姓名和身份证号码）

		// 更新数据库记录
		sql := fmt.Sprintf(`
			UPDATE useresignrecord
			SET RECORDSTATUS = 3,
				UPDATETIME = SYSDATE
			WHERE RECORDID = %v  
		`, recordId)
		if _, err = global.M2A_DB.Exec(sql); err != nil {
			global.M2A_LOG.Error("【HandleWillFace】 更新用户电子签记录失败", zap.Error(err))
			return
		}
	}

	return
}
