import {createTwoFilesPatch,parsePatch} from 'diff';
import {parse,html} from 'diff2html';
export const LIMIT=131072;
export function validateText(s){if(typeof s!=='string'||new TextEncoder().encode(s).length>LIMIT)throw Error('Текст должен быть не больше 128 КБ.');if(s.includes('\0'))throw Error('Нужен текстовый файл без нулевых байтов.');if(s.split('\n').length>3000)throw Error('Предел — 3000 строк на поле.');}
export function filename(s){if(typeof s!=='string'||s.length>120||!s||!/^[-\w./А-Яа-яЁё ]+$/.test(s)||s.includes('..')||s.startsWith('/'))throw Error('Имя: до 120 символов, буквы, цифры, пробелы, точка, дефис и /.');return s;}
export function makePatch(before,after,name='example.txt',context=3){validateText(before);validateText(after);filename(name);if(!Number.isInteger(context)||context<0||context>12)throw Error('Контекст от 0 до 12 строк.');const patch=createTwoFilesPatch('a/'+name,'b/'+name,before,after,'','',{context,maxEditLength:6000,timeout:1500});if(patch===undefined)throw Error('Сравнение слишком сложное. Сократите изменения.');return patch;}
export function renderDiff(patch,format='line-by-line'){validateText(patch);if(!['line-by-line','side-by-side'].includes(format))throw Error('Неизвестный режим.');if(/^diff --(?:cc|combined)\b/m.test(patch)||/^@@@/m.test(patch))throw Error('Combined diff не поддерживается. Используйте обычный unified diff.');if(/GIT binary patch|^Binary files /m.test(patch))throw Error('Двоичные файлы в этой мастерской не показываются.');const checked=parsePatch(patch);if(!checked.length||checked.some(f=>!f.oldFileName||!f.newFileName))throw Error('Нужны заголовки файлов --- и +++.');const parsed=parse(patch,{diffMaxChanges:3000,diffMaxLineLength:20000});if(!parsed.length)throw Error('Не найден unified diff. Нужны строки --- / +++ и заголовок @@.');if(parsed.length>30)throw Error('Предел — 30 файлов.');if(parsed.some(f=>f.isTooBig))throw Error('Diff слишком большой для отображения: не более 20000 символов в строке и 3000 изменений на файл. Сократите вход; это не означает совпадение версий.');const additions=parsed.reduce((n,f)=>n+f.addedLines,0),deletions=parsed.reduce((n,f)=>n+f.deletedLines,0);const output=html(parsed,{outputFormat:format,drawFileList:true,matching:'lines',diffStyle:'word',renderNothingWhenEmpty:false,diffMaxChanges:3000,diffMaxLineLength:20000});if(output.length>2_000_000)throw Error('Результат слишком большой. Сократите вход.');return{html:output,parsed,files:parsed.length,additions,deletions,patch};}
