"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.LocalProvider = void 0;
exports.tasksRoot = tasksRoot;
exports.ensureTaskFolders = ensureTaskFolders;
exports.parseFrontmatter = parseFrontmatter;
exports.renderFrontmatter = renderFrontmatter;
exports.parseSession = parseSession;
exports.renderSessionEntry = renderSessionEntry;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const crypto = __importStar(require("node:crypto"));
const logger_1 = require("../logger");
const TASK_FOLDERS = ['open', 'pending', 'progress', 'review', 'done'];
const STATUS_TO_FOLDER = {
    'open': 'open',
    'pending': 'pending',
    'in progress': 'progress',
    'review': 'review',
    'done': 'done',
    'closed': 'done',
    'cancelled': 'done',
    'complete': 'done',
};
const FOLDER_TO_STATUS = {
    'open': 'open',
    'pending': 'pending',
    'progress': 'in progress',
    'review': 'review',
    'done': 'done',
};
function tasksRoot(baseDir = process.cwd()) {
    return path.join(baseDir, '.aidev', 'tasks');
}
function ensureTaskFolders(baseDir = process.cwd()) {
    for (const folder of TASK_FOLDERS) {
        fs.mkdirSync(path.join(tasksRoot(baseDir), folder), { recursive: true });
    }
}
function parseFrontmatter(content) {
    const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
    if (!match)
        return { meta: {}, body: content.trim() };
    const meta = {};
    for (const line of match[1].split(/\r?\n/)) {
        const idx = line.indexOf(':');
        if (idx === -1)
            continue;
        const key = line.slice(0, idx).trim();
        const val = line.slice(idx + 1).trim();
        if (key)
            meta[key] = val;
    }
    return { meta, body: match[2].trim() };
}
function renderFrontmatter(meta, body) {
    const lines = Object.entries(meta)
        .filter(([, v]) => v !== '')
        .map(([k, v]) => `${k}: ${v}`);
    return `---\n${lines.join('\n')}\n---\n\n${body}\n`;
}
// ─── Session file parser ───────────────────────────────────────────────────────
function parseSession(content) {
    if (!content.trim())
        return [];
    // Normalize CRLF to LF so regexes work on Windows-edited files
    const normalized = content.replace(/\r\n/g, '\n');
    const comments = [];
    const blocks = normalized.split(/(?:^|\n)---\n/);
    for (const block of blocks) {
        const trimmed = block.trim();
        if (!trimmed)
            continue;
        // Match: ## author — timestamp  OR  ## author
        const headerMatch = trimmed.match(/^##\s+(.+?)\s+—\s+(\d{4}-\d{2}-\d{2}T[\d:.]+Z)\s*\n([\s\S]*)/);
        const headerNoDate = trimmed.match(/^##\s+(.+?)\s*\n([\s\S]*)/);
        let author;
        let date;
        let text;
        if (headerMatch) {
            author = headerMatch[1].trim();
            date = new Date(headerMatch[2]).getTime();
            text = headerMatch[3].trim();
        }
        else if (headerNoDate) {
            author = headerNoDate[1].trim();
            date = Date.now();
            text = headerNoDate[2].trim();
        }
        else {
            continue;
        }
        comments.push({
            id: `${date}-${author}`,
            text,
            author,
            authorId: author,
            date,
        });
    }
    return comments;
}
function renderSessionEntry(author, text) {
    const ts = new Date().toISOString();
    return `\n---\n\n## ${author} — ${ts}\n\n${text}\n`;
}
// ─── File helpers ──────────────────────────────────────────────────────────────
function findTaskFile(baseDir, taskId) {
    for (const folder of TASK_FOLDERS) {
        const dir = path.join(tasksRoot(baseDir), folder);
        if (!fs.existsSync(dir))
            continue;
        const files = fs.readdirSync(dir);
        const match = files.find((f) => f.startsWith(`${taskId}-`) && f.endsWith('.md') && !f.endsWith('.session.md'));
        if (match)
            return { folder, filename: match, dir };
    }
    return null;
}
function sessionFilename(taskFilename) {
    return taskFilename.replace(/\.md$/, '.session.md');
}
function slugify(text) {
    return text
        .toLowerCase()
        .replace(/[^a-z0-9]+/g, '-')
        .replace(/^-|-$/g, '')
        .slice(0, 60);
}
function shortId() {
    return crypto.randomUUID().replace(/-/g, '').slice(0, 8);
}
class LocalProvider {
    constructor(baseDir = process.cwd(), mode = 'code') {
        this.baseDir = baseDir;
        this.mode = mode;
        ensureTaskFolders(this.baseDir);
    }
    async fetchTasks(_options) {
        logger_1.logger.debug(`Fetching ${this.mode} tasks from local .aidev/tasks folders`);
        const tasks = [];
        for (const folder of TASK_FOLDERS) {
            const dir = path.join(tasksRoot(this.baseDir), folder);
            if (!fs.existsSync(dir))
                continue;
            const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md') && !f.endsWith('.session.md'));
            for (const file of files) {
                const content = fs.readFileSync(path.join(dir, file), 'utf8');
                const { meta, body } = parseFrontmatter(content);
                const taskType = meta.type === 'non-code' ? 'non-code' : 'code';
                if (taskType !== this.mode)
                    continue;
                const idMatch = file.match(/^([a-f0-9]+)-/);
                const id = idMatch ? idMatch[1] : file.replace(/\.md$/, '');
                tasks.push({
                    id,
                    name: meta.title || file.replace(/\.md$/, ''),
                    description: body,
                    status: FOLDER_TO_STATUS[folder],
                    url: path.join(dir, file),
                    tags: meta.tags ? meta.tags.split(',').map((t) => t.trim()).filter(Boolean) : [],
                    priority: meta.priority ? parseInt(meta.priority, 10) : undefined,
                    sourceListId: meta.listId || meta.list_id || undefined,
                });
            }
        }
        return tasks;
    }
    async fetchTasksByStatus(statuses, _options) {
        const normalized = statuses.map((s) => s.toLowerCase());
        const all = await this.fetchTasks();
        return all.filter((t) => normalized.includes(t.status.toLowerCase()));
    }
    async postComment(taskId, text) {
        logger_1.logger.debug(`Posting comment to local task ${taskId}`);
        const found = findTaskFile(this.baseDir, taskId);
        if (!found) {
            throw new Error(`Local task not found: ${taskId}`);
        }
        const sessionPath = path.join(found.dir, sessionFilename(found.filename));
        const entry = renderSessionEntry('aidev', text);
        if (fs.existsSync(sessionPath)) {
            fs.appendFileSync(sessionPath, entry, 'utf8');
        }
        else {
            fs.writeFileSync(sessionPath, `<!-- aidev session log — append your comments below using "## your-name" as header -->\n${entry}`, 'utf8');
        }
    }
    async getComments(taskId) {
        logger_1.logger.debug(`Fetching comments for local task ${taskId}`);
        const found = findTaskFile(this.baseDir, taskId);
        if (!found)
            return [];
        const sessionPath = path.join(found.dir, sessionFilename(found.filename));
        if (!fs.existsSync(sessionPath))
            return [];
        const content = fs.readFileSync(sessionPath, 'utf8');
        return parseSession(content);
    }
    async fetchAvailableStatuses() {
        return Object.values(FOLDER_TO_STATUS);
    }
    async updateStatus(taskId, status) {
        const targetFolder = STATUS_TO_FOLDER[status.toLowerCase()];
        if (!targetFolder) {
            logger_1.logger.warn(`Unknown status "${status}" — mapping to "open"`);
        }
        const folder = targetFolder || 'open';
        logger_1.logger.debug(`Updating local task ${taskId} status to "${status}" (folder: ${folder})`);
        const found = findTaskFile(this.baseDir, taskId);
        if (!found) {
            throw new Error(`Local task not found: ${taskId}`);
        }
        if (found.folder === folder)
            return;
        const destDir = path.join(tasksRoot(this.baseDir), folder);
        fs.mkdirSync(destDir, { recursive: true });
        // Move task file
        fs.renameSync(path.join(found.dir, found.filename), path.join(destDir, found.filename));
        // Move session file if it exists
        const sessName = sessionFilename(found.filename);
        const sessSource = path.join(found.dir, sessName);
        if (fs.existsSync(sessSource)) {
            fs.renameSync(sessSource, path.join(destDir, sessName));
        }
    }
    async removeTag(taskId, tag) {
        logger_1.logger.debug(`Removing tag "${tag}" from local task ${taskId}`);
        const found = findTaskFile(this.baseDir, taskId);
        if (!found) {
            throw new Error(`Local task not found: ${taskId}`);
        }
        const filePath = path.join(found.dir, found.filename);
        const content = fs.readFileSync(filePath, 'utf8');
        const { meta, body } = parseFrontmatter(content);
        if (!meta.tags)
            return;
        const want = tag.trim().toLowerCase();
        const remaining = meta.tags
            .split(',')
            .map((t) => t.trim())
            .filter((t) => t && t.toLowerCase() !== want);
        if (remaining.length === meta.tags.split(',').map((t) => t.trim()).filter(Boolean).length)
            return;
        if (remaining.length > 0) {
            meta.tags = remaining.join(', ');
        }
        else {
            delete meta.tags;
        }
        fs.writeFileSync(filePath, renderFrontmatter(meta, body), 'utf8');
    }
    async addTag(taskId, tag) {
        logger_1.logger.debug(`Adding tag "${tag}" to local task ${taskId}`);
        const found = findTaskFile(this.baseDir, taskId);
        if (!found)
            throw new Error(`Local task not found: ${taskId}`);
        const filePath = path.join(found.dir, found.filename);
        const content = fs.readFileSync(filePath, 'utf8');
        const { meta, body } = parseFrontmatter(content);
        const existing = meta.tags
            ? meta.tags.split(',').map((t) => t.trim()).filter(Boolean)
            : [];
        if (existing.some((t) => t.toLowerCase() === tag.trim().toLowerCase()))
            return;
        existing.push(tag.trim());
        meta.tags = existing.join(', ');
        fs.writeFileSync(filePath, renderFrontmatter(meta, body), 'utf8');
    }
    async createTask(params) {
        const id = shortId();
        const slug = slugify(params.title);
        const filename = `${id}-${slug}.md`;
        const meta = {
            title: params.title,
        };
        if (this.mode === 'non-code')
            meta.type = 'non-code';
        if (params.priority)
            meta.priority = String(params.priority);
        if (params.tags.length > 0)
            meta.tags = params.tags.join(', ');
        if (params.dueDate)
            meta.due_date = new Date(params.dueDate).toISOString().split('T')[0];
        meta.created = new Date().toISOString();
        const content = renderFrontmatter(meta, params.description || '');
        const destDir = path.join(tasksRoot(this.baseDir), 'open');
        fs.mkdirSync(destDir, { recursive: true });
        const filePath = path.join(destDir, filename);
        fs.writeFileSync(filePath, content, 'utf8');
        logger_1.logger.debug(`Created local task: ${filePath}`);
        return { id, url: filePath };
    }
}
exports.LocalProvider = LocalProvider;
//# sourceMappingURL=local.js.map