/**
 * PMO Utility Functions
 */
/**
 * Convert a string to a URL-safe slug
 */
export function slugify(text) {
    return text
        .toLowerCase()
        .trim()
        .replace(/[^\w\s-]/g, '') // Remove non-word chars (except spaces and hyphens)
        .replace(/[\s_]+/g, '-') // Replace spaces and underscores with hyphens
        .replace(/-+/g, '-') // Replace multiple hyphens with single hyphen
        .replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens
        .substring(0, 100); // Limit length
}
/**
 * Format a date as ISO string (date only)
 */
export function formatDate(date) {
    return date.toISOString().split('T')[0];
}
/**
 * Format a date as ISO timestamp
 */
export function formatTimestamp(date) {
    return date.toISOString();
}
/**
 * Parse an ISO date string
 */
export function parseDate(str) {
    return new Date(str);
}
/**
 * Entity type prefixes for ID generation
 */
export const ENTITY_PREFIXES = {
    ticket: 'TKT',
    epic: 'EPIC',
    spec: 'SPEC',
    project: 'PROJ',
};
/**
 * Entity type to table name mapping for self-healing ID generation
 */
const ENTITY_TABLES = {
    ticket: 'pmo_tickets',
    epic: 'pmo_epics',
    spec: 'pmo_specs',
    project: 'pmo_projects',
};
/**
 * Generate a sequential ID for an entity.
 *
 * Format: TKT-001, EPIC-001, SPEC-001, PROJ-001
 *
 * Uses pmo_settings table to track the next ID for each entity type.
 * IDs are zero-padded to 3 digits (001-999), then expand (1000+).
 *
 * Self-healing: If counter is behind MAX(id) in the table, auto-corrects.
 *
 * @param db - Database instance with prepare method
 * @param entityType - Type of entity (ticket, epic, spec, project)
 * @returns Generated ID like "TKT-001"
 */
export function generateEntityId(db, entityType) {
    const typePrefix = ENTITY_PREFIXES[entityType];
    const tableName = ENTITY_TABLES[entityType];
    const settingKey = `next_${entityType}_id`;
    // Get MAX(id) from the entity table for self-healing
    // Extract numeric part: TKT-001 → 1, EPIC-042 → 42
    const prefixLen = typePrefix.length + 1; // e.g., "TKT-" = 4 chars
    const maxResult = db.prepare(`SELECT MAX(CAST(SUBSTR(id, ${prefixLen + 1}) AS INTEGER)) as max_num FROM ${tableName}`).get();
    const maxExistingId = maxResult?.max_num || 0;
    // Get current counter
    const row = db.prepare(`SELECT value FROM pmo_settings WHERE key = ?`).get(settingKey);
    let nextNum = row ? parseInt(row.value, 10) : 1;
    // Self-healing: if counter is behind existing IDs, fix it
    if (nextNum <= maxExistingId) {
        nextNum = maxExistingId + 1;
    }
    // Update counter
    db.prepare(`
    INSERT INTO pmo_settings (key, value) VALUES (?, ?)
    ON CONFLICT(key) DO UPDATE SET value = ?
  `).run(settingKey, String(nextNum + 1), String(nextNum + 1));
    // Format ID with zero-padding (3 digits minimum)
    const numStr = nextNum.toString().padStart(3, '0');
    return `${typePrefix}-${numStr}`;
}
/**
 * Parse an entity ID into its components.
 *
 * Format: TKT-001 → { entityType: 'TKT', number: 1 }
 *
 * @param id - Entity ID to parse
 * @returns Parsed components, or null if invalid format
 */
export function parseEntityId(id) {
    if (!id)
        return null;
    const match = id.match(/^([A-Z]+)-(\d+)$/);
    if (match) {
        return {
            entityType: match[1],
            number: parseInt(match[2], 10),
            raw: id,
        };
    }
    return null;
}
/**
 * Deep clone an object
 */
export function deepClone(obj) {
    return JSON.parse(JSON.stringify(obj));
}
// =============================================================================
// Work Column Settings
// =============================================================================
/**
 * Default column names for work commands (Linear-style workflow)
 *
 * Linear-style: Backlog → Planned → In Progress → Review → Done
 * - planned: Move tickets here when scheduled/assigned
 * - in_progress: Move tickets here when work starts
 * - review: Move tickets here when work is ready for review
 * - done: Move tickets here when work is complete (reviewed/merged)
 */
export const DEFAULT_WORK_COLUMNS = {
    planned: 'Planned',
    in_progress: 'In Progress',
    review: 'Review',
    done: 'Done',
};
/**
 * Get a work column setting from pmo_settings with fallback to default.
 * Column matching is case-insensitive.
 *
 * Settings keys:
 * - column_in_progress: Column to move tickets to when work starts
 * - column_review: Column to move tickets to when work is ready for review
 * - column_done: Column to move tickets to when work is complete
 *
 * @param db - Database instance
 * @param columnType - Type of column (in_progress, review, done)
 * @returns The configured column name, or the default if not set
 */
