/**
 * CopilotWorkspaceIdeAdapter -- GitHub Copilot Workspace IDE adapter.
 *
 * Copilot Workspace (Technical Preview as of 2026-04-22) does not expose
 * runtime per-tool hooks. AI governance fires exclusively at the PR
 * boundary: a GitHub App intercepts PRs authored by
 * `copilot-workspace[bot]`, runs diff evaluation against the active
 * compliance pack plus a secret scanner, and posts a check-run result.
 *
 * Architecture:
 *   - Runtime methods (interceptToolInvocation, recordToolResult,
 *     routeToolToReviewer) throw IdeUnsupportedError with an explicit
 *     message directing callers to evaluatePrDiff() instead. This is
 *     AC 9 -- explicit deferral, not silent skip.
 *   - PrBoundaryEnforcer is fully implemented:
 *       evaluatePrDiff(ctx)     -- diff evaluation + secret scan
 *       postCheckRun(ctx, dec)  -- emits canonical events + stubs
 *                                  the GitHub Checks API call shape
 *   - User identity resolved from ctx.webhookPayload.sender.login.
 *     Missing sender -> neutral check-run (AC 12, fail-closed).
 *   - Bot-author gate: only acts on PRs by `copilot-workspace[bot]`.
 *     Other authors produce a DEBUG no-op.
 *
 * DEFERRED-COPILOT-WS-GITHUB-APP: The real GitHub App delivery (App
 * private key, installation token flow, actual POST to the GitHub
 * Checks API) is stubbed at the _postCheckRunToGitHub() method
 * boundary. Wire the real implementation when the GitHub App is
 * provisioned. Add a KANBAN task to track.
 *
 * TESTED_API_VERSION = '2024-11-01' (GitHub Checks API version,
 * spec-aligned). Verify at implementation of the real GitHub App
 * delivery against the current GitHub REST API version.
 *
 * Spec: P3-IDE-01 AC 9-12, Section 2.4
 *
 * @connexum/ai-governance
 */
import { BaseIdeAdapter } from './base.js';
import { IdeUnsupportedError, IdeAttributionError, } from './index.js';
import { buildIdeAuditEvent, scanDiffForSecrets } from './shared.js';
// ---------------------------------------------------------------------------
// GitHub Checks API version pinning (spec-aligned AC 11)
// ---------------------------------------------------------------------------
/**
 * GitHub Checks API version this adapter is aligned with.
 * Used in the check-run stub payload (Accept header).
 *
 * DEFERRED-COPILOT-WS-GITHUB-APP: Verify against the GitHub REST API
 * version at time of real GitHub App provisioning.
 */
