import * as gitDiffParser from 'gitdiff-parser';
/**
 * Parses unified Git diffs into Heddle's review model.
 *
 * This class owns the boundary between the third-party diff parser shape and
 * the stable review data consumed by server/control-plane presenters.
 */
export class ReviewDiffParser {
    static parser = gitDiffParser.default?.parse
        ?? gitDiffParser.parse;
    static parseUnifiedDiffFiles(diff) {
        const normalized = diff.trim();
        if (!normalized || !normalized.includes('diff --git ')) {
            return [];
        }
        let parsed;
        try {
            parsed = ReviewDiffParser.parser ? ReviewDiffParser.parser(normalized) : [];
        }
        catch {
            return [];
        }
        const chunks = ReviewDiffParser.splitUnifiedDiffChunks(normalized);
        return parsed.map((file) => ReviewDiffParser.projectParsedFile(file, chunks));
    }
    static splitUnifiedDiffChunks(diff) {
        return diff
            .split(/\n(?=diff --git )/)
            .map((chunk) => chunk.trim())
            .filter(Boolean);
    }
    static projectParsedFile(file, chunks) {
        const patch = ReviewDiffParser.findUnifiedDiffChunkForFile(chunks, file) ?? ReviewDiffParser.renderParsedGitDiffFile(file);
        const hunks = file.hunks.map((hunk) => ({
            header: hunk.content,
            lines: hunk.changes.map(ReviewDiffParser.projectDiffLine),
        }));
        const counts = ReviewDiffParser.countDiffLines(hunks);
        return {
            path: ReviewDiffParser.pathForParsedGitDiffFile(file),
            oldPath: ReviewDiffParser.oldPathForParsedGitDiffFile(file),
            status: ReviewDiffParser.statusFromParsedGitDiffFile(file),
            patch,
            binary: file.isBinary === true || /Binary files .* differ/.test(patch),
            additions: counts.additions,
            deletions: counts.deletions,
            hunks,
        };
    }
    static findUnifiedDiffChunkForFile(chunks, file) {
        const candidates = [
            `diff --git a/${file.oldPath} b/${file.newPath}`,
            `rename from ${file.oldPath}`,
            `rename to ${file.newPath}`,
            `--- a/${file.oldPath}`,
            `+++ b/${file.newPath}`,
        ];
        return chunks.find((chunk) => candidates.some((candidate) => chunk.includes(candidate)));
    }
    static renderParsedGitDiffFile(file) {
        const oldPath = file.type === 'add' ? '/dev/null' : `a/${file.oldPath}`;
        const newPath = file.type === 'delete' ? '/dev/null' : `b/${file.newPath}`;
        const lines = [
            `diff --git a/${file.oldPath} b/${file.newPath}`,
            `--- ${oldPath}`,
            `+++ ${newPath}`,
        ];
        for (const hunk of file.hunks) {
            lines.push(hunk.content);
            for (const change of hunk.changes) {
                lines.push(change.content);
            }
        }
        return lines.join('\n');
    }
    static pathForParsedGitDiffFile(file) {
        return file.type === 'delete' ? file.oldPath : file.newPath;
    }
    static oldPathForParsedGitDiffFile(file) {
        if (file.type === 'add' || file.type === 'delete') {
            return undefined;
        }
        if (file.oldPath === file.newPath) {
            return undefined;
        }
        return file.oldPath;
    }
    static statusFromParsedGitDiffFile(file) {
        if (file.oldPath !== file.newPath && file.type === 'modify') {
            return 'renamed';
        }
        switch (file.type) {
            case 'add':
                return 'added';
            case 'delete':
                return 'deleted';
            case 'rename':
                return 'renamed';
            case 'copy':
                return 'copied';
            case 'modify':
                return 'modified';
            default:
                return 'unknown';
        }
    }
    static projectDiffLine(change) {
        return {
            type: ReviewDiffParser.lineTypeFromChange(change),
            content: change.content,
            oldLineNumber: 'oldLineNumber' in change && typeof change.oldLineNumber === 'number' ? change.oldLineNumber : undefined,
            newLineNumber: 'newLineNumber' in change && typeof change.newLineNumber === 'number' ? change.newLineNumber : undefined,
        };
    }
    static lineTypeFromChange(change) {
        switch (change.type) {
            case 'insert':
                return 'added';
            case 'delete':
                return 'deleted';
            case 'normal':
                return 'context';
            default:
                return 'unknown';
        }
    }
    static countDiffLines(hunks) {
        let additions = 0;
        let deletions = 0;
        for (const hunk of hunks) {
            for (const line of hunk.lines) {
                if (line.type === 'added') {
                    additions += 1;
                }
                if (line.type === 'deleted') {
                    deletions += 1;
                }
            }
        }
        return { additions, deletions };
    }
}
//# sourceMappingURL=service.js.map