import { readFile, readdir, stat } from "node:fs/promises";
import path from "node:path";
import { getLogger } from "../../../../util/logger.js";
import { getSystemSkillContent, parseSkillPath, isSkillPath, SkillLayer, SKILL_PATH_PREFIXES, } from "../../../skills/index.js";
import { SYSTEM_SKILLS } from "../../../skills/system/_registry.generated.js";
import { ensureMaterializedSystemSkills } from "../../../skills/system/materialized.js";
export class SkillFileHandler {
    type = "skill";
    logger = getLogger();
    knowledgeManager;
    currentAppRootDirPath;
    systemSkillsDirPath;
    systemSkillsMaterializationPromise;
    constructor(knowledgeManager, appRootDirPath) {
        this.knowledgeManager = knowledgeManager;
        this.logger.info(`[Skills] SkillFileHandler initialized`);
        this.logger.info(`[Skills] Loaded ${Object.keys(SYSTEM_SKILLS).length} system skill files`);
        if (knowledgeManager) {
            this.logger.info(`[Skills] KnowledgeManager available for on-demand facts`);
        }
        if (appRootDirPath) {
            this.prewarmSystemSkillsMaterialization(appRootDirPath);
        }
    }
    // --- Path Checking Helpers ---
    /**
     * Check if path is a system skill path.
     */
    isSkillPathForLayer(layer, virtualPath) {
        return virtualPath.startsWith(SKILL_PATH_PREFIXES[layer]);
    }
    /**
     * Check if this handler manages the given virtual path.
     */
    matches(virtualPath) {
        const isMatch = isSkillPath(virtualPath);
        this.logger.info(`[Skills] matches check: path="${virtualPath}", isMatch=${isMatch}`);
        return isMatch;
    }
    /**
     * Check if a physical path belongs to this handler's domain.
     * App skills are stored at their virtual path (.superblocks/skills/app/).
     */
    matchesPhysical(physicalPath) {
        return (physicalPath.startsWith(".superblocks/skills/") ||
            physicalPath.startsWith(".superblocks/system-skills/"));
    }
    /**
     * Parse a virtual path to extract skill info.
     */
    parse(virtualPath) {
        const info = parseSkillPath(virtualPath);
        if (!info)
            return undefined;
        return {
            type: this.type,
            params: {
                layer: info.layer,
                skillName: info.skillName,
                fileName: info.fileName,
            },
        };
    }
    /**
     * Convert physical path to virtual path.
     * App skills use their physical path as their virtual path.
     */
    toVirtualPath(physicalPath) {
        const systemSkillsPrefix = ".superblocks/system-skills/";
        if (physicalPath.startsWith(systemSkillsPrefix)) {
            const relative = physicalPath.slice(systemSkillsPrefix.length);
            if (!relative) {
                return undefined;
            }
            return `${SKILL_PATH_PREFIXES[SkillLayer.SYSTEM]}${relative}`;
        }
        if (physicalPath.startsWith(".superblocks/skills/")) {
            return physicalPath;
        }
        return undefined;
    }
    // --- Main Read/Write/Delete Operations ---
    /**
     * Read a skill file.
     * Used by Clark's readFile tool for progressive loading.
     */
    async read(virtualPath, options) {
        this.logger.info(`[Skills] Reading skill: ${virtualPath}`);
        let content;
        let skillType = "unknown";
        if (this.isSkillPathForLayer(SkillLayer.SYSTEM, virtualPath)) {
            skillType = SkillLayer.SYSTEM;
            content = await this.readSystemSkill(virtualPath, options);
        }
        else if (this.isSkillPathForLayer(SkillLayer.ORG, virtualPath)) {
            skillType = SkillLayer.ORG;
            content = this.readOrgFact(virtualPath);
        }
        else if (this.isSkillPathForLayer(SkillLayer.INTEGRATION, virtualPath)) {
            skillType = SkillLayer.INTEGRATION;
            content = this.readIntegrationFact(virtualPath);
        }
        else if (this.isSkillPathForLayer(SkillLayer.USER, virtualPath)) {
            skillType = SkillLayer.USER;
            content = this.readUserFact(virtualPath);
        }
        // else if (this.isSkillPathForLayer(SkillLayer.APP, virtualPath)) {
        //   skillType = SkillLayer.APP;
        //   content = await this.readAppSkill(virtualPath, options);
        // }
        if (content) {
            this.logger.info(`[Skills] Loaded ${skillType} skill: ${virtualPath} (${content.length} chars)`);
        }
        else {
            this.logger.info(`[Skills] Skill not found: ${virtualPath}`);
        }
        return content;
    }
    /**
     * Write a skill file.
     * Only allowed for app skills; org/user skills go through Learning API.
     */
    async write(virtualPath, content, options) {
        const result = await this.writeSkillFile(virtualPath, content, options);
        if (!result.success) {
            throw new Error(result.error);
        }
    }
    /**
     * Write a skill file with result object.
     * This is the preferred method for Track 2 integration.
     */
    async writeSkillFile(virtualPath, _content, _options) {
        if (this.isSkillPathForLayer(SkillLayer.SYSTEM, virtualPath)) {
            return { success: false, error: "System skills are read-only" };
        }
        // if (this.isSkillPathForLayer(SkillLayer.APP, virtualPath)) {
        //   return this.writeAppSkill(virtualPath, content, options);
        // }
        // if (
        //   this.isSkillPathForLayer(SkillLayer.ORG, virtualPath) ||
        //   this.isSkillPathForLayer(SkillLayer.INTEGRATION, virtualPath)
        // ) {
        //   return {
        //     success: false,
        //     error: "Org/integration skills must be updated via Learning API",
        //   };
        // }
        // if (this.isSkillPathForLayer(SkillLayer.USER, virtualPath)) {
        //   return {
        //     success: false,
        //     error: "User skills must be updated via Learning API",
        //   };
        // }
        return { success: false, error: "Unknown skill path" };
    }
    /**
     * Delete a skill file.
     * System skills cannot be deleted.
     */
    async delete(virtualPath, _options) {
        if (this.isSkillPathForLayer(SkillLayer.SYSTEM, virtualPath)) {
            throw new Error(`Cannot delete system skill '${virtualPath}'. System skills are managed by the platform.`);
        }
        throw new Error(`Skill deletion not yet implemented for non-system skills.`);
    }
    /**
     * Get metadata for a skill file.
     */
    async getMetadata(virtualPath, options) {
        if (this.isSkillPathForLayer(SkillLayer.SYSTEM, virtualPath)) {
            return this.getSystemSkillMetadata(virtualPath, options);
        }
        // if (this.isSkillPathForLayer(SkillLayer.APP, virtualPath)) {
        //   return this.getAppSkillMetadata(virtualPath, options);
        // }
        // if (this.isSkillPathForLayer(SkillLayer.ORG, virtualPath) || this.isSkillPathForLayer(SkillLayer.INTEGRATION, virtualPath)) {
        //   return undefined;
        // }
        // if (this.isSkillPathForLayer(SkillLayer.USER, virtualPath)) {
        //   return undefined;
        // }
        return undefined;
    }
    // --- Skill Listing ---
    /**
     * List files in a skill directory.
     * Used by Clark's listFiles tool for discovery.
     */
    async listFiles(virtualPath, options) {
        this.logger.info(`[Skills] Listing skills directory: ${virtualPath}`);
        let files = [];
        let skillType = "unknown";
        const info = parseSkillPath(virtualPath);
        if (info?.layer === SkillLayer.SYSTEM) {
            skillType = SkillLayer.SYSTEM;
            files = await this.listSystemSkills(virtualPath, options);
        }
        // else if (
        //   info?.layer === SkillLayer.ORG
        // ) {
        //   skillType = SkillLayer.ORG;
        //   files = await this.listOrgSkills(virtualPath);
        // } else if (
        //   info?.layer === SkillLayer.USER
        // ) {
        //   skillType = SkillLayer.USER;
        //   files = await this.listUserSkills(virtualPath);
        // }
        this.logger.info(`[Skills] Found ${files.length} ${skillType} skills in ${virtualPath}: ${files.map((f) => f.path).join(", ") || "(none)"}`);
        return files;
    }
    /**
     * List skill files for AppShell compatibility.
     * Returns FileMetadata[] for integration with existing listing code.
     */
    async listSkillFilesAsMetadata(virtualPath, options) {
        const files = await this.listFiles(virtualPath, options);
        return files.map((file) => ({
            path: file.path,
            size: file.size ?? 0,
            mtime: file.mtime ?? new Date(),
            isDirectory: file.type === "directory",
            isFile: file.type === "file",
        }));
    }
    // --- System Skills (Track 1: bundled in package, read-only) ---
    async ensureSystemSkillsMaterialized(options) {
        if (this.currentAppRootDirPath === options.appRootDirPath &&
            this.systemSkillsDirPath) {
            return;
        }
        if (this.currentAppRootDirPath === options.appRootDirPath &&
            this.systemSkillsMaterializationPromise) {
            await this.systemSkillsMaterializationPromise;
            return;
        }
        this.currentAppRootDirPath = options.appRootDirPath;
        this.systemSkillsMaterializationPromise = (async () => {
            try {
                const result = await ensureMaterializedSystemSkills(options.appRootDirPath, SYSTEM_SKILLS, this.logger);
                this.systemSkillsDirPath = result.systemSkillsDir;
            }
            catch (error) {
                this.logger.warn(`[Skills] Failed to materialize system skills; using bundled fallback`, { error });
                this.systemSkillsDirPath = undefined;
            }
        })();
        await this.systemSkillsMaterializationPromise;
    }
    prewarmSystemSkillsMaterialization(appRootDirPath) {
        if (this.currentAppRootDirPath === appRootDirPath &&
            this.systemSkillsMaterializationPromise) {
            return;
        }
        this.currentAppRootDirPath = appRootDirPath;
        this.systemSkillsMaterializationPromise = (async () => {
            try {
                const result = await ensureMaterializedSystemSkills(appRootDirPath, SYSTEM_SKILLS, this.logger);
                this.systemSkillsDirPath = result.systemSkillsDir;
            }
            catch (error) {
                this.logger.warn(`[Skills] Failed to prewarm system skills materialization`, { error });
                this.systemSkillsDirPath = undefined;
            }
        })();
    }
    async readSystemSkill(virtualPath, options) {
        await this.ensureSystemSkillsMaterialized(options);
        // Convert virtual path to registry key
        // virtualPath: "skills/system/superblocks-frontend/SKILL.md"
        // registryKey: "superblocks-frontend/SKILL.md"
        const relativePath = virtualPath.slice(SKILL_PATH_PREFIXES[SkillLayer.SYSTEM].length);
        this.logger.info(`[Skills] readSystemSkill: virtualPath=${virtualPath}, relativePath=${relativePath}`);
        if (this.systemSkillsDirPath) {
            const systemSkillPath = path.join(this.systemSkillsDirPath, relativePath);
            try {
                const diskContent = await readFile(systemSkillPath, "utf-8");
                return diskContent;
            }
            catch (error) {
                this.logger.warn(`[Skills] Failed to read materialized system skill, using bundled fallback`, { relativePath, error });
            }
        }
        const content = getSystemSkillContent(relativePath);
        if (content) {
            this.logger.info(`[Skills] Successfully loaded system skill: ${relativePath} (${content.length} chars)`);
        }
        else {
            this.logger.info(`[Skills] System skill not found: ${relativePath}`);
        }
        return content;
    }
    async getSystemSkillMetadata(virtualPath, options) {
        await this.ensureSystemSkillsMaterialized(options);
        const relativePath = virtualPath.slice(SKILL_PATH_PREFIXES[SkillLayer.SYSTEM].length);
        if (this.systemSkillsDirPath) {
            const systemSkillPath = path.join(this.systemSkillsDirPath, relativePath);
            try {
                const fileStats = await stat(systemSkillPath);
                return {
                    path: virtualPath,
                    size: fileStats.size,
                    mtime: fileStats.mtime,
                    isDirectory: false,
                    isFile: true,
                };
            }
            catch (error) {
                this.logger.warn(`[Skills] Failed to stat materialized system skill, using bundled fallback`, { relativePath, error });
            }
        }
        const content = getSystemSkillContent(relativePath);
        if (!content) {
            return undefined;
        }
        return {
            path: virtualPath,
            size: content.length,
            mtime: new Date(), // No real mtime for bundled content
            isDirectory: false,
            isFile: true,
        };
    }
    async listSystemSkills(virtualPath, options) {
        await this.ensureSystemSkillsMaterialized(options);
        if (this.systemSkillsDirPath) {
            const diskEntries = await this.listSystemSkillsFromDisk(virtualPath);
            if (diskEntries.length > 0) {
                return diskEntries;
            }
        }
        return this.listSystemSkillsFromRegistry(virtualPath);
    }
    async listSystemSkillsFromDisk(virtualPath) {
        if (!this.systemSkillsDirPath) {
            return [];
        }
        const relativeDir = virtualPath.startsWith(SKILL_PATH_PREFIXES[SkillLayer.SYSTEM])
            ? virtualPath
                .slice(SKILL_PATH_PREFIXES[SkillLayer.SYSTEM].length)
                .replace(/^\/+|\/+$/g, "")
            : "";
        const absoluteDir = relativeDir
            ? path.join(this.systemSkillsDirPath, relativeDir)
            : this.systemSkillsDirPath;
        try {
            const dirEntries = await readdir(absoluteDir, { withFileTypes: true });
            const files = [];
            for (const entry of dirEntries) {
                const childRelativePath = relativeDir
                    ? `${relativeDir}/${entry.name}`
                    : entry.name;
                const virtualChildPath = `${SKILL_PATH_PREFIXES[SkillLayer.SYSTEM]}${childRelativePath}`;
                if (entry.isDirectory()) {
                    files.push({
                        path: virtualChildPath,
                        type: "directory",
                        readOnly: true,
                    });
                    continue;
                }
                const filePath = path.join(absoluteDir, entry.name);
                const fileStats = await stat(filePath);
                files.push({
                    path: virtualChildPath,
                    type: "file",
                    readOnly: true,
                    size: fileStats.size,
                    mtime: fileStats.mtime,
                });
            }
            files.sort((a, b) => a.path.localeCompare(b.path));
            return files;
        }
        catch {
            return [];
        }
    }
    listSystemSkillsFromRegistry(virtualPath) {
        const entries = [];
        // Determine the prefix to filter by
        let filterPrefix = "";
        if (virtualPath.startsWith(SKILL_PATH_PREFIXES[SkillLayer.SYSTEM])) {
            filterPrefix = virtualPath.slice(SKILL_PATH_PREFIXES[SkillLayer.SYSTEM].length);
        }
        // Track directories we've seen to avoid duplicates
        const seenDirs = new Set();
        for (const [key, rawContent] of Object.entries(SYSTEM_SKILLS)) {
            const content = rawContent;
            // Check if this file matches the filter
            if (filterPrefix && !key.startsWith(filterPrefix)) {
                continue;
            }
            // Get the path relative to the filter
            const relativePath = filterPrefix
                ? key.slice(filterPrefix.length).replace(/^\//, "")
                : key;
            // Check if this is a direct child or in a subdirectory
            const parts = relativePath.split("/");
            if (parts.length === 1) {
                // Direct file
                const itemVirtualPath = `${SKILL_PATH_PREFIXES[SkillLayer.SYSTEM]}${key}`;
                entries.push({
                    path: itemVirtualPath,
                    type: "file",
                    readOnly: true,
                    size: content.length,
                    mtime: new Date(),
                });
            }
            else {
                // In a subdirectory - add the directory if not seen
                const dirName = parts[0]; // Safe: parts.length > 1 guaranteed here
                const dirKey = filterPrefix ? `${filterPrefix}/${dirName}` : dirName;
                if (!seenDirs.has(dirKey)) {
                    seenDirs.add(dirKey);
                    const itemVirtualPath = `${SKILL_PATH_PREFIXES[SkillLayer.SYSTEM]}${dirKey}`;
                    entries.push({
                        path: itemVirtualPath,
                        type: "directory",
                        readOnly: true,
                    });
                }
            }
        }
        return entries;
    }
    // --- On-Demand Facts (org/user/integration knowledge from KnowledgeManager) ---
    /**
     * Format a fact as markdown content for Clark to read.
     */
    formatFactAsKnowledge(fact, type) {
        return `---
name: ${fact.name}
type: ${type}
---

# ${fact.name}

${fact.description}

## Content

${fact.content}`;
    }
    /**
     * Read an org fact.
     * Path format: skills/org/{factId}/KNOWLEDGE.md
     */
    readOrgFact(virtualPath) {
        this.logger.info(`[Skills] readOrgFact called with path: ${virtualPath}`);
        if (!this.knowledgeManager) {
            this.logger.warn(`[Skills] No KnowledgeManager available for org fact`);
            return undefined;
        }
        // Extract factId from path: skills/org/{factId}/KNOWLEDGE.md
        const relativePath = virtualPath.slice(SKILL_PATH_PREFIXES[SkillLayer.ORG].length);
        const parts = relativePath.split("/");
        const factId = parts[0];
        this.logger.info(`[Skills] readOrgFact: relativePath="${relativePath}", factId="${factId}"`);
        if (!factId) {
            this.logger.warn(`[Skills] Could not extract factId from path: ${virtualPath}`);
            return undefined;
        }
        const fact = this.knowledgeManager.getFactById(factId);
        if (!fact) {
            this.logger.warn(`[Skills] Org fact not found in cache: ${factId}`);
            return undefined;
        }
        this.logger.info(`[Skills] readOrgFact: found fact "${fact.name}" (${factId})`);
        return this.formatFactAsKnowledge(fact, "org");
    }
    /**
     * Read a user fact.
     * Path format: skills/user/{factId}/KNOWLEDGE.md
     */
    readUserFact(virtualPath) {
        this.logger.info(`[Skills] readUserFact called with path: ${virtualPath}`);
        if (!this.knowledgeManager) {
            this.logger.warn(`[Skills] No KnowledgeManager available for user fact`);
            return undefined;
        }
        // Extract factId from path: skills/user/{factId}/KNOWLEDGE.md
        const relativePath = virtualPath.slice(SKILL_PATH_PREFIXES[SkillLayer.USER].length);
        const parts = relativePath.split("/");
        const factId = parts[0];
        this.logger.info(`[Skills] readUserFact: relativePath="${relativePath}", factId="${factId}"`);
        if (!factId) {
            this.logger.warn(`[Skills] Could not extract factId from path: ${virtualPath}`);
            return undefined;
        }
        const fact = this.knowledgeManager.getFactById(factId);
        if (!fact) {
            this.logger.warn(`[Skills] User fact not found in cache: ${factId}`);
            return undefined;
        }
        this.logger.info(`[Skills] readUserFact: found fact "${fact.name}" (${factId})`);
        return this.formatFactAsKnowledge(fact, "user");
    }
    /**
     * Read an integration fact.
     * Path format: skills/integrations/{integrationId}/{factId}/KNOWLEDGE.md
     */
    readIntegrationFact(virtualPath) {
        this.logger.info(`[Skills] readIntegrationFact called with path: ${virtualPath}`);
        if (!this.knowledgeManager) {
            this.logger.warn(`[Skills] No KnowledgeManager available for integration fact`);
            return undefined;
        }
        // Extract integrationId and factId from path: skills/integrations/{integrationId}/{factId}/KNOWLEDGE.md
        const relativePath = virtualPath.slice(SKILL_PATH_PREFIXES[SkillLayer.INTEGRATION].length);
        const parts = relativePath.split("/");
        const integrationId = parts[0];
        const factId = parts[1];
        this.logger.info(`[Skills] readIntegrationFact: relativePath="${relativePath}", integrationId="${integrationId}", factId="${factId}"`);
        if (!integrationId || !factId) {
            this.logger.warn(`[Skills] Could not extract integrationId/factId from path: ${virtualPath}`);
            return undefined;
        }
        const fact = this.knowledgeManager.getFactById(factId);
        if (!fact) {
            this.logger.warn(`[Skills] Integration fact not found in cache: ${factId}`);
            return undefined;
        }
        this.logger.info(`[Skills] readIntegrationFact: found fact "${fact.name}" (${factId})`);
        return this.formatFactAsKnowledge(fact, `integration`);
    }
}
//# sourceMappingURL=skill-handler.js.map