"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.TailwindTokenAdapter = void 0;
const ts = __importStar(require("typescript"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const glob_1 = require("glob");
const logger_1 = require("../../utils/logger");
const CSSVariableExtractor_1 = require("../extractors/CSSVariableExtractor");
const log = (0, logger_1.createLogger)('TailwindTokenAdapter');
/**
 * Adapter for extracting design tokens from Tailwind CSS configurations.
 * Parses tailwind.config.{js,ts,cjs,mjs} files and extracts theme.extend tokens.
 */
class TailwindTokenAdapter {
    CONFIG_FILENAMES = [
        'tailwind.config.ts',
        'tailwind.config.js',
        'tailwind.config.cjs',
        'tailwind.config.mjs',
    ];
    SUPPORTED_CATEGORIES = [
        'colors',
        'spacing',
        'fontSize',
        'borderRadius',
        'boxShadow',
        'fontFamily',
    ];
    detect(projectRoot) {
        try {
            const pattern = '**/tailwind.config.{js,ts,cjs,mjs}';
            const matches = glob_1.glob.sync(pattern, {
                cwd: projectRoot,
                ignore: [
                    '**/node_modules/**',
                    '**/.git/**',
                    '**/dist/**',
                    '**/build/**',
                ],
                nodir: true,
            });
            return matches.length > 0;
        }
        catch (error) {
            log.warn('Error detecting Tailwind config:', error);
            return false;
        }
    }
    extract(projectRoot, configPath) {
        // If explicit config path provided, use it; otherwise search
        const resolvedConfigPath = configPath
            ? path.resolve(projectRoot, configPath)
            : this.findConfigPath(projectRoot);
        if (!resolvedConfigPath) {
            log.warn('No Tailwind config file found');
            return null;
        }
        try {
            log.info(`Parsing Tailwind config: ${resolvedConfigPath}`);
            const sourceCode = fs.readFileSync(resolvedConfigPath, 'utf-8');
            const sourceFile = ts.createSourceFile(resolvedConfigPath, sourceCode, ts.ScriptTarget.Latest, true);
            const themeExtend = this.extractThemeExtend(sourceFile);
            if (!themeExtend) {
                log.warn('No theme.extend found in Tailwind config');
                return null;
            }
            const registry = this.buildTokenRegistry(themeExtend);
            // Extract and resolve CSS variables
            const cssExtractor = new CSSVariableExtractor_1.CSSVariableExtractor(projectRoot);
            const cssVariables = cssExtractor.extract();
            if (cssVariables.size > 0) {
                log.info(`Resolving CSS variables (${cssVariables.size} variables found)`);
                this.resolveCSSVariables(registry, cssVariables, cssExtractor);
            }
            return registry;
        }
        catch (error) {
            log.warn('Failed to parse Tailwind config:', error);
            return null;
        }
    }
    getFramework() {
        return 'tailwind';
    }
    /**
     * Returns the resolved config file path
     */
    getConfigPath(projectRoot, configPath) {
        return configPath
            ? path.resolve(projectRoot, configPath)
            : this.findConfigPath(projectRoot);
    }
    /**
     * Finds the Tailwind config file path using glob search
     */
    findConfigPath(projectRoot) {
        try {
            const pattern = '**/tailwind.config.{js,ts,cjs,mjs}';
            const matches = glob_1.glob.sync(pattern, {
                cwd: projectRoot,
                absolute: true,
                ignore: [
                    '**/node_modules/**',
                    '**/.git/**',
                    '**/dist/**',
                    '**/build/**',
                ],
                nodir: true,
            });
            if (matches.length === 0) {
                return null;
            }
            // Prefer root-level config if multiple found
            const rootConfig = matches.find((match) => {
                const relativePath = path.relative(projectRoot, match);
                return !relativePath.includes(path.sep);
            });
            return rootConfig || matches[0];
        }
        catch (error) {
            log.warn('Error searching for Tailwind config:', error);
            return null;
        }
    }
    /**
     * Extracts the theme.extend object from the AST
     */
    extractThemeExtend(sourceFile) {
        let themeExtendNode = null;
        const visit = (node) => {
            // Look for: theme: { extend: { ... } }
            if (ts.isPropertyAssignment(node)) {
                const name = node.name.getText(sourceFile);
                if (name === 'theme' &&
                    ts.isObjectLiteralExpression(node.initializer)) {
                    // Found theme object, now look for extend
                    node.initializer.properties.forEach((prop) => {
                        if (ts.isPropertyAssignment(prop) &&
                            prop.name.getText(sourceFile) === 'extend' &&
                            ts.isObjectLiteralExpression(prop.initializer)) {
                            themeExtendNode = prop.initializer;
                        }
                    });
                }
            }
            ts.forEachChild(node, visit);
        };
        visit(sourceFile);
        return themeExtendNode;
    }
    /**
     * Builds the token registry from theme.extend object
     */
    buildTokenRegistry(themeExtend) {
        const registry = {
            colors: {},
            spacing: {},
            fontSize: {},
            borderRadius: {},
            boxShadow: {},
            fontFamily: {},
        };
        themeExtend.properties.forEach((prop) => {
            if (!ts.isPropertyAssignment(prop))
                return;
            const categoryName = prop.name.getText();
            if (!this.SUPPORTED_CATEGORIES.includes(categoryName))
                return;
            const category = categoryName;
            const tokens = this.parseTokenCategory(prop.initializer);
            if (tokens) {
                registry[category] = tokens;
            }
        });
        return registry;
    }
    /**
     * Parses a token category (e.g., colors, spacing) into a token map
     */
    parseTokenCategory(node) {
        if (!ts.isObjectLiteralExpression(node))
            return null;
        const tokens = {};
        node.properties.forEach((prop) => {
            if (!ts.isPropertyAssignment(prop))
                return;
            const tokenName = this.getPropertyName(prop.name);
            if (!tokenName)
                return;
            const value = this.parseTokenValue(prop.initializer);
            if (value !== null) {
                tokens[tokenName] = value;
            }
        });
        return tokens;
    }
    /**
     * Parses a token value - can be a string literal or nested object
     */
    parseTokenValue(node) {
        // String literal: 'primary': '#0D262E'
        if (ts.isStringLiteral(node)) {
            return node.text;
        }
        // Template literal without substitutions: `calc(var(--radius) - 2px)`
        if (ts.isNoSubstitutionTemplateLiteral(node)) {
            return node.text; // Returns content without backticks
        }
        // Template literal with substitutions: `${value}px`
        if (ts.isTemplateExpression(node)) {
            // Reconstruct the template string by combining head, spans, and tail
            let result = node.head.text;
            for (const span of node.templateSpans) {
                // For substitutions, we'd need to evaluate them, but in Tailwind configs
                // this is rare. For now, return the raw text as fallback.
                result += span.literal.text;
            }
            return result;
        }
        // Array literal: ['Satoshi', 'sans-serif'] → "Satoshi, sans-serif"
        if (ts.isArrayLiteralExpression(node)) {
            const elements = [];
            for (const element of node.elements) {
                if (ts.isStringLiteral(element)) {
                    elements.push(element.text);
                }
            }
            return elements.length > 0 ? elements.join(', ') : null;
        }
        // Nested object: 'orange': { '500': '#FF4F00', ... }
        if (ts.isObjectLiteralExpression(node)) {
            const nested = {};
            node.properties.forEach((prop) => {
                if (!ts.isPropertyAssignment(prop))
                    return;
                const key = this.getPropertyName(prop.name);
                if (!key)
                    return;
                const value = this.parseTokenValue(prop.initializer);
                if (typeof value === 'string') {
                    nested[key] = value;
                }
            });
            return Object.keys(nested).length > 0 ? nested : null;
        }
        return null;
    }
    /**
     * Extracts property name from PropertyName node
     */
    getPropertyName(name) {
        if (ts.isIdentifier(name)) {
            return name.text;
        }
        if (ts.isStringLiteral(name)) {
            return name.text;
        }
        if (ts.isNumericLiteral(name)) {
            return name.text;
        }
        return null;
    }
    /**
     * Resolves CSS variable references (var(--name)) in token values
     * Mutates the registry in place
     */
    resolveCSSVariables(registry, variables, extractor) {
        // Iterate through each category
        for (const category of Object.keys(registry)) {
            const tokens = registry[category];
            for (const [tokenName, tokenValue] of Object.entries(tokens)) {
                if (typeof tokenValue === 'string') {
                    // Flat token - resolve directly
                    const resolved = extractor.resolveValue(tokenValue, variables);
                    if (resolved !== tokenValue) {
                        tokens[tokenName] = resolved;
                        log.debug(`Resolved ${category}.${tokenName}: ${tokenValue} -> ${resolved}`);
                    }
                }
                else if (typeof tokenValue === 'object') {
                    // Nested token - resolve each shade
                    for (const [shade, shadeValue] of Object.entries(tokenValue)) {
                        const resolved = extractor.resolveValue(shadeValue, variables);
                        if (resolved !== shadeValue) {
                            tokenValue[shade] = resolved;
                            log.debug(`Resolved ${category}.${tokenName}.${shade}: ${shadeValue} -> ${resolved}`);
                        }
                    }
                }
            }
        }
    }
}
exports.TailwindTokenAdapter = TailwindTokenAdapter;
//# sourceMappingURL=TailwindTokenAdapter.js.map