import { Router } from 'express';
import * as issueService from '../services/issue.js';
import * as issueCommentService from '../services/issue-comment.js';
import * as channelService from '../services/channel.js';
import { broadcastToWorkspace } from '../ws/connection-manager.js';
import { stopChannelRuns } from '../ws/agent-runner.js';
import * as agentService from '../services/agent.js';
import * as taskService from '../services/task.js';
import { retryIssue } from '../services/issue-retry.js';
import { hasActiveIssueAutomation, runIssueAutomation } from '../agents/issue-agent-runner.js';
import { continueIssueTaskScheduling, stopIssueAutomation } from '../agents/issue-task-controller.js';
import * as workflowService from '../services/workflow.js';
import { scheduleIssueTitleGeneration } from '../services/generated-title.js';
const router = Router({ mergeParams: true });
router.get('/', (req, res) => {
    const status = req.query.status;
    const issues = issueService.list(req.params.id, status);
    res.json(issues);
});
router.post('/', (req, res) => {
    const { title, description, members, workflowId } = req.body;
    const trimmedTitle = typeof title === 'string' ? title.trim() : '';
    const trimmedDescription = typeof description === 'string' ? description.trim() : '';
    if (!trimmedTitle && !trimmedDescription) {
        res.status(400).json({ error: 'title or description is required' });
        return;
    }
    const issue = issueService.create(req.params.id, {
        title: trimmedTitle,
        description: trimmedDescription,
        members: mergeWorkflowMembers(members, workflowId),
        workflowId,
    });
    const channel = issue.channelId ? channelService.getChannel(req.params.id, issue.channelId) : null;
    if (channel)
        broadcastToWorkspace(req.params.id, 'channel.updated', channel);
    broadcastToWorkspace(req.params.id, 'issue.created', issue);
    res.status(201).json(issue);
    if (!trimmedTitle) {
        scheduleIssueTitleGeneration({
            workspaceId: req.params.id,
            issueId: issue.id,
            requirement: trimmedDescription,
            description: trimmedDescription,
            broadcast: (event, data) => broadcastToWorkspace(req.params.id, event, data),
        });
    }
});
router.get('/:issueId', (req, res) => {
    const issue = issueService.getById(req.params.id, req.params.issueId);
    if (!issue) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    res.json(issue);
});
router.put('/:issueId', (req, res) => {
    const issue = issueService.getById(req.params.id, req.params.issueId);
    if (!issue) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    const previousStatus = issue.status;
    const { title, description, status, members, workflowId, continuousRun } = req.body;
    if (title)
        issue.title = title;
    if (description !== undefined)
        issue.description = description;
    if (workflowId !== undefined)
        issue.workflowId = workflowId || undefined;
    if (continuousRun !== undefined)
        issue.continuousRun = continuousRun !== false;
    if (members !== undefined) {
        issue.members = normalizeIssueMembers(req.params.id, mergeWorkflowMembers(members, issue.workflowId));
    }
    else if (workflowId !== undefined) {
        issue.members = normalizeIssueMembers(req.params.id, mergeWorkflowMembers(issue.members, issue.workflowId));
    }
    if ((members !== undefined || workflowId !== undefined) && issue.channelId) {
        const updatedChannel = channelService.updateChannel(req.params.id, issue.channelId, { members: issue.members });
        if (updatedChannel)
            broadcastToWorkspace(req.params.id, 'channel.updated', updatedChannel);
    }
    if (status) {
        issue.status = status;
    }
    const saved = issueService.save(req.params.id, issue);
    broadcastToWorkspace(req.params.id, 'issue.updated', saved);
    if (status && status !== previousStatus) {
        // 状态变更时停止该议题关联频道中所有运行中的 agent
        if (issue.channelId) {
            stopChannelRuns(req.params.id, issue.channelId);
        }
        broadcastToWorkspace(req.params.id, 'issue.status_changed', { issueId: req.params.issueId, from: previousStatus, to: status });
        // Issue 归档/取消归档时，同步关联的 Channel
        if (issue.channelId && (status === 'archived' || previousStatus === 'archived')) {
            const archived = status === 'archived';
            const updatedChannel = channelService.updateChannel(req.params.id, issue.channelId, { archived });
            if (updatedChannel)
                broadcastToWorkspace(req.params.id, 'channel.updated', updatedChannel);
        }
    }
    res.json(saved);
});
router.post('/:issueId/start', (req, res) => {
    const workspaceId = req.params.id;
    const { issueId } = req.params;
    const before = issueService.getById(workspaceId, issueId);
    if (!before) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    // Pre-check: must have workflow or existing tasks
    const hasWorkflow = Boolean(before.workflowId);
    const existingTasks = taskService.list(workspaceId, issueId);
    if (!hasWorkflow && existingTasks.length === 0) {
        res.status(400).json({ error: 'No workflow or tasks configured for this issue' });
        return;
    }
    const issue = issueService.updateStatus(workspaceId, issueId, 'planned');
    if (!issue) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    broadcastToWorkspace(workspaceId, 'issue.status_changed', { issueId, from: before?.status ?? 'draft', to: 'planned' });
    broadcastToWorkspace(workspaceId, 'issue.updated', issue);
    res.json(issue);
    // trigger issue automation
    const ctx = {
        workspaceId,
        broadcast: (event, data) => broadcastToWorkspace(workspaceId, event, data),
        getSession: (sessionId) => agentService.getById(workspaceId, sessionId),
        updateSessionStatus: (sessionId, status, extra) => agentService.updateStatus(workspaceId, sessionId, status, extra),
    };
    runIssueAutomation(workspaceId, issueId, ctx).catch((err) => {
        console.error(`[issue-start] automation error for issue ${issueId}:`, err);
    });
});
router.post('/:issueId/resume', async (req, res) => {
    const workspaceId = req.params.id;
    const { issueId } = req.params;
    const issue = issueService.getById(workspaceId, issueId);
    if (!issue) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    const ctx = {
        workspaceId,
        broadcast: (event, data) => broadcastToWorkspace(workspaceId, event, data),
        getSession: (sessionId) => agentService.getById(workspaceId, sessionId),
        updateSessionStatus: (sessionId, status, extra) => agentService.updateStatus(workspaceId, sessionId, status, extra),
    };
    const result = await retryIssue(workspaceId, issueId, ctx, { manual: true });
    if (!result.issue) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    res.json(result.issue);
});
router.post('/:issueId/continue', (req, res) => {
    const workspaceId = req.params.id;
    const { issueId } = req.params;
    const before = issueService.getById(workspaceId, issueId);
    if (!before) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    const issue = before.status === 'draft' || before.status === 'planned'
        ? issueService.updateStatus(workspaceId, issueId, 'in_progress')
        : before;
    if (!issue) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    if (issue.status !== before.status) {
        broadcastToWorkspace(workspaceId, 'issue.status_changed', { issueId, from: before.status, to: issue.status });
    }
    broadcastToWorkspace(workspaceId, 'issue.updated', issue);
    res.json(issue);
    const ctx = {
        workspaceId,
        broadcast: (event, data) => broadcastToWorkspace(workspaceId, event, data),
        getSession: (sessionId) => agentService.getById(workspaceId, sessionId),
        updateSessionStatus: (sessionId, status, extra) => agentService.updateStatus(workspaceId, sessionId, status, extra),
    };
    continueIssueTaskScheduling(workspaceId, issueId, ctx).catch((err) => {
        console.error(`[issue-continue] scheduling error for issue ${issueId}:`, err);
    });
});
router.post('/:issueId/interrupt', (req, res) => {
    const workspaceId = req.params.id;
    const { issueId } = req.params;
    const before = issueService.getById(workspaceId, issueId);
    if (!before) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    const reason = 'Interrupted by user';
    const failedTasks = stopIssueAutomation(workspaceId, issueId, reason);
    for (const { task, from } of failedTasks) {
        broadcastToWorkspace(workspaceId, 'task.status_changed', { taskId: task.id, from, to: task.status });
        broadcastToWorkspace(workspaceId, 'task.updated', task);
    }
    if (before.channelId)
        stopChannelRuns(workspaceId, before.channelId);
    const issue = issueService.markError(workspaceId, issueId, reason);
    if (!issue) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    broadcastToWorkspace(workspaceId, 'issue.status_changed', { issueId, from: before.status, to: 'error' });
    broadcastToWorkspace(workspaceId, 'issue.updated', issue);
    res.json(issue);
});
router.delete('/:issueId', (req, res) => {
    const { id, issueId } = req.params;
    const issue = issueService.getById(id, issueId);
    if (!issue) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    // 先停止该议题关联频道中所有运行中的 agent
    if (issue.channelId) {
        stopChannelRuns(id, issue.channelId);
    }
    issueService.remove(id, issueId);
    res.status(204).send();
});
router.get('/:issueId/comments', (req, res) => {
    const issue = issueService.getById(req.params.id, req.params.issueId);
    if (!issue) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    res.json(issueCommentService.listIssueComments(req.params.id, req.params.issueId));
});
router.post('/:issueId/comments', (req, res) => {
    const workspaceId = req.params.id;
    const { issueId } = req.params;
    const content = typeof req.body.content === 'string' ? req.body.content.trim() : '';
    const mentions = Array.isArray(req.body.mentions)
        ? [...new Set(req.body.mentions.filter((mention) => typeof mention === 'string' && Boolean(mention.trim())).map((mention) => mention.trim()))]
        : [];
    if (!content) {
        res.status(400).json({ error: 'content is required' });
        return;
    }
    const issue = issueService.getById(workspaceId, issueId);
    if (!issue) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    const comment = issueCommentService.createIssueComment(workspaceId, issueId, {
        senderId: 'user',
        content,
        source: 'user',
        metadata: mentions.length ? { mentions } : undefined,
    });
    if (!comment) {
        res.status(404).json({ error: 'issue not found' });
        return;
    }
    if (issue.status !== 'draft') {
        const updatedIssue = issueService.updateStatus(workspaceId, issueId, 'draft');
        if (updatedIssue) {
            broadcastToWorkspace(workspaceId, 'issue.status_changed', { issueId, from: issue.status, to: updatedIssue.status });
            broadcastToWorkspace(workspaceId, 'issue.updated', updatedIssue);
        }
    }
    res.status(201).json(comment);
    if (!hasActiveIssueAutomation(workspaceId)) {
        const ctx = {
            workspaceId,
            broadcast: (event, data) => broadcastToWorkspace(workspaceId, event, data),
            getSession: (sessionId) => agentService.getById(workspaceId, sessionId),
            updateSessionStatus: (sessionId, status, extra) => agentService.updateStatus(workspaceId, sessionId, status, extra),
        };
        runIssueAutomation(workspaceId, issueId, ctx).catch((err) => {
            console.error(`[issue-comment] automation error for issue ${issueId}:`, err);
        });
    }
});
router.put('/:issueId/comments/:commentId', (req, res) => {
    const content = typeof req.body.content === 'string' ? req.body.content.trim() : '';
    if (!content) {
        res.status(400).json({ error: 'content is required' });
        return;
    }
    const comment = issueCommentService.updateIssueComment(req.params.id, req.params.issueId, req.params.commentId, { content });
    if (!comment) {
        res.status(404).json({ error: 'comment not found' });
        return;
    }
    res.json(comment);
});
router.delete('/:issueId/comments/:commentId', (req, res) => {
    const ok = issueCommentService.deleteIssueComment(req.params.id, req.params.issueId, req.params.commentId);
    if (!ok) {
        res.status(404).json({ error: 'comment not found' });
        return;
    }
    res.status(204).send();
});
export default router;
function normalizeIssueMembers(workspaceId, members) {
    const agentIds = new Set(agentService.listPresets(workspaceId).map((agent) => agent.id));
    const normalized = [];
    const seen = new Set();
    for (const member of members) {
        if (!agentIds.has(member) || seen.has(member))
            continue;
        seen.add(member);
        normalized.push(member);
    }
    return normalized;
}
function mergeWorkflowMembers(members, workflowId) {
    const base = Array.isArray(members)
        ? members.filter((member) => typeof member === 'string' && Boolean(member.trim())).map((member) => member.trim())
        : [];
    if (typeof workflowId !== 'string' || !workflowId.trim())
        return base;
    const workflow = workflowService.getWorkflow(workflowId.trim());
    if (!workflow)
        return base;
    return Array.from(new Set([
        ...base,
        ...workflow.nodes.filter((n) => n.type === 'agent').map((node) => node.data.agentConfigId).filter(Boolean),
    ]));
}
//# sourceMappingURL=issue.js.map