export const COPILOT_WORKSPACE_TESTED_API_VERSION = '2024-11-01';
// ---------------------------------------------------------------------------
// Bot identity (AC 9) -- the only PR author this adapter governs
// ---------------------------------------------------------------------------
const COPILOT_WORKSPACE_BOT_AUTHOR = 'copilot-workspace[bot]';
// ---------------------------------------------------------------------------
// CopilotWorkspaceIdeAdapter
// ---------------------------------------------------------------------------
export class CopilotWorkspaceIdeAdapter extends BaseIdeAdapter {
    adapterId = 'copilot-workspace';
    ideId = 'copilot-workspace';
    copilotConfig = null;
    // ---------------------------------------------------------------------------
    // Lifecycle
    // ---------------------------------------------------------------------------
    initialize(config) {
        super.initialize(config);
        this.copilotConfig = config;
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: resolveUserIdentity
    //
    // Copilot Workspace identity comes from the GitHub webhook payload.
    // This method resolves from the session context only -- the primary
    // resolution path is inside evaluatePrDiff() where the webhook payload
    // is available. A direct call resolves from session.identity or fails.
    // ---------------------------------------------------------------------------
    async resolveUserIdentity(session) {
        this.ensureInitialized();
        const fromSession = session.identity?.userId?.trim();
        const githubLogin = session.identity?.githubLogin?.trim();
        if (fromSession || githubLogin) {
            const identity = {
                userId: fromSession ?? githubLogin,
                githubLogin: githubLogin,
                orgId: session.identity?.orgId,
                attributionMethod: 'github-webhook',
            };
            this.emitIdentityResolved(identity.userId, session.clientSessionId);
            return identity;
        }
        const allowAnon = this.config?.allowAnonymousIdeSessions === true;
        if (allowAnon) {
            const anon = { userId: 'anonymous', attributionMethod: 'github-webhook' };
            this.emitIdentityResolved(anon.userId, session.clientSessionId);
            return anon;
        }
        this.emitIdentityUnresolved(session.clientSessionId, 'CopilotWorkspaceIdeAdapter: no user identity in session context.');
        throw new IdeAttributionError({
            message: 'CopilotWorkspaceIdeAdapter: cannot resolve user identity from session ' +
                'context. Identity is normally resolved from the GitHub webhook payload ' +
                'during evaluatePrDiff(). Pass identity in session context or set ' +
                'allowAnonymousIdeSessions: true.',
            ideId: this.ideId,
            adapterId: this.adapterId,
        });
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: runtime methods -- throw IdeUnsupportedError (AC 9)
    //
    // Copilot Workspace operates at PR boundary only. These surfaces do not
    // apply. Explicit deferral, not silent skip.
    // ---------------------------------------------------------------------------
    async interceptToolInvocation(_ctx) {
        throw new IdeUnsupportedError({
            message: 'CopilotWorkspaceIdeAdapter: interceptToolInvocation() is not supported. ' +
                'Copilot Workspace operates at PR boundary only. Use evaluatePrDiff() ' +
                'instead. The adapter is delivered as a GitHub App that intercepts ' +
                'PR-opened and PR-synchronised webhooks.',
            ideId: this.ideId,
            adapterId: this.adapterId,
            surface: 'interceptToolInvocation',
        });
    }
    async recordToolResult(_ctx, _result) {
        throw new IdeUnsupportedError({
            message: 'CopilotWorkspaceIdeAdapter: recordToolResult() is not supported. ' +
                'Copilot Workspace operates at PR boundary only. Use evaluatePrDiff() ' +
                'and postCheckRun() instead.',
            ideId: this.ideId,
            adapterId: this.adapterId,
            surface: 'recordToolResult',
        });
    }
    async routeToolToReviewer(_tool, _ctx, _tier) {
        throw new IdeUnsupportedError({
            message: 'CopilotWorkspaceIdeAdapter: routeToolToReviewer() is not supported. ' +
                'Copilot Workspace operates at PR boundary only. Use evaluatePrDiff() ' +
                'instead. PR-level reviewer routing is handled via check-run status.',
            ideId: this.ideId,
            adapterId: this.adapterId,
            surface: 'routeToolToReviewer',
        });
    }
    // ---------------------------------------------------------------------------
    // PrBoundaryEnforcer: evaluatePrDiff (AC 10)
    // ---------------------------------------------------------------------------
    /**
     * Evaluate a PR diff against the active compliance pack, run the secret
     * scanner, and run license-violation pattern matching.
     *
     * User identity is resolved from ctx.webhookPayload.sender.login.
     * If missing, returns neutral (AC 12 -- fail-closed, not silent-approve).
     *
     * Bot-author gate: if the PR author is not copilot-workspace[bot], returns
     * a neutral decision with a DEBUG audit event. This adapter only governs
     * Copilot Workspace PRs.
     *
     * @param ctx - PR diff context including the unified diff and webhook payload
     */
    async evaluatePrDiff(ctx) {
        this.ensureInitialized();
        const payload = ctx.webhookPayload;
        // 1. Bot-author gate (AC 9) -- only govern Copilot Workspace PRs
        const prAuthor = payload?.pull_request?.user?.login ??
            (payload?.sender?.login === COPILOT_WORKSPACE_BOT_AUTHOR
                ? COPILOT_WORKSPACE_BOT_AUTHOR
                : undefined);
        if (prAuthor && prAuthor !== COPILOT_WORKSPACE_BOT_AUTHOR) {
            this.emit(buildIdeAuditEvent('ide:pr-check-neutral', {
                ideId: this.ideId,
                adapterId: this.adapterId,
                detail: `PR author '${prAuthor}' is not '${COPILOT_WORKSPACE_BOT_AUTHOR}'. ` +
                    'CopilotWorkspaceIdeAdapter only governs Copilot Workspace PRs. No-op.',
                data: { prAuthor, prUrl: ctx.prUrl },
            }));
            return {
                status: 'neutral',
                reason: `PR author '${prAuthor}' is not ${COPILOT_WORKSPACE_BOT_AUTHOR}. No governance action.`,
            };
        }
        // 2. User identity resolution (AC 12)
        const senderLogin = payload?.sender?.login;
        const orgLogin = payload?.organization?.login;
        if (!senderLogin) {
            this.emit(buildIdeAuditEvent('ide:user-identity-unresolved', {
                ideId: this.ideId,
                adapterId: this.adapterId,
                detail: 'CopilotWorkspaceIdeAdapter: webhook payload missing sender.login. ' +
                    'Check-run set to neutral (unresolved-user-identity). No silent approve.',
                data: { prUrl: ctx.prUrl, prNumber: ctx.prNumber },
            }));
            return {
                status: 'neutral',
                reason: 'unresolved-user-identity',
                violations: [{
                        rule: 'identity-resolution',
                        detail: 'PR webhook payload did not include a resolvable sender.login.',
                    }],
            };
        }
        // Identity resolved -- emit debug event
        const resolvedIdentity = {
            userId: senderLogin,
            githubLogin: senderLogin,
            orgId: orgLogin,
            attributionMethod: 'github-webhook',
        };
        this.emitIdentityResolved(resolvedIdentity.userId);
        // 3. Secret scanner (AC 10)
        const secretFindings = scanDiffForSecrets(ctx.diff);
        const violations = [];
        for (const finding of secretFindings) {
            violations.push({
                rule: `secret-scanner:${finding.pattern}`,
                detail: `Potential secret detected at diff line ${finding.line}: ` +
                    `${finding.pattern}. Excerpt: ${finding.excerpt}`,
            });
        }
        // 4. License-violation pattern scan (AC 10)
        const licensePatterns = this.copilotConfig?.licenseViolationPatterns ?? [];
        if (licensePatterns.length > 0) {
            const addedLines = ctx.diff
                .split('\n')
                .filter((l) => l.startsWith('+') && !l.startsWith('+++'));
            addedLines.forEach((line, idx) => {
                for (const pattern of licensePatterns) {
                    if (pattern.test(line)) {
                        violations.push({
                            rule: `license-policy:${pattern.source}`,
                            detail: `License policy violation at added line ${idx + 1}: ${line.slice(0, 120)}`,
                        });
                        break;
                    }
                }
            });
        }
        // 5. Compliance pack evaluation (stub -- real pack evaluation wired via
        //    the governance runtime when evaluatePrDiff is called from the App).
        //    Currently evaluates pass/fail based on violations found.
        if (violations.length > 0) {
            return {
                status: 'failed',
                reason: `${violations.length} violation(s) found in PR diff.`,
                violations,
            };
        }
        return {
            status: 'passed',
            reason: 'No violations found. Diff is compliant.',
        };
    }
    // ---------------------------------------------------------------------------
    // PrBoundaryEnforcer: postCheckRun (AC 10)
    // ---------------------------------------------------------------------------
    /**
     * Emit canonical audit events for the check-run decision and produce the
     * stubbed GitHub Checks API payload.
     *
     * The actual HTTP call to POST /repos/{owner}/{repo}/check-runs is stubbed
     * at _postCheckRunToGitHub(). Wire the real GitHub App delivery there.
     *
     * DEFERRED-COPILOT-WS-GITHUB-APP: Implement _postCheckRunToGitHub() with
     * real GitHub App private key + installation token flow.
     */
    async postCheckRun(ctx, decision) {
        this.ensureInitialized();
        // Emit canonical check-run event based on decision status
        let eventType;
        switch (decision.status) {
            case 'passed':
                eventType = 'ide:pr-check-passed';
                break;
            case 'failed':
                eventType = 'ide:pr-check-failed';
                break;
            case 'neutral':
            default:
                eventType = 'ide:pr-check-neutral';
                break;
        }
        this.emit(buildIdeAuditEvent(eventType, {
            ideId: this.ideId,
            adapterId: this.adapterId,
            detail: decision.reason,
            data: {
                prUrl: ctx.prUrl,
                prNumber: ctx.prNumber,
                headSha: ctx.headSha,
                status: decision.status,
                violations: decision.violations,
            },
        }));
        // Also emit check-created so the audit chain has a complete lifecycle
        this.emit(buildIdeAuditEvent('ide:pr-check-created', {
            ideId: this.ideId,
            adapterId: this.adapterId,
            detail: `Check-run created for PR #${ctx.prNumber} on ${ctx.repositoryFullName}`,
            data: { prUrl: ctx.prUrl, headSha: ctx.headSha },
        }));
        // Build and stub the GitHub Checks API payload
        const payload = this._buildCheckRunPayload(ctx, decision);
        await this._postCheckRunToGitHub(payload);
    }
    // ---------------------------------------------------------------------------
    // Capabilities
    // ---------------------------------------------------------------------------
    getIdeCapabilities() {
        return {
            runtimeHooks: false,
            prBoundary: true,
            userIdentity: true,
            reviewerRouting: false,
            integrationMode: 'github-app-pr-boundary',
        };
    }
    // ---------------------------------------------------------------------------
    // Internal: GitHub Checks API payload builder
    // ---------------------------------------------------------------------------
    /**
     * Build the shape of the payload that would be POSTed to
     * POST /repos/{owner}/{repo}/check-runs
     *
     * This is the stub boundary for DEFERRED-COPILOT-WS-GITHUB-APP.
     * The payload shape matches the GitHub Checks API 2024-11-01 contract.
     */
    _buildCheckRunPayload(ctx, decision) {
        const conclusionMap = {
            passed: 'success',
            failed: 'failure',
            neutral: 'neutral',
        };
        const violationSummary = decision.violations && decision.violations.length > 0
            ? decision.violations.map((v) => `- [${v.rule}] ${v.detail}`).join('\n')
            : 'No violations found.';
        return {
            name: '@connexum/ai-governance',
            head_sha: ctx.headSha,
            status: 'completed',
            conclusion: conclusionMap[decision.status] ?? 'neutral',
            output: {
                title: `Governance check: ${decision.status}`,
                summary: `**${decision.reason}**\n\n` +
                    `Pack: ${this.config?.packId ?? 'unbound'}\n` +
                    `PR: ${ctx.prUrl}\n\n` +
                    `Violations:\n${violationSummary}`,
            },
            _apiVersion: COPILOT_WORKSPACE_TESTED_API_VERSION,
            _installationId: this.copilotConfig?.githubAppInstallationId,
        };
    }
    /**
     * Stub for the real GitHub Checks API call.
     *
     * DEFERRED-COPILOT-WS-GITHUB-APP: Replace this stub with the real
     * GitHub App installation token flow and POST to
     * /repos/{owner}/{repo}/check-runs
     *
     * Returns the payload shape that would be sent. Callers can inspect
     * this in tests to verify correctness without making live API calls.
     */
    async _postCheckRunToGitHub(payload) {
        // STUB: no real HTTP call. In production:
        // 1. Obtain installation token via GitHub App JWT
        // 2. POST to https://api.github.com/repos/{owner}/{repo}/check-runs
        //    with Authorization: token <installation_token>
        //    and Accept: application/vnd.github+json
        //    and X-GitHub-Api-Version: COPILOT_WORKSPACE_TESTED_API_VERSION
        return payload;
    }
}
//# sourceMappingURL=copilot-workspace.js.map