"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.appendEvent = appendEvent;
exports.readEvents = readEvents;
exports.replayNoteEvents = replayNoteEvents;
exports.replayTaskEvents = replayTaskEvents;
exports.replaySkillEvents = replaySkillEvents;
exports.ensureGitignore = ensureGitignore;
exports.ensureGitattributes = ensureGitattributes;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const logger_1 = require("../lib/logger");
const log = (0, logger_1.createLogger)('events-log');
// ---------------------------------------------------------------------------
// Core functions
// ---------------------------------------------------------------------------
/** Append a single event as a JSON line to the events.jsonl file. */
function appendEvent(eventsPath, event) {
    try {
        const line = JSON.stringify({ ts: new Date().toISOString(), ...event }) + '\n';
        fs.appendFileSync(eventsPath, line, 'utf-8');
    }
    catch (err) {
        log.error({ err }, 'failed to append event');
    }
}
/** Read and parse all events from a JSONL file. Invalid lines are skipped. */
function readEvents(eventsPath) {
    try {
        if (!fs.existsSync(eventsPath))
            return [];
        const content = fs.readFileSync(eventsPath, 'utf-8');
        const events = [];
        for (const line of content.split('\n')) {
            const trimmed = line.trim();
            if (!trimmed)
                continue;
            try {
                events.push(JSON.parse(trimmed));
            }
            catch { /* skip invalid lines */ }
        }
        return events;
    }
    catch {
        return [];
    }
}
/** Sort events by timestamp (ISO strings are lexicographically sortable). */
function sortByTs(events) {
    return [...events].sort((a, b) => (a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0));
}
function isoToMs(value) {
    if (value == null)
        return null;
    const d = new Date(value);
    return isNaN(d.getTime()) ? null : d.getTime();
}
// ---------------------------------------------------------------------------
// Replay functions
// ---------------------------------------------------------------------------
/**
 * Replay note events + content to reconstruct a ParsedNoteFile.
 * Returns null if no 'created' event is found.
 */
function replayNoteEvents(events, content) {
    const sorted = sortByTs(events);
    const created = sorted.find(e => e.op === 'created');
    if (!created)
        return null;
    let title = created.title;
    let tags = created.tags ?? [];
    let updatedBy = null;
    const relations = [];
    const attachments = [];
    for (const ev of sorted) {
        if (ev.op === 'update') {
            const u = ev;
            if (typeof u.title === 'string')
                title = u.title;
            if (Array.isArray(u.tags))
                tags = u.tags;
            if (typeof u.by === 'string')
                updatedBy = u.by;
        }
        else if (ev.op === 'relation') {
            const r = ev;
            const key = `${r.to}:${r.kind}:${r.graph ?? ''}`;
            if (r.action === 'add') {
                if (!relations.some(x => `${x.to}:${x.kind}:${x.graph ?? ''}` === key)) {
                    const entry = { to: r.to, kind: r.kind };
                    if (r.graph)
                        entry.graph = r.graph;
                    relations.push(entry);
                }
            }
            else {
                const idx = relations.findIndex(x => `${x.to}:${x.kind}:${x.graph ?? ''}` === key);
                if (idx !== -1)
                    relations.splice(idx, 1);
            }
        }
        else if (ev.op === 'attachment') {
            const a = ev;
            if (a.action === 'add') {
                if (!attachments.some(x => x.filename === a.file)) {
                    attachments.push({ filename: a.file, mimeType: 'application/octet-stream', size: 0, addedAt: 0 });
                }
            }
            else {
                const idx = attachments.findIndex(x => x.filename === a.file);
                if (idx !== -1)
                    attachments.splice(idx, 1);
            }
        }
    }
    const version = sorted.length;
    // updatedAt = ts of last event; createdAt from created event
    const lastEvent = sorted[sorted.length - 1];
    const updatedAt = isoToMs(lastEvent?.ts);
    const createdAt = isoToMs(created.ts) ?? created.createdAt;
    return {
        id: created.id,
        title,
        content,
        tags,
        createdAt: created.createdAt ?? createdAt,
        updatedAt: updatedAt ?? created.createdAt,
        version,
        createdBy: created.createdBy ?? null,
        updatedBy,
        relations,
        attachments,
    };
}
/**
 * Replay task events + description to reconstruct a ParsedTaskFile.
 * Returns null if no 'created' event is found.
 */
