package com.nbport.zgwl.contractlaunch.controller;

import com.nbport.zgwl.common.PageResult;
import com.nbport.zgwl.common.R;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchApproveDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchQueryDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchSealDto;
import com.nbport.zgwl.contractlaunch.service.ContractLaunchService;
import com.nbport.zgwl.contractlaunch.utils.ContractLaunchConstants;
import com.nbport.zgwl.entity.SysUserEntity;
import com.nbport.zgwl.utils.AdminSecurityManage;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * 合同发起 Controller
 *
 * 这里除了正常 CRUD，还额外提供了几类“测试按钮”接口：
 * 1. 模拟法务审批通过 / 驳回
 * 2. 模拟合同采集
 * 3. 合同归档
 * 4. 回写签章结果
 *
 * 这些测试接口在法务系统不可用期间便于你完整演示流程，
 * 后续接入真实系统后，保留或删除都很方便。
 */
@RestController
@RequiredArgsConstructor
@RequestMapping(ContractLaunchConstants.REQUEST_PREFIX)
public class ContractLaunchController {

    private final ContractLaunchService contractLaunchService;
    private final AdminSecurityManage adminSecurityManage;

    /**
     * 分页查询合同列表。
     *
     * 支持按合同名称、合同编号、合同类型、状态、创建时间范围过滤。
     * 返回结果附带合同方摘要、文件列表、履行计划（不含日志，列表页日志量大）。
     *
     * 对应前端功能：合同列表页
     *
     * 请求体 ContractLaunchQueryDto 字段：
     *   contractName - 合同名称（模糊）
     *   contractCode - 合同编号
     *   contractType - 合同类型
     *   status       - 状态（draft/reviewing/rejected/pending_sign/signing/...）
     *   dateStart    - 创建时间范围-开始（yyyy-MM-dd）
     *   dateEnd      - 创建时间范围-结束（yyyy-MM-dd）
     */
    @PostMapping("/list")
    public R<PageResult<ContractLaunchDto>> queryList(@RequestBody ContractLaunchQueryDto queryDto) {
        return R.success(contractLaunchService.queryPage(queryDto));
    }

    /**
     * 查询合同详情（含所有子表数据）。
     *
     * 返回合同基本信息 + 合同方列表 + 文件列表 + 履行计划 + 操作日志。
     *
     * 对应前端功能：合同详情页 / 编辑页面回显
     */
    @GetMapping("/{id}")
    public R<ContractLaunchDto> getDetail(@PathVariable("id") Long id) {
        return R.success(contractLaunchService.getDetail(id));
    }

    /**
     * 获取当前登录人的我方组织信息（当前先返回 mock 数据）。
     *
     * 用途：
     * 1. 发起合同页面只读展示“我方”信息
     * 2. 后续接入真实组织主数据时，前端不需要再改字段来源
     */
    @GetMapping("/self-profile")
    public R<Object> getSelfProfile() {
        SysUserEntity currentUser = adminSecurityManage.getUser();
        return R.success(currentUser == null ? null : currentUser.getSysOrgEntity());
    }

    /**
     * 创建合同草稿。
     *
     * 流程：生成 contractSysId（CLS-xxx）和 contractCode → 保存主表 → 保存子表（合同方/文件/履行计划）→ 记录操作日志
     * 状态变为：draft（草稿）
     *
     * 必填校验项：合同名称、合同类型、合同类型编码、计价方式、币种、签订方式、合同期限类型、至少一个甲方+乙方
     *
     * 对应前端功能：新建合同 → 保存草稿
     */
    @PostMapping
    public R<ContractLaunchDto> createDraft(@RequestBody ContractLaunchDto contract) {
        return R.success(contractLaunchService.createDraft(contract));
    }

    /**
     * 编辑合同草稿。
     *
     * 仅 draft（草稿）或 rejected（已驳回）状态允许编辑。
     * 逻辑：先删子表旧数据，再以当前数据重新插入（替换式更新）。
     * 保留原有的 contractSysId、contractCode、创建人/创建时间 不变。
     *
     * 对应前端功能：编辑草稿 → 保存
     */
    @PutMapping
    public R<ContractLaunchDto> updateDraft(@RequestBody ContractLaunchDto contract) {
        return R.success(contractLaunchService.updateDraft(contract));
    }

