// engine/pm-platform-creators.ts — Platform-specific issue creation for SPEC-023b
// Jira, Linear, GitHub Projects, Trello, Notion via fetch()
/**
 * Derive labels/tags automatically from the spec's type and scope.
 * Criteria 8: labels created automatically based on type and scope.
 * Exported here (canonical location) and re-exported by pm-integrator.ts.
 */
export function generateLabels(spec) {
    const labels = ['sdd', `spec-${spec.id}`, spec.type, spec.scope];
    if (spec.risk !== 'low') {
        labels.push(`risk-${spec.risk}`);
    }
    if (spec.tags.length > 0) {
        labels.push(...spec.tags.slice(0, 5));
    }
    return [...new Set(labels)];
}
// === Jira helpers ===
function buildJiraPayload(spec, config, labels) {
    const priorityMap = {
        critical: 'Highest',
        high: 'High',
        medium: 'Medium',
        low: 'Low',
    };
    return {
        fields: {
            project: { key: config.fieldMapping.projectKey ?? config.projectId },
            summary: spec.title,
            description: {
                type: 'doc',
                version: 1,
                content: [
                    {
                        type: 'paragraph',
                        content: [
                            {
                                type: 'text',
                                text: `SDD Spec: ${spec.id}\nType: ${spec.type}\nScope: ${spec.scope}\nTarget: ${spec.target}`,
                            },
                        ],
                    },
                ],
            },
            issuetype: { name: spec.type === 'bugfix' ? 'Bug' : 'Story' },
            labels,
            priority: { name: priorityMap[spec.risk] ?? 'Medium' },
        },
    };
}
export function buildJiraBaseUrl(config) {
    const domain = config.fieldMapping.domain ?? 'your-domain';
    return config.baseUrl ?? `https://${domain}.atlassian.net/rest/api/3`;
}
export function buildJiraAuthHeader(config) {
    const email = config.fieldMapping.email ?? '';
    return `Basic ${Buffer.from(`${email}:${config.apiKey}`).toString('base64')}`;
}
/**
 * Create an issue in Jira via REST API v3.
 * Criteria 1: integrate_pm can create issues in Jira based on a spec.
 */
export async function createIssueJira(spec, config) {
    const labels = generateLabels(spec);
    const baseUrl = buildJiraBaseUrl(config);
    const payload = buildJiraPayload(spec, config, labels);
    const response = await fetch(`${baseUrl}/issue`, {
        method: 'POST',
        headers: {
            Authorization: buildJiraAuthHeader(config),
            'Content-Type': 'application/json',
            Accept: 'application/json',
        },
        body: JSON.stringify(payload),
        signal: AbortSignal.timeout(10_000),
    });
    if (!response.ok) {
        const text = await response.text();
        throw new Error(`Jira API error ${response.status}: ${text}`);
    }
    const data = (await response.json());
    const domain = config.fieldMapping.domain ?? 'your-domain';
    return {
        platform: 'jira',
        issueId: data.key,
        issueUrl: `https://${domain}.atlassian.net/browse/${data.key}`,
        status: 'created',
        labels,
    };
}
// === Linear integration ===
const LINEAR_CREATE_MUTATION = `
mutation CreateIssue($input: IssueCreateInput!) {
  issueCreate(input: $input) {
    success
    issue { id identifier url }
  }
}`;
/**
 * Create an issue in Linear via GraphQL API.
 * Criteria 2: integrate_pm can create issues in Linear based on a spec.
 */
