"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var ToolHooksService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ToolHooksService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const core_entities_1 = require("../core-entities");
const fs = require("fs/promises");
const path = require("path");
const vm = require("vm");
const readmeData = require("../seeding/data/tool-hooks/readme");
const exampleHookData = require("../seeding/data/tool-hooks/example-hook");
const eslintHookData = require("../seeding/data/tool-hooks/eslint-hook");
let ToolHooksService = ToolHooksService_1 = class ToolHooksService {
    constructor(toolHooksRepository) {
        this.toolHooksRepository = toolHooksRepository;
        this.logger = new common_1.Logger(ToolHooksService_1.name);
        this.projectRoot = process.env.REPOBURG_PROJECT_PATH || process.cwd();
        this.hooksDir = path.join(this.projectRoot, '.repoburg', 'hooks');
        this.ensureHooksDir();
    }
    async ensureHooksDir() {
        try {
            await fs.mkdir(this.hooksDir, { recursive: true });
            await this.createExampleFiles();
        }
        catch (e) {
            this.logger.error(`Failed to create hooks directory: ${e.message}`);
        }
    }
    async createExampleFiles() {
        const hooksToSeed = [readmeData, exampleHookData, eslintHookData];
        for (const hook of hooksToSeed) {
            const filePath = path.join(this.hooksDir, hook.filename);
            try {
                await fs
                    .access(filePath)
                    .catch(() => fs.writeFile(filePath, hook.content, 'utf-8'));
            }
            catch (e) {
                this.logger.warn(`Failed to seed ${hook.filename}: ${e.message}`);
            }
        }
    }
    async findAll() {
        return this.toolHooksRepository.find();
    }
    async create(dto) {
        const hook = this.toolHooksRepository.create(dto);
        return this.toolHooksRepository.save(hook);
    }
    async update(id, dto) {
        const hook = await this.toolHooksRepository.findOneBy({ id });
        if (!hook)
            throw new common_1.NotFoundException(`Hook with ID ${id} not found`);
        Object.assign(hook, dto);
        return this.toolHooksRepository.save(hook);
    }
    async remove(id) {
        await this.toolHooksRepository.delete(id);
    }
    async findByTrigger(toolName, timing) {
        return this.toolHooksRepository.find({
            where: { tool_name: toolName, timing, is_active: true },
        });
    }
    async listScripts() {
        try {
            const files = await fs.readdir(this.hooksDir);
            return files.filter((f) => f.endsWith('.ts') || f.endsWith('.js'));
        }
        catch (e) {
            this.logger.warn(`Could not list scripts: ${e.message}`);
            return [];
        }
    }
    async createScript(filename, content) {
        if (!filename.endsWith('.ts'))
            filename += '.ts';
        const scriptPath = path.join(this.hooksDir, filename);
        await fs.writeFile(scriptPath, content, 'utf-8');
    }
    generateTemplate(toolName) {
        const argsType = this.getArgsInterface(toolName);
        return `/**
 * Hook for tool: ${toolName}
 */

// --- Type Definitions ---

export interface ${this.pascalCase(toolName)}Args {
${argsType}
}

export interface PlanContext {
  originalParsedActions: Array<{ tool_name: string; arguments: any }>;
  feedback: {
    validationErrors: Array<{ tool_name: string; error: string }>;
    invalidToolErrors: Array<{ tool_name: string; arguments: any }>;
  };
  flags: {
    should_halt: boolean;
    halt_reason?: string;
    is_final: boolean;
    follow_up_initiated: boolean;
  };
}

export interface HookAction {
  tool_name: '${toolName}';
  args: ${this.pascalCase(toolName)}Args;
  // Only present for 'after' hooks
  result?: {
    status: 'SUCCESS' | 'FAILURE';
    output?: string;
    error?: string;
  };
}

export interface HookContext {
  hook_type: 'before' | 'after';
  action: HookAction;
  plan_context: PlanContext; 
}

export interface HookOutput {
  should_halt_plan?: boolean;
  message?: string;
  update_context?: Record<string, any>;
}

// --- Hook Logic ---

export default async function(context: HookContext): Promise<HookOutput | void> {
  const { args } = context.action;
  
  // Add your hook logic here

  return {
    should_halt_plan: false
  };
}
`;
    }
    pascalCase(str) {
        return str
            .split('_')
            .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
            .join('');
    }
    getArgsInterface(toolName) {
        switch (toolName) {
            case 'patch':
                return `  file_path: string;
  patch_code: string;`;
            case 'create_file':
            case 'overwrite_file':
            case 'write_todo':
                return `  file_path: string;
  content: string;`;
            case 'delete_file':
                return `  file_path: string;`;
            case 'run_command':
                return `  command_string: string;
  reason?: string;`;
            case 'execute_code':
                return `  code: string;`;
            case 'request_context':
                return `  files?: string;
  folders?: string;`;
            case 'quick_edit':
                return `  file_path: string;
  search_block: string;
  replace_block: string;`;
            case 'apply_diff':
                return `  diff_string: string;`;
            default:
                return `  [key: string]: any;`;
        }
    }
    generateSamplePayload(toolName, timing) {
        const sampleArgs = this.getSampleArgs(toolName);
        return {
            hook_type: timing,
            action: {
                tool_name: toolName,
                args: sampleArgs,
                ...(timing === 'after'
                    ? {
                        result: {
                            status: 'SUCCESS',
                            output: 'Sample output from tool execution',
                        },
                    }
                    : {}),
            },
            plan_context: {
                flags: { should_halt: false },
                feedback: { validationErrors: [] },
            },
        };
    }
    getSampleArgs(toolName) {
        switch (toolName) {
            case 'patch':
                return { file_path: 'src/main.ts', patch_code: '// ... code ...' };
            case 'create_file':
            case 'overwrite_file':
            case 'write_todo':
                return { file_path: 'src/utils.ts', content: 'export const x = 1;' };
            case 'delete_file':
                return { file_path: 'src/temp.ts' };
            case 'execute_code':
                return { code: 'console.log("hello");' };
            case 'request_context':
                return { files: 'src/app.ts' };
            case 'quick_edit':
                return {
                    file_path: 'src/config.ts',
                    search_block: 'port: 3000',
                    replace_block: 'port: 8080',
                };
            default:
                return { key: 'value' };
        }
    }
    async executeHook(scriptFilename, payload) {
        return this.runScript(scriptFilename, payload, console);
    }
    async testHook(scriptFilename, payload) {
        const logs = [];
        const mockConsole = {
            log: (...args) => logs.push(args
                .map((a) => (typeof a === 'object' ? JSON.stringify(a) : String(a)))
                .join(' ')),
            error: (...args) => logs.push('[ERROR] ' +
                args
                    .map((a) => typeof a === 'object' ? JSON.stringify(a) : String(a))
                    .join(' ')),
            warn: (...args) => logs.push('[WARN] ' +
                args
                    .map((a) => typeof a === 'object' ? JSON.stringify(a) : String(a))
                    .join(' ')),
            info: (...args) => logs.push('[INFO] ' +
                args
                    .map((a) => typeof a === 'object' ? JSON.stringify(a) : String(a))
                    .join(' ')),
        };
        try {
            const result = await this.runScript(scriptFilename, payload, mockConsole);
            return { result, logs };
        }
        catch (e) {
            logs.push(`[SYSTEM ERROR] ${e.message}`);
            return { result: null, logs };
        }
    }
    async runScript(scriptFilename, payload, consoleEnv) {
        const scriptPath = path.join(this.hooksDir, scriptFilename);
        try {
            await fs.access(scriptPath);
        }
        catch {
            throw new Error(`Hook script not found: ${scriptFilename}`);
        }
        try {
            const fileContent = await fs.readFile(scriptPath, 'utf8');
            let ts;
            try {
                ts = require('typescript');
            }
            catch (e) {
                throw new Error('TypeScript package is required to execute hooks. Please install it.');
            }
            const { outputText } = ts.transpileModule(fileContent, {
                compilerOptions: {
                    module: ts.ModuleKind.CommonJS,
                    target: ts.ScriptTarget.ES2019,
                    skipLibCheck: true,
                },
            });
            const moduleRef = { exports: {} };
            const sandbox = {
                module: moduleRef,
                exports: moduleRef.exports,
                require: require,
                console: consoleEnv,
                process: process,
            };
            const context = vm.createContext(sandbox);
            const script = new vm.Script(outputText);
            script.runInContext(context);
            const exported = moduleRef.exports;
            const defaultExport = exported.default || exported;
            if (typeof defaultExport !== 'function') {
                throw new Error(`Hook script ${scriptFilename} must export a default function. Found: ${typeof defaultExport}`);
            }
            const result = await defaultExport(payload);
            return result || {};
        }
        catch (e) {
            this.logger.error(`Failed to execute hook ${scriptFilename}: ${e.message}`);
            throw e;
        }
    }
};
exports.ToolHooksService = ToolHooksService;
exports.ToolHooksService = ToolHooksService = ToolHooksService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(core_entities_1.ToolHook)),
    __metadata("design:paramtypes", [typeorm_2.Repository])
], ToolHooksService);
//# sourceMappingURL=tool-hooks.service.js.map