package tech.easyflow.admin.controller.agent;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import tech.easyflow.admin.controller.ai.support.AiResourceCreatorNameSupport;
import tech.easyflow.agent.entity.Agent;
import tech.easyflow.agent.entity.AgentKnowledgeBinding;
import tech.easyflow.agent.entity.AgentToolBinding;
import tech.easyflow.agent.publish.AgentPublishAppService;
import tech.easyflow.agent.runtime.AgentChatRequest;
import tech.easyflow.agent.runtime.AgentDraftChatRequest;
import tech.easyflow.agent.runtime.AgentRunService;
import tech.easyflow.agent.runtime.composer.AgentComposerDraft;
import tech.easyflow.agent.runtime.composer.AgentComposerDraftService;
import tech.easyflow.agent.runtime.composer.AgentComposerSession;
import tech.easyflow.agent.runtime.media.AgentMediaService;
import tech.easyflow.agent.runtime.media.AgentMediaUploadView;
import com.easyagents.agent.runtime.media.AgentMediaResource;
import tech.easyflow.agent.service.AgentApprovalStateService;
import tech.easyflow.agent.service.AgentKnowledgeBindingService;
import tech.easyflow.agent.service.AgentService;
import tech.easyflow.agent.service.AgentToolBindingService;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.approval.entity.vo.ApprovalActionResult;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.log.annotation.LogReporterDisabled;
import tech.easyflow.system.entity.vo.RoleCategoryAccessSnapshot;
import tech.easyflow.system.enums.CategoryResourceType;
import tech.easyflow.system.enums.ResourceAction;
import tech.easyflow.system.service.CategoryPermissionService;
import tech.easyflow.system.service.ResourceAccessService;

import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import static tech.easyflow.agent.entity.table.AgentTableDef.AGENT;

/**
 * Agent 管理端控制器。
 */
@RestController
@RequestMapping("/api/v1/agent")
public class AgentController extends BaseCurdController<AgentService, Agent> {

    @Resource
    private AgentToolBindingService agentToolBindingService;
    @Resource
    private AgentKnowledgeBindingService agentKnowledgeBindingService;
    @Resource
    private AgentRunService agentRunService;
    @Resource
    private AgentPublishAppService agentPublishAppService;
    @Resource
    private ResourceAccessService resourceAccessService;
    @Resource
    private CategoryPermissionService categoryPermissionService;
    @Resource
    private AgentApprovalStateService agentApprovalStateService;
    @Resource
    private AiResourceCreatorNameSupport aiResourceCreatorNameSupport;
    @Resource
    private AgentMediaService agentMediaService;
    @Resource
    private AgentComposerDraftService agentComposerDraftService;

    /**
     * 创建 Agent 控制器。
     *
     * @param service Agent 服务
     */
    public AgentController(AgentService service) {
        super(service);
    }

    /**
     * 获取 Agent 详情。
     *
     * @param id Agent ID
     * @return Agent 详情
     */
    @GetMapping("/getDetail")
    public Result<Agent> getDetail(BigInteger id) {
        Agent agent = service.getDetail(id);
        agentApprovalStateService.fillAgentApprovalState(agent);
        return Result.ok(agent);
    }

    /**
     * 保存 Agent 草稿。
     *
     * @param agent Agent 草稿
     * @return Agent 详情
     */
    @Override
    @PostMapping("save")
    public Result<?> save(@JsonBody Agent agent) {
        return Result.ok(service.saveDraft(agent));
    }

    /**
     * 更新 Agent 草稿。
     *
     * @param agent Agent 草稿
     * @return Agent 详情
     */
    @Override
    @PostMapping("update")
    public Result<?> update(@JsonBody Agent agent) {
        return Result.ok(service.updateDraft(agent));
    }