export function getWorkColumnSetting(db, columnType) {
    const settingKey = `column_${columnType}`;
    const row = db.prepare(`SELECT value FROM pmo_settings WHERE key = ?`).get(settingKey);
    return row?.value || DEFAULT_WORK_COLUMNS[columnType];
}
/**
 * Set a work column setting in pmo_settings.
 *
 * @param db - Database instance
 * @param columnType - Type of column (in_progress, review, done)
 * @param columnName - Name of the column to use
 */
export function setWorkColumnSetting(db, columnType, columnName) {
    const settingKey = `column_${columnType}`;
    db.prepare(`
    INSERT INTO pmo_settings (key, value) VALUES (?, ?)
    ON CONFLICT(key) DO UPDATE SET value = ?
  `).run(settingKey, columnName, columnName);
}
/**
 * Find a column by configured name (case-insensitive match).
 * Returns the actual column name from the board if found, null otherwise.
 *
 * @param columnNames - Array of column names from the board
 * @param targetColumn - The column name to find (from settings or default)
 * @returns The matching column name, or null if not found
 */
export function findColumnByName(columnNames, targetColumn) {
    const targetLower = targetColumn.toLowerCase();
    // First try exact match (case-insensitive)
    const exactMatch = columnNames.find(col => col.toLowerCase() === targetLower);
    if (exactMatch)
        return exactMatch;
    // Then try partial match (contains)
    const partialMatch = columnNames.find(col => col.toLowerCase().includes(targetLower) ||
        targetLower.includes(col.toLowerCase()));
    return partialMatch || null;
}
/**
 * Check if two arrays have the same elements (order-independent)
 */
export function arraysEqual(a, b) {
    if (a.length !== b.length)
        return false;
    const sortedA = [...a].sort();
    const sortedB = [...b].sort();
    return sortedA.every((val, idx) => val === sortedB[idx]);
}
// =============================================================================
// Workspace Priority Settings
// =============================================================================
/**
 * Default priority scale (backwards compatible with hardcoded P0-P3).
 */
export const DEFAULT_PRIORITIES = ['P0', 'P1', 'P2', 'P3'];
/**
 * Settings key for storing workspace priorities.
 */
const PRIORITIES_SETTING_KEY = 'priorities';
/**
 * Get the workspace priority scale from pmo_settings.
 * Returns the user-defined priority scale, or DEFAULT_PRIORITIES if not set.
 *
 * Priority values are ordered strings - position in array determines sort order.
 * Index 0 is highest priority.
 *
 * @param db - Database instance
 * @returns Array of priority strings ordered from highest to lowest
 */
export function getWorkspacePriorities(db) {
    const row = db.prepare(`SELECT value FROM pmo_settings WHERE key = ?`).get(PRIORITIES_SETTING_KEY);
    if (!row)
        return [...DEFAULT_PRIORITIES];
    try {
        const parsed = JSON.parse(row.value);
        if (Array.isArray(parsed) && parsed.length > 0 && parsed.every((p) => typeof p === 'string')) {
            return parsed;
        }
        return [...DEFAULT_PRIORITIES];
    }
    catch {
        return [...DEFAULT_PRIORITIES];
    }
}
/**
 * Set the workspace priority scale in pmo_settings.
 *
 * @param db - Database instance
 * @param priorities - Array of priority strings ordered from highest to lowest
 */
export function setWorkspacePriorities(db, priorities) {
    const value = JSON.stringify(priorities);
    db.prepare(`
    INSERT INTO pmo_settings (key, value) VALUES (?, ?)
    ON CONFLICT(key) DO UPDATE SET value = ?
  `).run(PRIORITIES_SETTING_KEY, value, value);
}
/**
 * Check if a value is a valid priority for the workspace.
 *
 * @param db - Database instance
 * @param value - Value to check
 * @returns true if the value is in the workspace priority scale
 */
export function isValidWorkspacePriority(db, value) {
    const priorities = getWorkspacePriorities(db);
    return priorities.includes(value);
}
/**
 * Get the sort index for a priority value.
 * Returns the position in the priority array (0 = highest priority).
 * Returns Infinity for unknown priorities (sorts last).
 *
 * @param db - Database instance
 * @param value - Priority value
 * @returns Sort index (lower = higher priority)
 */
export function getPrioritySortIndex(db, value) {
    if (!value)
        return Infinity;
    const priorities = getWorkspacePriorities(db);
    const index = priorities.indexOf(value);
    return index !== -1 ? index : Infinity;
}
//# sourceMappingURL=utils.js.map