/**
 * Run Code Review Use Case
 *
 * Orchestrates the full code review lifecycle:
 *   1. Parse PR identifier (number or URL)
 *   2. Fetch PR metadata and diff (local git or GitHub API)
 *   3. Annotate diff with line numbers
 *   4. Build reviewer prompt with XML boundary tags
 *   5. Invoke IStructuredAgentCaller for structured review output
 *   6. Parse and validate comments against diff ranges
 *   7. Persist CodeReview entity with status tracking
 *
 * Status transitions: Pending → InProgress → Completed | Failed
 */
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { injectable, inject } from 'tsyringe';
import { randomUUID } from 'node:crypto';
import { readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { CodeReviewStatus } from '../../../domain/generated/output.js';
/**
 * Parse a GitHub PR URL into owner, repo, and PR number.
 * Supports: https://github.com/owner/repo/pull/42
 */
export function parsePrUrl(url) {
    const match = url.match(/(?:https?:\/\/)?(?:www\.)?github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/i);
    if (!match)
        return null;
    return {
        owner: match[1],
        repo: match[2],
        prNumber: parseInt(match[3], 10),
    };
}
// ---------------------------------------------------------------------------
// Diff validation
// ---------------------------------------------------------------------------
/**
 * Build a set of valid line numbers per file per side from parsed diffs.
 */
export function buildValidLineMap(diffs) {
    const map = new Map();
    for (const diff of diffs) {
        const entry = { left: new Set(), right: new Set() };
        for (const hunk of diff.hunks) {
            for (const line of hunk.lines) {
                if (line.type === 'added' && line.newNumber !== undefined) {
                    entry.right.add(line.newNumber);
                }
                else if (line.type === 'removed' && line.oldNumber !== undefined) {
                    entry.left.add(line.oldNumber);
                }
                else if (line.type === 'context') {
                    if (line.newNumber !== undefined)
                        entry.right.add(line.newNumber);
                    if (line.oldNumber !== undefined)
                        entry.left.add(line.oldNumber);
                }
            }
        }
        map.set(diff.path, entry);
    }
    return map;
}
/**
 * Validate a comment's line against the diff valid-line map.
 */
export function isCommentInDiffRange(comment, validLines) {
    const fileEntry = validLines.get(comment.path);
    if (!fileEntry)
        return false;
    const side = comment.side === 'LEFT' ? 'left' : 'right';
    return fileEntry[side].has(comment.line);
}
// ---------------------------------------------------------------------------
// Review output schema for IStructuredAgentCaller
// ---------------------------------------------------------------------------
const REVIEW_OUTPUT_SCHEMA = {
    type: 'object',
    properties: {
        summary: { type: 'string', description: '2-4 sentence overall assessment' },
        comments: {
            type: 'array',
            items: {
                type: 'object',
                properties: {
                    path: { type: 'string', description: 'file path relative to repo root' },
                    line: { type: 'number', description: 'absolute line number in the file' },
                    body: { type: 'string', description: 'markdown description of the finding' },
                    side: {
                        type: 'string',
                        enum: ['LEFT', 'RIGHT'],
                        description: 'which side of the diff',
                    },
                    suggestion: {
                        type: 'string',
                        description: 'optional replacement code (raw code, no markdown fences)',
                    },
                    startLine: {
                        type: 'number',
                        description: 'optional start line for multi-line comments',
                    },
                },
                required: ['path', 'line', 'body'],
            },
        },
    },
    required: ['summary', 'comments'],
    additionalProperties: false,
};
/** Path to repo-level review guidelines */
const GUIDELINES_FILE = '.shep/review-guidelines.md';
// ---------------------------------------------------------------------------
// Use Case
// ---------------------------------------------------------------------------
let RunCodeReviewUseCase = class RunCodeReviewUseCase {
    codeReviewRepo;
    platformReviewService;
    structuredCaller;
    gitPrService;
    annotateFileDiffs;
    buildReviewPrompt;
    parseReviewOutput;
    constructor(codeReviewRepo, platformReviewService, structuredCaller, gitPrService, annotateFileDiffs, buildReviewPrompt, parseReviewOutput) {
        this.codeReviewRepo = codeReviewRepo;
        this.platformReviewService = platformReviewService;
        this.structuredCaller = structuredCaller;
        this.gitPrService = gitPrService;
        this.annotateFileDiffs = annotateFileDiffs;
        this.buildReviewPrompt = buildReviewPrompt;
        this.parseReviewOutput = parseReviewOutput;
    }
    async execute(input) {
        // 1. Parse target to determine owner/repo/prNumber
        const parsed = this.resolveTarget(input);
        if (!parsed) {
            return {
                ok: false,
                error: `Cannot parse PR target: "${input.target}". Provide a GitHub PR URL or a PR number with --owner and --repo.`,
            };
        }
        const { owner, repo, prNumber } = parsed;
        const now = new Date();
        // 2. Create initial CodeReview entity in Pending status
        const reviewId = randomUUID();
        let review = {
            id: reviewId,
            featureId: input.featureId,
            repositoryPath: input.repositoryPath ?? '',
            prNumber,
            status: CodeReviewStatus.Pending,
            createdAt: now,
            updatedAt: now,
        };
        await this.codeReviewRepo.create(review);
        try {
            // 3. Transition to InProgress
            review = { ...review, status: CodeReviewStatus.InProgress, updatedAt: new Date() };
            await this.codeReviewRepo.update(review);
            // 4. Fetch PR metadata
            const prMetadata = await this.platformReviewService.fetchPrMetadata(owner, repo, prNumber);
            review = { ...review, prUrl: prMetadata.prUrl, updatedAt: new Date() };
            await this.codeReviewRepo.update(review);
            // 5. Fetch diff (local git preferred, fallback to GitHub API)
            let diffs;
            if (input.repositoryPath) {
                try {
                    diffs = await this.gitPrService.getFileDiffs(input.repositoryPath, prMetadata.baseBranch);
                }
                catch {
                    // Local git diff failed — fall back to GitHub API
                    diffs = await this.platformReviewService.fetchPrDiff(owner, repo, prNumber);
                }
            }
            else {
                diffs = await this.platformReviewService.fetchPrDiff(owner, repo, prNumber);
            }
            // 6. Annotate diff with line numbers
            const annotatedDiff = this.annotateFileDiffs(diffs);
            // 7. Fetch existing comments for dedup
            const existingComments = await this.platformReviewService.fetchExistingComments(owner, repo, prNumber);
            // 8. Read guidelines if present
            const guidelines = this.readGuidelines(input.repositoryPath);
            // 9. Build prompt
            const { systemPrompt, userPrompt } = this.buildReviewPrompt({
                prMetadata: {
                    title: prMetadata.title,
                    description: prMetadata.description,
                    baseBranch: prMetadata.baseBranch,
                    headBranch: prMetadata.headBranch,
                    commits: prMetadata.commits,
                },
                annotatedDiff,
                existingComments: existingComments.map((c) => ({
                    path: c.path,
                    line: c.line,
                    body: c.body,
                    author: c.author,
                })),
                guidelines,
            });
            // 10. Invoke structured agent caller
            const fullPrompt = `${systemPrompt}\n\n${userPrompt}`;
            const agentResult = await this.structuredCaller.call(fullPrompt, REVIEW_OUTPUT_SCHEMA, {
                maxTurns: 20,
                silent: true,
            });
            // 11. Parse output (additional validation on top of structured caller)
            const reviewOutput = this.parseReviewOutput(JSON.stringify(agentResult));
            // 12. Validate comments against diff ranges
            const validLines = buildValidLineMap(diffs);
            const reviewComments = reviewOutput.comments.map((c) => {
                const inRange = isCommentInDiffRange(c, validLines);
                return {
                    path: c.path,
                    line: c.line,
                    body: c.body,
                    side: (c.side ?? 'RIGHT'),
                    suggestion: c.suggestion,
                    startLine: c.startLine,
                    inDiffRange: inRange,
                };
            });
            // 13. Update review with results — status Completed
            review = {
                ...review,
                status: CodeReviewStatus.Completed,
                summary: reviewOutput.summary,
                comments: reviewComments,
                updatedAt: new Date(),
            };
            await this.codeReviewRepo.update(review);
            return { ok: true, review };
        }
        catch (error) {
            // Handle failure — update status to Failed
            const errorMessage = error instanceof Error ? error.message : String(error);
            review = {
                ...review,
                status: CodeReviewStatus.Failed,
                errorMessage,
                updatedAt: new Date(),
            };
            await this.codeReviewRepo.update(review);
            return { ok: false, error: errorMessage };
        }
    }
    /**
     * Resolve the target string into owner/repo/prNumber.
     * Supports: PR URL or PR number with explicit owner/repo.
     */
    resolveTarget(input) {
        // Try PR URL first
        const urlParsed = parsePrUrl(input.target);
        if (urlParsed)
            return urlParsed;
        // Try plain number with explicit owner/repo
        const num = parseInt(input.target, 10);
        if (!isNaN(num) && num > 0 && input.owner && input.repo) {
            return { owner: input.owner, repo: input.repo, prNumber: num };
        }
        return null;
    }
    /**
     * Read repository-level review guidelines from .shep/review-guidelines.md.
     */
    readGuidelines(repositoryPath) {
        if (!repositoryPath)
            return undefined;
        const guidelinesPath = join(repositoryPath, GUIDELINES_FILE);
        try {
            if (existsSync(guidelinesPath)) {
                const content = readFileSync(guidelinesPath, 'utf-8');
                return content.trim() || undefined;
            }
        }
        catch {
            // File not readable — skip guidelines
        }
        return undefined;
    }
};
RunCodeReviewUseCase = __decorate([
    injectable(),
    __param(0, inject('ICodeReviewRepository')),
    __param(1, inject('IPlatformReviewService')),
    __param(2, inject('IStructuredAgentCaller')),
    __param(3, inject('IGitPrService')),
    __param(4, inject('DiffAnnotator')),
    __param(5, inject('PromptBuilder')),
    __param(6, inject('OutputParser')),
    __metadata("design:paramtypes", [Object, Object, Object, Object, Function, Function, Function])
], RunCodeReviewUseCase);
export { RunCodeReviewUseCase };