    /**
     * 查询 Agent 列表。
     *
     * @param entity 查询条件
     * @param asTree 是否转树
     * @param sortKey 排序字段
     * @param sortType 排序方式
     * @return Agent 列表
     */
    @Override
    public Result<List<Agent>> list(Agent entity, Boolean asTree, String sortKey, String sortType) {
        HttpServletRequest request = currentRequest();
        QueryWrapper queryWrapper = request == null ? QueryWrapper.create() : buildQueryWrapper(request);
        if (!applyCategoryPermission(queryWrapper)) {
            return Result.ok(Collections.emptyList());
        }
        applyPublishedOnlyFilter(queryWrapper);
        queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
        List<Agent> agents = service.list(queryWrapper);
        if (isPublishedOnlyRequest()) {
            agents = agents.stream().map(agent -> service.fromSnapshot(agent.getPublishedSnapshotJson())).toList();
        }
        agentApprovalStateService.fillAgentApprovalState(agents);
        aiResourceCreatorNameSupport.fillAgentCreatorNames(agents);
        return Result.ok(agents);
    }

    /**
     * 运行 Agent 纯文本聊天。
     *
     * @param request 聊天请求
     * @return SSE Emitter
     */
    @PostMapping("chat")
    public SseEmitter chat(@JsonBody AgentChatRequest request) {
        return agentRunService.chat(request);
    }

    /**
     * 运行 Agent 草稿态纯文本试用。
     *
     * @param request 草稿试用请求
     * @return SSE Emitter
     */
    @PostMapping("/chat/draft")
    public SseEmitter chatDraft(@JsonBody AgentDraftChatRequest request) {
        return agentRunService.chatDraft(request);
    }

    /**
     * 上传一张 Agent 聊天临时图片。
     *
     * @param file 图片文件
     * @param mode 聊天模式
     * @param agentId Agent ID
     * @param sessionId 会话 ID
     * @return 上传结果
     */
    @PostMapping(value = "/media/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Result<AgentMediaUploadView> uploadMedia(@RequestParam("file") MultipartFile file,
                                                    @RequestParam("mode") String mode,
                                                    @RequestParam("agentId") String agentId,
                                                    @RequestParam("sessionId") String sessionId) {
        return Result.ok(agentMediaService.upload(file, mode, agentId, sessionId, SaTokenUtil.getLoginAccount()));
    }

    /**
     * 删除当前账号尚未发送的临时图片。
     *
     * @param uploadId 上传 ID
     * @return 操作结果
     */
    @PostMapping("/media/delete")
    public Result<Void> deleteMedia(@JsonBody(value = "uploadId", required = true) String uploadId) {
        agentMediaService.deleteUpload(uploadId, SaTokenUtil.getLoginAccount());
        return Result.ok();
    }

    /**
     * 通过鉴权代理读取 Agent 私有聊天图片。
     *
     * @param reference 稳定图片引用
     * @return 图片响应
     */
    @GetMapping("/media/content")
    @LogReporterDisabled
    public ResponseEntity<byte[]> mediaContent(@RequestParam("reference") String reference) {
        AgentMediaResource resource = agentMediaService.load(reference, SaTokenUtil.getLoginAccount());
        return ResponseEntity.ok()
                .header(HttpHeaders.CACHE_CONTROL, "private, no-store")
                .header(HttpHeaders.CONTENT_DISPOSITION, "inline")
                .contentType(MediaType.parseMediaType(resource.mimeType()))
                .contentLength(resource.bytes().length)
                .body(resource.bytes());
    }

    /**
     * 为输入框预分配稳定会话 ID。
     *
     * @param mode 聊天模式
     * @return 会话信息
     */
    @PostMapping("/composer/session")
    public Result<AgentComposerSession> allocateComposerSession(
            @JsonBody(value = "mode", required = true) String mode) {
        return Result.ok(agentComposerDraftService.allocateSession(mode));
    }

    /**
     * 保存 Agent 输入草稿。
     *
     * @param draft 输入草稿
     * @return 保存后的草稿
     */
    @PostMapping("/composer/draft/persist")
    public Result<AgentComposerDraft> saveComposerDraft(@JsonBody AgentComposerDraft draft) {
        return Result.ok(agentComposerDraftService.save(draft, SaTokenUtil.getLoginAccount()));
    }

    /**
     * 获取当前会话或最近未发送会话的输入草稿。
     *
     * @param mode 聊天模式
     * @param agentId Agent ID
     * @param sessionId 会话 ID，可为空
     * @return 输入草稿
     */
    @GetMapping("/composer/draft")
    public Result<AgentComposerDraft> getComposerDraft(@RequestParam("mode") String mode,
                                                       @RequestParam("agentId") String agentId,
                                                       @RequestParam(value = "sessionId", required = false) String sessionId) {
        return Result.ok(agentComposerDraftService.get(mode, agentId, sessionId, SaTokenUtil.getLoginAccount())
                .orElse(null));
    }

