import { readdir } from "node:fs/promises";
import { join } from "node:path";
import type { Command } from "@lib/types";
import { config } from "@lib/config";
import type { LoadResult, LoadError } from "./types";
import type { Client } from "../BotClient";
import { logger } from "@lib/logger";

/**
 * Handles loading commands from the file system
 */
export class CommandLoader {
    private client: Client;

    constructor(client: Client) {
        this.client = client;
    }

    /**
     * Load commands from a directory recursively
     */
    async loadFromDirectory(dir: string, reload: boolean = false): Promise<LoadResult> {
        const result: LoadResult = { loaded: 0, skipped: 0, errors: [] };
        await this.scanDirectory(dir, reload, result);
        return result;
    }

    /**
     * Recursively scan directory for command files
     */
    private async scanDirectory(dir: string, reload: boolean, result: LoadResult): Promise<void> {
        try {
            const files = await readdir(dir, { withFileTypes: true });

            for (const file of files) {
                const filePath = join(dir, file.name);

                if (file.isDirectory()) {
                    await this.scanDirectory(filePath, reload, result);
                    continue;
                }

                if ((!file.name.endsWith('.ts') && !file.name.endsWith('.js')) || file.name.endsWith('.test.ts') || file.name.endsWith('.spec.ts')) continue;

                await this.loadCommandFile(filePath, reload, result);
            }
        } catch (error) {
            logger.error(`Error reading directory ${dir}:`, error);
            result.errors.push({ file: dir, error });
        }
    }

    /**
     * Load a single command file
     */
    private async loadCommandFile(filePath: string, reload: boolean, result: LoadResult): Promise<void> {
        try {
            const importPath = reload ? `${filePath}?t=${Date.now()}` : filePath;
            const commandModule = await import(importPath);
            const commands = Object.values(commandModule);

            if (commands.length === 0) {
                logger.warn(`No commands found in ${filePath}`);
                result.skipped++;
                return;
            }

            const category = this.extractCategory(filePath);

            for (const command of commands) {
                if (this.isValidCommand(command)) {
                    command.category = category;

                    const isEnabled = config.commands[command.data.name] !== false;

                    if (!isEnabled) {
                        logger.info(`🚫 Skipping disabled command: ${command.data.name}`);
                        result.skipped++;
                        continue;
                    }

                    this.client.commands.set(command.data.name, command);
                    logger.success(`Loaded command: ${command.data.name}`);
                    result.loaded++;
                } else {
                    logger.warn(`Skipping invalid command in ${filePath}`);
                    result.skipped++;
                }
            }
        } catch (error) {
            logger.error(`Failed to load command from ${filePath}:`, error);
            result.errors.push({ file: filePath, error });
        }
    }

    /**
     * Extract category from file path
     * e.g., /path/to/commands/admin/features.ts -> "admin"
     */
    private extractCategory(filePath: string): string {
        const pathParts = filePath.split('/');
        return pathParts[pathParts.length - 2] ?? "uncategorized";
    }

    /**
     * Type guard to validate command structure
     */
    private isValidCommand(command: any): command is Command {
        return command && typeof command === 'object' && 'data' in command && 'execute' in command;
    }
}
