/**
 * Ticketing Integration
 *
 * Integrates with Jira, ServiceNow, and Linear for creating
 * issues and incidents from certification findings.
 *
 * @module enterprise/integrations/ticketing
 */
/**
 * Jira client
 */
export class JiraClient {
    config;
    constructor(config) {
        this.config = {
            defaultIssueType: "Bug",
            severityMapping: {
                critical: "Highest",
                high: "High",
                medium: "Medium",
                low: "Low",
                info: "Lowest",
            },
            ...config,
        };
    }
    /**
     * Create a Jira issue
     */
    async createTicket(request) {
        const priority = request.priority || this.mapSeverity(request.findings?.[0]?.severity);
        const issueData = {
            fields: {
                project: { key: request.project || this.config.defaultProject },
                summary: request.title,
                description: this.formatDescription(request.description, request.findings),
                issuetype: { name: request.type || this.config.defaultIssueType },
                priority: priority ? { name: priority } : undefined,
                labels: request.labels,
                assignee: request.assignee ? { accountId: request.assignee } : undefined,
                ...this.mapCustomFields(request.customFields),
            },
        };
        const response = await this.request("POST", "/rest/api/3/issue", issueData);
        const data = (await response.json());
        return {
            id: data.id,
            key: data.key,
            url: `${this.config.baseUrl}/browse/${data.key}`,
            platform: "jira",
            createdAt: new Date().toISOString(),
        };
    }
    /**
     * Add comment to existing issue
     */
    async addComment(issueKey, comment) {
        await this.request("POST", `/rest/api/3/issue/${issueKey}/comment`, {
            body: {
                type: "doc",
                version: 1,
                content: [
                    {
                        type: "paragraph",
                        content: [{ type: "text", text: comment }],
                    },
                ],
            },
        });
    }
    /**
     * Attach findings report to issue
     */
    async attachFile(issueKey, filename, content) {
        const boundary = "----VasperaFormBoundary";
        const body = [
            `--${boundary}`,
            `Content-Disposition: form-data; name="file"; filename="${filename}"`,
            "Content-Type: application/json",
            "",
            content,
            `--${boundary}--`,
        ].join("\r\n");
        await fetch(`${this.config.baseUrl}/rest/api/3/issue/${issueKey}/attachments`, {
            method: "POST",
            headers: {
                Authorization: this.getAuthHeader(),
                "Content-Type": `multipart/form-data; boundary=${boundary}`,
                "X-Atlassian-Token": "no-check",
            },
            body,
        });
    }
    /**
     * Search for existing issues
     */
    async searchIssues(jql) {
        const response = await this.request("GET", `/rest/api/3/search?jql=${encodeURIComponent(jql)}&fields=summary`);
        const data = (await response.json());
        return data.issues.map((i) => ({
            key: i.key,
            summary: i.fields.summary,
        }));
    }
    /**
     * Make Jira API request
     */
    async request(method, path, body) {
        const response = await fetch(`${this.config.baseUrl}${path}`, {
            method,
            headers: {
                Authorization: this.getAuthHeader(),
                "Content-Type": "application/json",
                Accept: "application/json",
            },
            body: body ? JSON.stringify(body) : undefined,
        });
        if (!response.ok) {
            const error = await response.text();
            throw new Error(`Jira API error: ${response.status} - ${error}`);
        }
        return response;
    }
    /**
     * Get authorization header
     */
    getAuthHeader() {
        if (this.config.isCloud) {
            const credentials = Buffer.from(`${this.config.email}:${this.config.apiToken}`).toString("base64");
            return `Basic ${credentials}`;
        }
        return `Bearer ${this.config.apiToken}`;
    }
    /**
     * Map severity to Jira priority
     */
    mapSeverity(severity) {
        if (!severity)
            return undefined;
        return this.config.severityMapping?.[severity];
    }
    /**
     * Format description with findings
     */
    formatDescription(description, findings) {
        let content = description;
        if (findings && findings.length > 0) {
            content += "\n\n## Findings\n\n";
            for (const f of findings) {
                content += `- **${f.id}** (${f.severity}): ${f.description}\n`;
            }
        }
        content += `\n\n---\n_Generated by Vaspera Hardening MCP_`;
        // Return Atlassian Document Format for Jira Cloud
        return {
            type: "doc",
            version: 1,
            content: [
                {
                    type: "paragraph",
                    content: [{ type: "text", text: content }],
                },
            ],
        };
    }
    /**
     * Map custom fields
     */
    mapCustomFields(fields) {
        if (!fields || !this.config.customFields)
            return {};
        const mapped = {};
        for (const [key, value] of Object.entries(fields)) {
            const fieldId = this.config.customFields[key] || key;
            mapped[fieldId] = value;
        }
        return mapped;
    }
}
/**
 * ServiceNow client
 */
