"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.TailwindCSSTokenAdapter = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const logger_1 = require("../../utils/logger");
const log = (0, logger_1.createLogger)('TailwindCSSTokenAdapter');
/** Regex to match @theme (with optional `inline` modifier) blocks in CSS */
const THEME_BLOCK_REGEX = /@theme\s*(?:inline\s*)?\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}/gs;
/** Regex to extract CSS custom property declarations from a block */
const CSS_PROPERTY_REGEX = /--([\w-]+)\s*:\s*([^;]+);/g;
/** Maps CSS custom property prefixes to TokenRegistry categories */
const PREFIX_TO_CATEGORY = {
    'color-': 'colors',
    'spacing-': 'spacing',
    'radius-': 'borderRadius',
    'font-size-': 'fontSize',
    'font-family-': 'fontFamily',
    'shadow-': 'boxShadow',
};
/**
 * Common CSS file locations to search for @theme blocks.
 * Reuses the same heuristic paths as CSSVariableExtractor.
 */
const CSS_FILE_PATHS = [
    'globals.css',
    'index.css',
    'styles.css',
    'app/globals.css',
    'app/layout.css',
    'src/globals.css',
    'src/index.css',
    'src/styles.css',
    'src/app/globals.css',
    'styles/globals.css',
    'styles/index.css',
    'src/styles/globals.css',
    'src/styles/index.css',
    'app/styles/globals.css',
    'src/ui/src/index.css',
    'src/ui/src/globals.css',
    'packages/ui/src/index.css',
    'packages/ui/src/globals.css',
];
/**
 * Adapter for extracting design tokens from Tailwind CSS v4 configurations.
 * Parses @theme blocks in CSS files and extracts custom properties as tokens.
 */
