"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.DesignMdRegistry = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
class DesignMdRegistry {
    root;
    constructor(root = process.cwd()) {
        this.root = root;
    }
    list() {
        const base = path.join(this.root, 'design', 'profiles');
        if (!fs.existsSync(base))
            return [];
        const dirs = fs.readdirSync(base, { withFileTypes: true }).filter(d => d.isDirectory()).map(d => d.name);
        const out = [];
        for (const d of dirs) {
            const metaPath = path.join(base, d, 'meta.json');
            if (!fs.existsSync(metaPath))
                continue;
            try {
                out.push(JSON.parse(fs.readFileSync(metaPath, 'utf-8')));
            }
            catch { /* skip malformed */ }
        }
        return out;
    }
    get(id) {
        const base = path.join(this.root, 'design', 'profiles', id);
        const metaPath = path.join(base, 'meta.json');
        const designPath = path.join(base, 'DESIGN.md');
        if (!fs.existsSync(metaPath) || !fs.existsSync(designPath))
            return { meta: null, design: null };
        return { meta: JSON.parse(fs.readFileSync(metaPath, 'utf-8')), design: fs.readFileSync(designPath, 'utf-8') };
    }
    /**
     * v1.15.0 Phase A3 — Full-text search across profile id/title/archetype +
     * DESIGN.md body. Scoring favors id/archetype matches over body matches.
     * Filters narrow the result set BEFORE scoring.
     */
    search(query, filters = {}) {
        const terms = query.toLowerCase().match(/[a-z0-9_.-]+/g) || [];
        if (terms.length === 0)
            return [];
        const all = this.list();
        const filtered = all.filter(p => {
            if (filters.source && p.source !== filters.source)
                return false;
            if (filters.archetype && p.archetype !== filters.archetype)
                return false;
            if (filters.qualityScore && p.qualityScore !== filters.qualityScore)
                return false;
            return true;
        });
        const results = [];
        for (const p of filtered) {
            const idHay = `${p.id} ${p.title}`.toLowerCase();
            const metaHay = `${p.archetype || ''} ${p.qualityScore || ''} ${p.source}`.toLowerCase();
            let score = 0;
            for (const t of terms) {
                if (idHay.includes(t))
                    score += 3;
                if (metaHay.includes(t))
                    score += 2;
            }
            if (score === 0) {
                // Coarse substring body match (one read per profile when meta misses).
                const { design } = this.get(p.id);
                if (design) {
                    const body = design.toLowerCase();
                    for (const t of terms)
                        if (body.includes(t))
                            score += 1;
                }
            }
            if (score > 0)
                results.push({ profile: p, score });
        }
        return results.sort((a, b) => b.score - a.score);
    }
    /** v1.15.0 Phase A3 — Filter helper. */
    findByArchetype(archetype) {
        return this.list().filter(p => p.archetype === archetype);
    }
    /**
     * v1.15.0 Phase A3 — Filter profiles whose tokens.json contains a color
     * within `deltaE` of the target hex. Uses euclidean RGB distance
     * (sufficient for the "designs with blue" UX).
     */
    findByTokenColor(hex, deltaE = 35) {
        const target = parseHex(hex);
        if (!target)
            return [];
        const out = [];
        for (const p of this.list()) {
            const tokensPath = path.join(this.root, 'design', 'profiles', p.id, 'tokens.json');
            if (!fs.existsSync(tokensPath))
                continue;
            try {
                const tokens = JSON.parse(fs.readFileSync(tokensPath, 'utf-8'));
                const colors = extractHexColors(tokens);
                for (const c of colors) {
                    const rgb = parseHex(c);
                    if (!rgb)
                        continue;
                    if (rgbDistance(target, rgb) <= deltaE) {
                        out.push(p);
                        break;
                    }
                }
            }
            catch { /* skip malformed tokens */ }
        }
        return out;
    }
    /**
     * v1.15.0 Phase A3 — Token-level diff between two profiles. Reads each
     * profile's tokens.json or falls back to DESIGN.md frontmatter.
     */
    diff(profileA, profileB) {
        const a = this.loadProfileTokens(profileA);
        const b = this.loadProfileTokens(profileB);
        if (!a || !b)
            return null;
        const aMeta = this.get(profileA).meta;
        const bMeta = this.get(profileB).meta;
        return {
            a: profileA,
            b: profileB,
            colors: mapDelta(a.colors, b.colors),
            typography: mapDelta(a.typography, b.typography),
            archetypeChanged: (aMeta?.archetype || '') !== (bMeta?.archetype || ''),
            qualityScoreChanged: (aMeta?.qualityScore || '') !== (bMeta?.qualityScore || ''),
            metaDelta: {
                archetype: { a: aMeta?.archetype, b: bMeta?.archetype },
                qualityScore: { a: aMeta?.qualityScore, b: bMeta?.qualityScore },
            },
        };
    }
    /** Reads tokens.json or falls back to DESIGN.md frontmatter parsing. */
    loadProfileTokens(id) {
        const base = path.join(this.root, 'design', 'profiles', id);
        if (!fs.existsSync(base))
            return null;
        const tokensPath = path.join(base, 'tokens.json');
        if (fs.existsSync(tokensPath)) {
            try {
                const t = JSON.parse(fs.readFileSync(tokensPath, 'utf-8'));
                return {
                    colors: flattenToStringMap(t.colors || t.color || {}),
                    typography: flattenToStringMap(t.typography || t.font || {}),
                };
            }
            catch { /* fall through */ }
        }
        const designPath = path.join(base, 'DESIGN.md');
        if (!fs.existsSync(designPath))
            return null;
        const content = fs.readFileSync(designPath, 'utf-8');
        return parseFrontmatterTokens(content);
    }
}
exports.DesignMdRegistry = DesignMdRegistry;
// ─── helpers ─────────────────────────────────────────────────────────────
function parseHex(hex) {
    const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
    if (m) {
        return [parseInt(m[1].slice(0, 2), 16), parseInt(m[1].slice(2, 4), 16), parseInt(m[1].slice(4, 6), 16)];
    }
    const m2 = /^#?([0-9a-f]{3})$/i.exec(hex.trim());
    if (!m2)
        return null;
    const r = parseInt(m2[1][0] + m2[1][0], 16);
    const g = parseInt(m2[1][1] + m2[1][1], 16);
    const b = parseInt(m2[1][2] + m2[1][2], 16);
    return [r, g, b];
}
function rgbDistance(a, b) {
    const dr = a[0] - b[0];
    const dg = a[1] - b[1];
    const db = a[2] - b[2];
    return Math.sqrt(dr * dr + dg * dg + db * db);
}
function extractHexColors(obj) {
    const out = [];
    if (typeof obj === 'string') {
        if (/^#[0-9a-f]{3,6}$/i.test(obj.trim()))
            out.push(obj.trim());
    }
    else if (Array.isArray(obj)) {
        for (const v of obj)
            out.push(...extractHexColors(v));
    }
    else if (obj && typeof obj === 'object') {
        for (const v of Object.values(obj))
            out.push(...extractHexColors(v));
    }
    return out;
}
function flattenToStringMap(obj, prefix = '') {
    const out = {};
    if (!obj || typeof obj !== 'object')
        return out;
    for (const [k, v] of Object.entries(obj)) {
        const key = prefix ? `${prefix}.${k}` : k;
        if (typeof v === 'string' || typeof v === 'number')
            out[key] = String(v);
        else if (v && typeof v === 'object' && !Array.isArray(v))
            Object.assign(out, flattenToStringMap(v, key));
    }
    return out;
}
function mapDelta(a, b) {
    const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
    const out = [];
    for (const k of keys) {
        if (a[k] !== b[k])
            out.push({ key: k, a: a[k], b: b[k] });
    }
    return out.sort((x, y) => x.key.localeCompare(y.key));
}
/**
 * Cheap frontmatter parser — extracts `colors:` and `typography:` blocks
 * the awesome-design-md collection puts at the top of every DESIGN.md.
 */
function parseFrontmatterTokens(content) {
    const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
    if (!fmMatch)
        return { colors: {}, typography: {} };
    const lines = fmMatch[1].split('\n');
    const colors = {};
    const typography = {};
    let section = null;
    let typoCurrent = '';
    for (const raw of lines) {
        if (/^colors:\s*$/.test(raw)) {
            section = 'colors';
            continue;
        }
        if (/^typography:\s*$/.test(raw)) {
            section = 'typography';
            typoCurrent = '';
            continue;
        }
        if (/^[a-zA-Z]/.test(raw)) {
            section = null;
            continue;
        }
        if (!section)
            continue;
        if (section === 'colors') {
            const m = /^\s+([a-zA-Z0-9_-]+):\s*['"]?(#[0-9a-fA-F]{3,6})['"]?$/.exec(raw);
            if (m)
                colors[m[1]] = m[2];
        }
        else {
            const head = /^\s{2}([a-zA-Z0-9_-]+):\s*$/.exec(raw);
            if (head) {
                typoCurrent = head[1];
                continue;
            }
            const kv = /^\s{4,}([a-zA-Z0-9_-]+):\s*([^\n]+)$/.exec(raw);
            if (kv && typoCurrent) {
                typography[`${typoCurrent}.${kv[1]}`] = kv[2].trim().replace(/^['"]|['"]$/g, '');
            }
        }
    }
    return { colors, typography };
}