    /**
     * 复制合同并生成新草稿。
     *
     * 业务设计：
     * 1. 允许从任意未删除合同复制出一份新的草稿，方便高频复用历史合同
     * 2. 保留主信息、合同方、正文附件、履行计划等业务内容
     * 3. 重置系统编号、合同编号、审批/签署/归档状态与相关痕迹，避免串流程数据
     *
     * 对应前端功能：列表页 → 复制合同
     */
    @PostMapping("/{id}/copy")
    public R<ContractLaunchDto> copyDraft(@PathVariable("id") Long id) {
        return R.success(contractLaunchService.copyDraft(id));
    }

    /**
     * 基于现有合同发起变更/附加合同草稿。
     *
     * 处理原则：
     * 1. 复制现有业务内容
     * 2. 自动关联主合同编码 mainContractCode
     * 3. 生成新的草稿，供业务在此基础上做变更补充
     */
    @PostMapping("/{id}/change")
    public R<ContractLaunchDto> createChangeDraft(@PathVariable("id") Long id) {
        return R.success(contractLaunchService.createChangeDraft(id));
    }

    /**
     * 提交法务审批。
     *
     * 状态流转：draft/rejected → reviewing
     * 调用结算平台 pushContract 接口将合同数据推送给法务系统开始审批流程。
     * 目前法务接口不可用，仅做状态变更 + 记录日志（TODO：联调时打开 settlementPushService.pushContract 调用）。
     *
     * 推送法务的报文包含：合同基本字段 + contractOpposites（相对方） + contractKxPlans（履行计划） + 附件等
     *
     * 对应前端功能：提交审批按钮
     */
    @PostMapping("/{id}/submit")
    public R<ContractLaunchDto> submitToLaw(@PathVariable("id") Long id) {
        return R.success(contractLaunchService.submitToLaw(id));
    }

    /**
     * 【测试按钮】模拟法务审批通过。
     *
     * 状态流转：reviewing → pending_sign（待签署）
     * 生成模拟的 lawContractCode 和 lawContractId 记录到合同上，以便后续流程使用。
     * 可在请求体中传入 approveMessage（审批意见）、lawContractCode、lawContractId 覆盖自动生成的值。
     *
     * 真实场景下，此方法应由法务系统的回调接口替代（见 lawApproveCallback）。
     *
     * 对应前端功能：测试按钮 → 模拟审批通过
     */
    @PostMapping("/{id}/mock-approve")
    public R<ContractLaunchDto> mockApprove(@PathVariable("id") Long id,
                                            @RequestBody(required = false) ContractLaunchApproveDto approveDto) {
        return R.success(contractLaunchService.mockApprove(id, approveDto));
    }

    /**
     * 【测试按钮】模拟法务审批驳回。
     *
     * 状态流转：reviewing → rejected（已驳回）
     * 可在请求体中传入 approveMessage（驳回原因）。
     * 驳回后合同可再次编辑和提交。
     *
     * 对应前端功能：测试按钮 → 模拟驳回
     */
    @PostMapping("/{id}/mock-reject")
    public R<ContractLaunchDto> mockReject(@PathVariable("id") Long id,
                                           @RequestBody(required = false) ContractLaunchApproveDto approveDto) {
        return R.success(contractLaunchService.mockReject(id, approveDto));
    }

    /**
     * 法务审批结果回调入口（供法务系统调用）。
     *
     * submitToLaw 时推给法务的 pushUrl 默认指向此接口：POST /contract/launch/callback/law/approve
     * 法务审批完成后，会往 pushUrl POST 审批结果。
     *
     * 当前法务接口不可用，但此回调接口已预先实现，可接受测试请求验证流程。
     * 请求体使用 Map 接收以兼容不同版本的字段名，内部通过 getFirstText 按多个 key 依次尝试提取。
     *
     * 识别合同方式：从报文中取 contractSysId 定位到本系统的合同
     * 判断通过/驳回：从 approveStatus/status/result 等多个字段名中取值判断
     *
     * 状态流转：reviewing → pending_sign（通过） / rejected（驳回）
     *
     * 对应前端功能：法务系统审批完成后自动回调（目前用法务系统暂未对接）
     */
    @PostMapping("/callback/law/approve")
    public R<ContractLaunchDto> lawApproveCallback(@RequestBody Map<String, Object> callbackPayload) {
        return R.success(contractLaunchService.handleLawApproveCallback(callbackPayload));
    }