class TailwindCSSTokenAdapter {
    detect(projectRoot) {
        try {
            const cssFiles = this.findCSSFiles(projectRoot);
            for (const filePath of cssFiles) {
                try {
                    const content = fs.readFileSync(filePath, 'utf8');
                    if (this.containsThemeBlock(content)) {
                        log.info(`Detected Tailwind v4 @theme in ${path.relative(projectRoot, filePath)}`);
                        return true;
                    }
                }
                catch {
                    // Skip unreadable files
                }
            }
            return false;
        }
        catch (error) {
            log.warn('Error detecting Tailwind v4 config:', error);
            return false;
        }
    }
    extract(projectRoot, configPath) {
        const resolvedConfigPath = configPath
            ? path.resolve(projectRoot, configPath)
            : this.findThemeFile(projectRoot);
        if (!resolvedConfigPath) {
            log.warn('No CSS file with @theme block found');
            return null;
        }
        try {
            log.info(`Parsing Tailwind v4 @theme: ${resolvedConfigPath}`);
            const content = fs.readFileSync(resolvedConfigPath, 'utf-8');
            const properties = this.extractThemeProperties(content);
            if (properties.length === 0) {
                log.warn('No custom properties found in @theme block');
                return null;
            }
            return this.buildTokenRegistry(properties);
        }
        catch (error) {
            log.warn('Failed to parse Tailwind v4 config:', error);
            return null;
        }
    }
    getFramework() {
        return 'tailwind';
    }
    /**
     * Returns the path to the CSS file containing @theme.
     * If configPath is provided but doesn't contain @theme, falls back to auto-discovery.
     */
    getConfigPath(projectRoot, configPath) {
        if (configPath) {
            const resolved = path.resolve(projectRoot, configPath);
            try {
                const content = fs.readFileSync(resolved, 'utf8');
                if (this.containsThemeBlock(content)) {
                    return resolved;
                }
                log.warn(`Provided config path ${configPath} does not contain @theme, searching for CSS theme file`);
            }
            catch {
                log.warn(`Could not read provided config path ${configPath}, searching for CSS theme file`);
            }
        }
        return this.findThemeFile(projectRoot);
    }
    /** Checks whether CSS content contains an @theme block */
    containsThemeBlock(content) {
        return /@theme\s*(?:inline\s*)?\{/.test(content);
    }
    /** Finds CSS files in common project locations */
    findCSSFiles(projectRoot) {
        const found = [];
        for (const relativePath of CSS_FILE_PATHS) {
            const fullPath = path.join(projectRoot, relativePath);
            if (fs.existsSync(fullPath)) {
                found.push(fullPath);
            }
        }
        return found;
    }
    /** Finds the first CSS file containing an @theme block */
    findThemeFile(projectRoot) {
        const cssFiles = this.findCSSFiles(projectRoot);
        for (const filePath of cssFiles) {
            try {
                const content = fs.readFileSync(filePath, 'utf8');
                if (this.containsThemeBlock(content)) {
                    return filePath;
                }
            }
            catch {
                // Skip unreadable files
            }
        }
        return null;
    }
    /** Extracts all CSS custom properties from @theme blocks */
    extractThemeProperties(content) {
        const properties = [];
        const blockMatches = content.matchAll(THEME_BLOCK_REGEX);
        for (const blockMatch of blockMatches) {
            const blockContent = blockMatch[1];
            const propMatches = blockContent.matchAll(CSS_PROPERTY_REGEX);
            for (const propMatch of propMatches) {
                properties.push({
                    name: propMatch[1].trim(),
                    value: propMatch[2].trim(),
                });
            }
        }
        return properties;
    }
    /**
     * Builds a TokenRegistry from extracted CSS custom properties.
     * Groups properties by prefix into categories, and nests related properties
     * (e.g., --color-gray-50 and --color-gray-500 become colors.gray: { '50': ..., '500': ... })
     */
    buildTokenRegistry(properties) {
        const registry = {
            colors: {},
            spacing: {},
            fontSize: {},
            borderRadius: {},
            boxShadow: {},
            fontFamily: {},
        };
        for (const { name, value } of properties) {
            const categorized = this.categorizeProperty(name);
            if (!categorized)
                continue;
            const { category, tokenPath } = categorized;
            // Split path into segments for nesting (e.g., "gray-50" → ["gray", "50"])
            const segments = tokenPath.split('-');
            if (segments.length === 1) {
                // Flat token: --color-primary → colors.primary
                registry[category][segments[0]] = value;
            }
            else {
                // Potentially nested: --color-gray-50 → colors.gray.50
                const parentKey = segments[0];
                const childKey = segments.slice(1).join('-');
                const existing = registry[category][parentKey];
                if (typeof existing === 'string') {
                    // Already a flat token with this name — convert to nested
                    registry[category][parentKey] = {
                        DEFAULT: existing,
                        [childKey]: value,
                    };
                }
                else if (typeof existing === 'object') {
                    existing[childKey] = value;
                }
                else {
                    // First entry — could be flat or start of a group
                    // Check if there are other properties that share this prefix
                    const hasSiblings = properties.some((p) => {
                        if (p.name === name)
                            return false;
                        const cat = this.categorizeProperty(p.name);
                        if (!cat || cat.category !== category)
                            return false;
                        return cat.tokenPath.startsWith(parentKey + '-');
                    });
                    if (hasSiblings) {
                        registry[category][parentKey] = { [childKey]: value };
                    }
                    else {
                        // Single property, use full path as flat token
                        registry[category][tokenPath] = value;
                    }
                }
            }
        }
        return registry;
    }
    /**
     * Maps a CSS custom property name to a registry category and token path.
     * Returns null if the property doesn't match any known prefix.
     */
    categorizeProperty(propertyName) {
        for (const [prefix, category] of Object.entries(PREFIX_TO_CATEGORY)) {
            if (propertyName.startsWith(prefix)) {
                return {
                    category,
                    tokenPath: propertyName.slice(prefix.length),
                };
            }
        }
        return null;
    }
}
exports.TailwindCSSTokenAdapter = TailwindCSSTokenAdapter;
//# sourceMappingURL=TailwindCSSTokenAdapter.js.map