function replayTaskEvents(events, description) {
    const sorted = sortByTs(events);
    const created = sorted.find(e => e.op === 'created');
    if (!created)
        return null;
    let title = created.title;
    let status = created.status;
    let priority = created.priority;
    let tags = created.tags ?? [];
    let dueDate = created.dueDate;
    let estimate = created.estimate;
    let completedAt = created.completedAt;
    let updatedBy = null;
    const relations = [];
    const attachments = [];
    for (const ev of sorted) {
        if (ev.op === 'update') {
            const u = ev;
            if (typeof u.title === 'string')
                title = u.title;
            if (typeof u.status === 'string')
                status = u.status;
            if (typeof u.priority === 'string')
                priority = u.priority;
            if (Array.isArray(u.tags))
                tags = u.tags;
            if ('dueDate' in u)
                dueDate = u.dueDate;
            if ('estimate' in u)
                estimate = u.estimate;
            if ('completedAt' in u)
                completedAt = u.completedAt;
            if (typeof u.by === 'string')
                updatedBy = u.by;
        }
        else if (ev.op === 'relation') {
            const r = ev;
            const key = `${r.to}:${r.kind}:${r.graph ?? ''}`;
            if (r.action === 'add') {
                if (!relations.some(x => `${x.to}:${x.kind}:${x.graph ?? ''}` === key)) {
                    const entry = { to: r.to, kind: r.kind };
                    if (r.graph)
                        entry.graph = r.graph;
                    relations.push(entry);
                }
            }
            else {
                const idx = relations.findIndex(x => `${x.to}:${x.kind}:${x.graph ?? ''}` === key);
                if (idx !== -1)
                    relations.splice(idx, 1);
            }
        }
        else if (ev.op === 'attachment') {
            const a = ev;
            if (a.action === 'add') {
                if (!attachments.some(x => x.filename === a.file)) {
                    attachments.push({ filename: a.file, mimeType: 'application/octet-stream', size: 0, addedAt: 0 });
                }
            }
            else {
                const idx = attachments.findIndex(x => x.filename === a.file);
                if (idx !== -1)
                    attachments.splice(idx, 1);
            }
        }
    }
    const version = sorted.length;
    const lastEvent = sorted[sorted.length - 1];
    const updatedAt = isoToMs(lastEvent?.ts);
    return {
        id: created.id,
        title,
        description,
        status,
        priority,
        tags,
        dueDate,
        estimate,
        completedAt,
        assignee: null,
        createdAt: created.createdAt,
        updatedAt: updatedAt ?? created.createdAt,
        version,
        createdBy: created.createdBy ?? null,
        updatedBy,
        relations,
        attachments,
    };
}
/**
 * Replay skill events + description to reconstruct a ParsedSkillFile.
 * Returns null if no 'created' event is found.
 */