export class ServiceNowClient {
    config;
    constructor(config) {
        this.config = {
            table: "incident",
            severityMapping: {
                critical: "1",
                high: "2",
                medium: "3",
                low: "4",
                info: "5",
            },
            ...config,
        };
    }
    /**
     * Create ServiceNow incident/record
     */
    async createTicket(request) {
        const priority = request.priority || this.mapSeverity(request.findings?.[0]?.severity);
        const recordData = {
            short_description: request.title,
            description: this.formatDescription(request.description, request.findings),
            priority,
            assignment_group: request.project || this.config.defaultProject,
            assigned_to: request.assignee,
            ...request.customFields,
        };
        const response = await this.request("POST", `/api/now/table/${this.config.table}`, recordData);
        const data = (await response.json());
        return {
            id: data.result.sys_id,
            key: data.result.number,
            url: `${this.config.baseUrl}/nav_to.do?uri=incident.do?sys_id=${data.result.sys_id}`,
            platform: "servicenow",
            createdAt: new Date().toISOString(),
        };
    }
    /**
     * Add work note to incident
     */
    async addWorkNote(sysId, note) {
        await this.request("PATCH", `/api/now/table/${this.config.table}/${sysId}`, {
            work_notes: note,
        });
    }
    /**
     * Update incident state
     */
    async updateState(sysId, state) {
        await this.request("PATCH", `/api/now/table/${this.config.table}/${sysId}`, {
            state,
        });
    }
    /**
     * Make ServiceNow API request
     */
    async request(method, path, body) {
        const response = await fetch(`${this.config.baseUrl}${path}`, {
            method,
            headers: {
                Authorization: `Bearer ${this.config.apiToken}`,
                "Content-Type": "application/json",
                Accept: "application/json",
            },
            body: body ? JSON.stringify(body) : undefined,
        });
        if (!response.ok) {
            const error = await response.text();
            throw new Error(`ServiceNow API error: ${response.status} - ${error}`);
        }
        return response;
    }
    /**
     * Map severity to ServiceNow priority
     */
    mapSeverity(severity) {
        if (!severity)
            return undefined;
        return this.config.severityMapping?.[severity];
    }
    /**
     * Format description
     */
    formatDescription(description, findings) {
        let content = description;
        if (findings && findings.length > 0) {
            content += "\n\nFindings:\n";
            for (const f of findings) {
                content += `- ${f.id} (${f.severity}): ${f.description}\n`;
            }
        }
        content += `\n\n---\nGenerated by Vaspera Hardening MCP`;
        return content;
    }
}
/**
 * Linear client
 */
export class LinearClient {
    config;
    constructor(config) {
        this.config = {
            severityMapping: {
                critical: "0", // Urgent
                high: "1", // High
                medium: "2", // Medium
                low: "3", // Low
                info: "4", // No priority
            },
            ...config,
            baseUrl: config.baseUrl || "https://api.linear.app",
        };
    }
    /**
     * Create Linear issue
     */
    async createTicket(request) {
        const priority = request.priority
            ? parseInt(request.priority)
            : this.mapSeverityToPriority(request.findings?.[0]?.severity);
        const mutation = `
      mutation CreateIssue($input: IssueCreateInput!) {
        issueCreate(input: $input) {
          success
          issue {
            id
            identifier
            url
          }
        }
      }
    `;
        const variables = {
            input: {
                title: request.title,
                description: this.formatDescription(request.description, request.findings),
                teamId: request.project || this.config.teamId || this.config.defaultProject,
                priority,
                labelIds: request.labels,
                assigneeId: request.assignee,
            },
        };
        const response = await this.graphql(mutation, variables);
        const issue = response.issueCreate?.issue;
        if (!issue) {
            throw new Error("Failed to create Linear issue");
        }
        return {
            id: issue.id,
            key: issue.identifier,
            url: issue.url,
            platform: "linear",
            createdAt: new Date().toISOString(),
        };
    }
    /**
     * Add comment to issue
     */
    async addComment(issueId, comment) {
        const mutation = `
      mutation CreateComment($input: CommentCreateInput!) {
        commentCreate(input: $input) {
          success
        }
      }
    `;
        await this.graphql(mutation, {
            input: {
                issueId,
                body: comment,
            },
        });
    }
    /**
     * Get team ID by name
     */
    async getTeamId(teamName) {
        const query = `
      query Teams {
        teams {
          nodes {
            id
            name
          }
        }
      }
    `;
        const response = await this.graphql(query, {});
        const team = response.teams?.nodes?.find((t) => t.name === teamName);
        return team?.id || null;
    }
    /**
     * Make GraphQL request to Linear API
     */
    async graphql(query, variables) {
        const response = await fetch(`${this.config.baseUrl}/graphql`, {
            method: "POST",
            headers: {
                Authorization: this.config.apiToken,
                "Content-Type": "application/json",
            },
            body: JSON.stringify({ query, variables }),
        });
        if (!response.ok) {
            throw new Error(`Linear API error: ${response.status}`);
        }
        const data = (await response.json());
        if (data.errors) {
            throw new Error(`Linear GraphQL error: ${data.errors[0].message}`);
        }
        return data.data || {};
    }
    /**
     * Map severity to Linear priority
     */
    mapSeverityToPriority(severity) {
        if (!severity)
            return 4;
        const mapping = this.config.severityMapping;
        return parseInt(mapping?.[severity] || "4");
    }
    /**
     * Format description with findings
     */
    formatDescription(description, findings) {
        let content = description;
        if (findings && findings.length > 0) {
            content += "\n\n## Findings\n\n";
            for (const f of findings) {
                content += `- **${f.id}** (${f.severity}): ${f.description}\n`;
            }
        }
        content += `\n\n---\n_Generated by Vaspera Hardening MCP_`;
        return content;
    }
}
// ============================================================================
// Unified Ticketing Client
// ============================================================================
/**
 * Unified ticketing client
 */