    /**
     * 删除已发送或主动清空的输入草稿。
     *
     * @param mode 聊天模式
     * @param agentId Agent ID
     * @param sessionId 会话 ID
     * @param imageUploadIds 调用方仍持有的上传 ID
     * @param deleteUploads 是否同时删除临时图片
     * @return 操作结果
     */
    @PostMapping("/composer/draft/delete")
    public Result<Void> deleteComposerDraft(@JsonBody(value = "mode", required = true) String mode,
                                            @JsonBody(value = "agentId", required = true) String agentId,
                                            @JsonBody(value = "sessionId", required = true) String sessionId,
                                            @JsonBody(value = "imageUploadIds") List<String> imageUploadIds,
                                            @JsonBody(value = "deleteUploads") Boolean deleteUploads) {
        agentComposerDraftService.delete(mode, agentId, sessionId, imageUploadIds,
                !Boolean.FALSE.equals(deleteUploads), SaTokenUtil.getLoginAccount());
        return Result.ok();
    }

    /**
     * 清理 Agent 草稿试运行会话。
     *
     * @param sessionId 草稿试运行会话 ID
     * @return 操作结果
     */
    @PostMapping("/chat/draft/clear")
    public Result<Void> clearDraftSession(@JsonBody(value = "sessionId", required = true) String sessionId) {
        agentRunService.clearDraftSession(sessionId);
        return Result.ok();
    }

    /**
     * 批准工具执行。
     *
     * @param requestId 请求 ID
     * @param resumeToken 恢复令牌
     * @return 操作结果
     */
    @PostMapping("/run/approve")
    public Result<Void> approve(@JsonBody("requestId") String requestId,
                                @JsonBody(value = "resumeToken", required = true) String resumeToken) {
        agentRunService.approve(requestId, resumeToken);
        return Result.ok();
    }

    /**
     * 拒绝工具执行。
     *
     * @param requestId 请求 ID
     * @param resumeToken 恢复令牌
     * @param reason 拒绝原因
     * @return 操作结果
     */
    @PostMapping("/run/reject")
    public Result<Void> reject(@JsonBody("requestId") String requestId,
                               @JsonBody(value = "resumeToken", required = true) String resumeToken,
                               @JsonBody("reason") String reason) {
        agentRunService.reject(requestId, resumeToken, reason);
        return Result.ok();
    }

    /**
     * 更新 Agent 工具绑定。
     *
     * @param agentId Agent ID
     * @param bindings 工具绑定
     * @return 保存后的启用绑定
     */
    @PostMapping("/toolBinding/update")
    @SaCheckPermission("/api/v1/agent/save")
    public Result<List<AgentToolBinding>> updateToolBinding(@JsonBody(value = "agentId", required = true) BigInteger agentId,
                                                            @JsonBody("bindings") List<AgentToolBinding> bindings) {
        return Result.ok(agentToolBindingService.replaceBindings(agentId, bindings));
    }

    /**
     * 更新 Agent 知识库绑定。
     *
     * @param agentId Agent ID
     * @param bindings 知识库绑定
     * @return 保存后的启用绑定
     */
    @PostMapping("/knowledgeBinding/update")
    @SaCheckPermission("/api/v1/agent/save")
    public Result<List<AgentKnowledgeBinding>> updateKnowledgeBinding(@JsonBody(value = "agentId", required = true) BigInteger agentId,
                                                                      @JsonBody("bindings") List<AgentKnowledgeBinding> bindings) {
        return Result.ok(agentKnowledgeBindingService.replaceBindings(agentId, bindings));
    }

    /**
     * 提交发布审批。
     *
     * @param id Agent ID
     * @return 审批实例 ID
     */
    @PostMapping("/submitPublishApproval")
    @SaCheckPermission("/api/v1/agent/save")
    public Result<BigInteger> submitPublishApproval(@JsonBody("id") BigInteger id) {
        return buildApprovalActionResult(agentPublishAppService.submitPublishApproval(id), "已提交发布审批", "已直接发布");
    }