function replaySkillEvents(events, description) {
    const sorted = sortByTs(events);
    const created = sorted.find(e => e.op === 'created');
    if (!created)
        return null;
    let title = created.title;
    let tags = created.tags ?? [];
    let steps = created.steps ?? [];
    let triggers = created.triggers ?? [];
    let inputHints = created.inputHints ?? [];
    let filePatterns = created.filePatterns ?? [];
    let source = created.source;
    let confidence = created.confidence;
    let usageCount = created.usageCount;
    let lastUsedAt = created.lastUsedAt;
    let updatedBy = null;
    const relations = [];
    const attachments = [];
    for (const ev of sorted) {
        if (ev.op === 'update') {
            const u = ev;
            if (typeof u.title === 'string')
                title = u.title;
            if (Array.isArray(u.tags))
                tags = u.tags;
            if (Array.isArray(u.steps))
                steps = u.steps;
            if (Array.isArray(u.triggers))
                triggers = u.triggers;
            if (Array.isArray(u.inputHints))
                inputHints = u.inputHints;
            if (Array.isArray(u.filePatterns))
                filePatterns = u.filePatterns;
            if (typeof u.source === 'string')
                source = u.source;
            if (typeof u.confidence === 'number')
                confidence = u.confidence;
            if (typeof u.usageCount === 'number')
                usageCount = u.usageCount;
            if ('lastUsedAt' in u)
                lastUsedAt = u.lastUsedAt;
            if (typeof u.by === 'string')
                updatedBy = u.by;
        }
        else if (ev.op === 'relation') {
            const r = ev;
            const key = `${r.to}:${r.kind}:${r.graph ?? ''}`;
            if (r.action === 'add') {
                if (!relations.some(x => `${x.to}:${x.kind}:${x.graph ?? ''}` === key)) {
                    const entry = { to: r.to, kind: r.kind };
                    if (r.graph)
                        entry.graph = r.graph;
                    relations.push(entry);
                }
            }
            else {
                const idx = relations.findIndex(x => `${x.to}:${x.kind}:${x.graph ?? ''}` === key);
                if (idx !== -1)
                    relations.splice(idx, 1);
            }
        }
        else if (ev.op === 'attachment') {
            const a = ev;
            if (a.action === 'add') {
                if (!attachments.some(x => x.filename === a.file)) {
                    attachments.push({ filename: a.file, mimeType: 'application/octet-stream', size: 0, addedAt: 0 });
                }
            }
            else {
                const idx = attachments.findIndex(x => x.filename === a.file);
                if (idx !== -1)
                    attachments.splice(idx, 1);
            }
        }
    }
    const version = sorted.length;
    const lastEvent = sorted[sorted.length - 1];
    const updatedAt = isoToMs(lastEvent?.ts);
    return {
        id: created.id,
        title,
        description,
        steps,
        triggers,
        inputHints,
        filePatterns,
        tags,
        source,
        confidence,
        usageCount,
        lastUsedAt,
        createdAt: created.createdAt,
        updatedAt: updatedAt ?? created.createdAt,
        version,
        createdBy: created.createdBy ?? null,
        updatedBy,
        relations,
        attachments,
    };
}
// ---------------------------------------------------------------------------
// Git helpers
// ---------------------------------------------------------------------------
/** Ensure a .gitignore file in parentDir contains the given pattern line. */
function ensureGitignore(parentDir, pattern) {
    try {
        const gitignorePath = path.join(parentDir, '.gitignore');
        let content = '';
        if (fs.existsSync(gitignorePath)) {
            content = fs.readFileSync(gitignorePath, 'utf-8');
        }
        if (!content.split('\n').some(l => l.trim() === pattern)) {
            fs.mkdirSync(parentDir, { recursive: true });
            fs.writeFileSync(gitignorePath, content + (content && !content.endsWith('\n') ? '\n' : '') + pattern + '\n', 'utf-8');
        }
    }
    catch (err) {
        log.error({ err }, 'failed to write .gitignore');
    }
}
/** Ensure a .gitattributes file in entityParentDir contains the merge=union line for events.jsonl. */
function ensureGitattributes(entityParentDir) {
    const pattern = '*/events.jsonl merge=union';
    try {
        const gitattrsPath = path.join(entityParentDir, '.gitattributes');
        let content = '';
        if (fs.existsSync(gitattrsPath)) {
            content = fs.readFileSync(gitattrsPath, 'utf-8');
        }
        if (!content.split('\n').some(l => l.trim() === pattern)) {
            fs.mkdirSync(entityParentDir, { recursive: true });
            fs.writeFileSync(gitattrsPath, content + (content && !content.endsWith('\n') ? '\n' : '') + pattern + '\n', 'utf-8');
        }
    }
    catch (err) {
        log.error({ err }, 'failed to write .gitattributes');
    }
}