    /**
     * 签署发起回调（浙江CA安全签调用）。
     *
     * 在浙江CA安全签平台创建签署合同后，回写 sealContractId 到本系统合同。
     * 状态流转：pending_sign → signing（签署中）
     *
     * 请求体 ContractLaunchSealDto 字段：
     *   sealContractId      - 签章平台生成的合同ID（必填）
     *   sealContractStatus  - 签署状态（1:签署中）
     *
     * 对应前端功能：调起浙江CA签署后，回写签章合同ID
     */
    @PostMapping("/{id}/seal/save")
    public R<ContractLaunchDto> saveSealInfo(@PathVariable("id") Long id,
                                             @RequestBody ContractLaunchSealDto sealDto) {
        return R.success(contractLaunchService.saveSealInfo(id, sealDto));
    }

    /**
     * 签署完成回调（浙江CA安全签/线下签署完成后调用）。
     *
     * 所有签署方完成签署后，将签署完成的合同文件信息回写。
     * 状态流转：signing → pending_collect（待采集）
     *
     * 请求体 ContractLaunchSealDto 字段：
     *   sealContractStatus   - 签署状态（2:签署完成）
     *   signedFileName       - 签署完成后的文件名
     *   signedFilePath       - 签署文件存储路径
     *   signedFileType       - 文件类型
     *   signedFileSize       - 文件大小
     *   signedOfflineReason  - 线下签署原因（如果未在线签署）
     *
     * 签署文件会自动保存到 file 子表（fileCategory = "signed"），先删旧签署文件再插入新的。
     *
     * 对应前端功能：签署完成 → 回写签署结果
     */
    @PostMapping("/{id}/seal/finish")
    public R<ContractLaunchDto> finishSeal(@PathVariable("id") Long id,
                                           @RequestBody ContractLaunchSealDto sealDto) {
        return R.success(contractLaunchService.markSealCompleted(id, sealDto));
    }

    /**
     * 【测试按钮】模拟合同采集。
     *
     * 合同签署完成后，调用数据采集 V3.0 的 collectContract 接口将合同信息推送到法务系统存档。
     * 目前法务接口不可用，仅做状态变更 + 记录日志（TODO：联调时打开 dataCollectionPushService.collectContract 调用）。
     *
     * 状态流转：pending_collect → pending_archive（待归档）
     *
     * 推送数据采集的报文包含：合同基本字段 + 用印信息（c_seal）+ 签订信息（c_sign）+ 附件等
     *
     * 对应前端功能：测试按钮 → 模拟采集
     */
    @PostMapping("/{id}/mock-collect")
    public R<ContractLaunchDto> mockCollect(@PathVariable("id") Long id) {
        return R.success(contractLaunchService.mockCollect(id));
    }

    /**
     * 合同归档。
     *
     * 合同全生命周期最后一步，状态变为 completed（已完成）。
     * 同时将归档信息推送到法务系统存档（TODO：联调时打开 settlementPushService.pushArchive 调用）。
     *
     * 请求体 ContractLaunchArchiveDto 字段：
     *   archiveKeeper       - 档案保管人手机号
     *   archiveKeeperUnit   - 档案保管单位名称
     *   archiveKeeperUnitCode - 档案保管单位编码
     *   archiveFiles        - 归档附件列表
     *
     * 归档附件自动保存到 file 子表（fileCategory = "archive"）。
     *
     * 对应前端功能：归档按钮 → 合同完结
     */
    @PostMapping("/{id}/archive")
    public R<ContractLaunchDto> archive(@PathVariable("id") Long id,
                                        @RequestBody(required = false) ContractLaunchArchiveDto archiveDto) {
        return R.success(contractLaunchService.archive(id, archiveDto));
    }

    /**
     * 合同终止。
     *
     * 允许除 completed 外的任意环节执行终止。
     * 当前仅在本系统内变更状态并记录日志，法务系统终止需后续分别操作。
     */
    @PostMapping("/{id}/terminate")
    public R<ContractLaunchDto> terminate(@PathVariable("id") Long id,
                                          @RequestBody(required = false) Map<String, Object> payload) {
        Object reasonValue = payload == null ? null : payload.get("terminateReason");
        String terminateReason = reasonValue == null ? null : String.valueOf(reasonValue).trim();
        return R.success(contractLaunchService.terminate(id, terminateReason));
    }

    /**
     * 删除合同草稿（逻辑删除）。
     *
     * 仅 draft（草稿）或 rejected（已驳回）状态允许删除。
     * 逻辑：设置 isDeleted = 'Y'，不物理删除数据。
     *
     * 对应前端功能：删除草稿按钮
     */
    @DeleteMapping("/{id}")
    public R<String> deleteDraft(@PathVariable("id") Long id) {
        contractLaunchService.deleteDraft(id);
        return R.success("删除成功");
    }
}