    /**
     * 提交下线审批。
     *
     * @param id Agent ID
     * @return 审批实例 ID
     */
    @PostMapping("/submitOfflineApproval")
    @SaCheckPermission("/api/v1/agent/save")
    public Result<BigInteger> submitOfflineApproval(@JsonBody("id") BigInteger id) {
        return buildApprovalActionResult(agentPublishAppService.submitOfflineApproval(id), "已提交下线审批", "已直接下线");
    }

    /**
     * 提交删除审批。
     *
     * @param id Agent ID
     * @return 审批实例 ID
     */
    @PostMapping("/submitDeleteApproval")
    @SaCheckPermission("/api/v1/agent/remove")
    public Result<BigInteger> submitDeleteApproval(@JsonBody("id") BigInteger id) {
        return buildApprovalActionResult(agentPublishAppService.submitDeleteApproval(id), "已提交删除审批", "已直接删除");
    }

    @Override
    protected Result<?> onRemoveBefore(Collection<Serializable> ids) {
        for (Serializable id : ids) {
            Agent agent = service.getById(String.valueOf(id));
            if (agent != null) {
                resourceAccessService.assertAccess(CategoryResourceType.AGENT, agent, ResourceAction.MANAGE, "无权限删除该 Agent");
            }
        }
        agentToolBindingService.remove(QueryWrapper.create().in("agent_id", ids));
        agentKnowledgeBindingService.remove(QueryWrapper.create().in("agent_id", ids));
        return super.onRemoveBefore(ids);
    }

    /**
     * 查询 Agent 分页。
     *
     * @param page 分页参数
     * @param queryWrapper 查询条件
     * @return Agent 分页
     */
    @Override
    protected Page<Agent> queryPage(Page<Agent> page, QueryWrapper queryWrapper) {
        if (!applyCategoryPermission(queryWrapper)) {
            return new Page<>(Collections.emptyList(), page.getPageNumber(), page.getPageSize(), 0L);
        }
        applyPublishedOnlyFilter(queryWrapper);
        Page<Agent> result = super.queryPage(page, queryWrapper);
        if (isPublishedOnlyRequest()) {
            result.setRecords(result.getRecords().stream().map(agent -> service.fromSnapshot(agent.getPublishedSnapshotJson())).toList());
        }
        agentApprovalStateService.fillAgentApprovalState(result.getRecords());
        aiResourceCreatorNameSupport.fillAgentCreatorNames(result.getRecords());
        return result;
    }

    private boolean applyCategoryPermission(QueryWrapper queryWrapper) {
        RoleCategoryAccessSnapshot access = categoryPermissionService.getCurrentAccess(CategoryResourceType.AGENT.getCode());
        if (!access.isRestricted()) {
            return true;
        }
        if (access.getCategoryIds().isEmpty()) {
            queryWrapper.eq(Agent::getCreatedBy, access.getAccountId());
            return true;
        }
        queryWrapper.and(AGENT.CREATED_BY.eq(access.getAccountId()).or(AGENT.CATEGORY_ID.in(access.getCategoryIds())));
        return true;
    }

    private void applyPublishedOnlyFilter(QueryWrapper queryWrapper) {
        if (isPublishedOnlyRequest()) {
            queryWrapper.eq("publish_status", PublishStatus.PUBLISHED.getCode());
        }
    }

    private boolean isPublishedOnlyRequest() {
        HttpServletRequest request = currentRequest();
        if (request == null) {
            return false;
        }
        return "true".equalsIgnoreCase(request.getParameter("publishedOnly"));
    }

    /**
     * 获取当前 HTTP 请求。
     *
     * @return 当前请求，不在 Web 请求上下文中时返回 null
     */
    private HttpServletRequest currentRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return null;
        }
        return attributes.getRequest();
    }

    private Result<BigInteger> buildApprovalActionResult(ApprovalActionResult actionResult,
                                                         String approvalMessage,
                                                         String directMessage) {
        return Result.ok(actionResult.isApprovalRequired() ? approvalMessage : directMessage, actionResult.getInstanceId());
    }
}