export class TicketingClient {
    client;
    platform;
    constructor(config) {
        this.platform = config.platform;
        switch (config.platform) {
            case "jira":
                this.client = new JiraClient(config);
                break;
            case "servicenow":
                this.client = new ServiceNowClient(config);
                break;
            case "linear":
                this.client = new LinearClient(config);
                break;
            default:
                throw new Error(`Unsupported ticketing platform: ${config.platform}`);
        }
    }
    /**
     * Create a ticket
     */
    async createTicket(request) {
        return this.client.createTicket(request);
    }
    /**
     * Create tickets for findings
     */
    async createTicketsForFindings(findings, options) {
        const created = [];
        const failed = [];
        // Filter by severity
        const severityOrder = ["critical", "high", "medium", "low", "info"];
        const minIndex = severityOrder.indexOf(options?.minSeverity || "info");
        const filteredFindings = findings.filter((f) => severityOrder.indexOf(f.severity) <= minIndex);
        if (options?.groupByCategory) {
            // Group findings by category
            const byCategory = new Map();
            for (const f of filteredFindings) {
                const group = byCategory.get(f.category) || [];
                group.push(f);
                byCategory.set(f.category, group);
            }
            for (const [category, categoryFindings] of byCategory) {
                const maxSeverity = categoryFindings.reduce((max, f) => severityOrder.indexOf(f.severity) < severityOrder.indexOf(max)
                    ? f.severity
                    : max, "info");
                const request = {
                    title: `[${maxSeverity.toUpperCase()}] ${category}: ${categoryFindings.length} finding(s)`,
                    description: `Security findings in category: ${category}`,
                    project: options?.project,
                    findings: categoryFindings,
                };
                try {
                    const ticket = await this.createTicket(request);
                    created.push(ticket);
                }
                catch (error) {
                    failed.push({
                        request,
                        error: error instanceof Error ? error.message : String(error),
                    });
                }
            }
        }
        else {
            // Create one ticket per finding
            for (const finding of filteredFindings) {
                const request = {
                    title: `[${finding.severity.toUpperCase()}] ${finding.id}: ${finding.description.slice(0, 100)}`,
                    description: finding.description,
                    project: options?.project,
                    findings: [finding],
                };
                try {
                    const ticket = await this.createTicket(request);
                    created.push(ticket);
                }
                catch (error) {
                    failed.push({
                        request,
                        error: error instanceof Error ? error.message : String(error),
                    });
                }
            }
        }
        return { created, failed };
    }
    /**
     * Get platform name
     */
    getPlatform() {
        return this.platform;
    }
}
// ============================================================================
// Factory Functions
// ============================================================================
/**
 * Create ticketing client
 */
export function createTicketingClient(config) {
    return new TicketingClient(config);
}
/**
 * Create Jira client
 */
export function createJiraClient(config) {
    return new JiraClient({ ...config, platform: "jira" });
}
/**
 * Create ServiceNow client
 */
export function createServiceNowClient(config) {
    return new ServiceNowClient({ ...config, platform: "servicenow" });
}
/**
 * Create Linear client
 */
export function createLinearClient(config) {
    return new LinearClient({ ...config, platform: "linear" });
}
//# sourceMappingURL=ticketing.js.map