export async function createIssueLinear(spec, config) {
    const labels = generateLabels(spec);
    const priorityMap = { critical: 1, high: 2, medium: 3, low: 4 };
    const variables = {
        input: {
            teamId: config.projectId,
            title: spec.title,
            description: `**SDD Spec:** ${spec.id}\n**Type:** ${spec.type}\n**Scope:** ${spec.scope}\n**Target:** ${spec.target}\n**Risk:** ${spec.risk}`,
            priority: priorityMap[spec.risk] ?? 3,
        },
    };
    const linearUrl = config.baseUrl ?? 'https://api.linear.app/graphql';
    const response = await fetch(linearUrl, {
        method: 'POST',
        headers: {
            Authorization: config.apiKey,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({ query: LINEAR_CREATE_MUTATION, variables }),
        signal: AbortSignal.timeout(10_000),
    });
    if (!response.ok) {
        const text = await response.text();
        throw new Error(`Linear API error ${response.status}: ${text}`);
    }
    const data = (await response.json());
    if (data.errors && data.errors.length > 0) {
        throw new Error(`Linear GraphQL error: ${data.errors[0]?.message ?? 'unknown'}`);
    }
    const issue = data.data?.issueCreate?.issue;
    if (!issue) {
        throw new Error('Linear API: issue not returned in response');
    }
    return {
        platform: 'linear',
        issueId: issue.identifier,
        issueUrl: issue.url,
        status: 'created',
        labels,
    };
}
// === GitHub Projects integration ===
/**
 * Create an issue in GitHub via REST API.
 * Criteria 3: integrate_pm can create issues in GitHub Projects based on a spec.
 */
export async function createIssueGithubProjects(spec, config) {
    const labels = generateLabels(spec);
    const owner = config.fieldMapping.owner ?? 'owner';
    const repo = config.fieldMapping.repo ?? config.projectId;
    const baseUrl = config.baseUrl ?? 'https://api.github.com';
    const body = [
        `## SDD Spec: ${spec.id}`,
        '',
        `**Type:** ${spec.type}`,
        `**Scope:** ${spec.scope}`,
        `**Target:** ${spec.target}`,
        `**Risk:** ${spec.risk}`,
        `**Difficulty:** ${spec.difficulty}/5`,
        '',
        `### Estimation`,
        `- Dev hours: ${spec.estimation.devHours}`,
        `- Review hours: ${spec.estimation.reviewHours}`,
        `- Total cost: $${spec.estimation.totalCostUsd}`,
        '',
        `---`,
        `_Created by Planu SDD MCP Server_`,
    ].join('\n');
    const response = await fetch(`${baseUrl}/repos/${owner}/${repo}/issues`, {
        method: 'POST',
        headers: {
            Authorization: `Bearer ${config.apiKey}`,
            'Content-Type': 'application/json',
            Accept: 'application/vnd.github+json',
            'X-GitHub-Api-Version': '2022-11-28',
        },
        body: JSON.stringify({
            title: `[${spec.id}] ${spec.title}`,
            body,
            labels: labels.slice(0, 10),
        }),
        signal: AbortSignal.timeout(10_000),
    });
    if (!response.ok) {
        const text = await response.text();
        throw new Error(`GitHub API error ${response.status}: ${text}`);
    }
    const data = (await response.json());
    return {
        platform: 'github-projects',
        issueId: String(data.number),
        issueUrl: data.html_url,
        status: 'created',
        labels,
    };
}
// === Trello integration ===
/**
 * Create a card in Trello via REST API.
 * Criteria 4: integrate_pm can create tasks in Trello based on a spec.
 */
export async function createTaskTrello(spec, config) {
    const labels = generateLabels(spec);
    const baseUrl = config.baseUrl ?? 'https://api.trello.com/1';
    const listId = config.fieldMapping.listId ?? config.projectId;
    const params = new URLSearchParams({
        key: config.fieldMapping.appKey ?? '',
        token: config.apiKey,
        name: `[${spec.id}] ${spec.title}`,
        desc: `SDD Spec: ${spec.id}\nType: ${spec.type}\nScope: ${spec.scope}\nRisk: ${spec.risk}`,
        idList: listId,
    });
    const response = await fetch(`${baseUrl}/cards?${params.toString()}`, {
        method: 'POST',
        headers: { Accept: 'application/json' },
        signal: AbortSignal.timeout(10_000),
    });
    if (!response.ok) {
        const text = await response.text();
        throw new Error(`Trello API error ${response.status}: ${text}`);
    }
    const data = (await response.json());
    return {
        platform: 'trello',
        issueId: data.id,
        issueUrl: data.shortUrl,
        status: 'created',
        labels,
    };
}
// === Notion integration ===
/**
 * Create a page in Notion via REST API.
 * Criteria 5: integrate_pm can create pages in Notion based on a spec.
 */
export async function createPageNotion(spec, config) {
    const labels = generateLabels(spec);
    const baseUrl = config.baseUrl ?? 'https://api.notion.com/v1';
    const payload = {
        parent: { database_id: config.projectId },
        properties: {
            Name: { title: [{ text: { content: spec.title } }] },
            'Spec ID': { rich_text: [{ text: { content: spec.id } }] },
            Type: { select: { name: spec.type } },
            Status: { select: { name: spec.status } },
            Risk: { select: { name: spec.risk } },
            Tags: { multi_select: labels.map((l) => ({ name: l })) },
        },
    };
    const response = await fetch(`${baseUrl}/pages`, {
        method: 'POST',
        headers: {
            Authorization: `Bearer ${config.apiKey}`,
            'Content-Type': 'application/json',
            'Notion-Version': '2022-06-28',
        },
        body: JSON.stringify(payload),
        signal: AbortSignal.timeout(10_000),
    });
    if (!response.ok) {
        const text = await response.text();
        throw new Error(`Notion API error ${response.status}: ${text}`);
    }
    const data = (await response.json());
    return {
        platform: 'notion',
        issueId: data.id,
        issueUrl: data.url,
        status: 'created',
        labels,
    };
}
//# sourceMappingURL=pm-platform-creators.js.map