/*
 * Copyright (C) 2024-present Puter Technologies Inc.
 * 
 * This file is part of Puter.
 * 
 * Puter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

const fs = require("fs");
const path_ = require("path");

const rootdir = path_.resolve(process.argv[2] ?? '.');

const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const { ModuleDoc } = require("./defs");
const processors = require("./processors");

const doc_module = new ModuleDoc();

const handle_file = (code, context) => {
    const ast = parser.parse(code);
    
    const traverse_callbacks = {};
    for ( const processor of processors ) {
        if ( processor.match(context) ) {
            for ( const key in processor.traverse ) {
                if ( ! traverse_callbacks[key] ) {
                    traverse_callbacks[key] = [];
                }
                traverse_callbacks[key].push(processor.traverse[key]);
            }
        }
    }
    for ( const key in traverse_callbacks ) {
        traverse(ast, {
            [key] (path) {
                context.skip = false;
                for ( const callback of traverse_callbacks[key] ) {
                    callback(path, context);
                    if ( context.skip ) return;
                }
            }
        });
    }
}

// Module and class files
{
    const files = fs.readdirSync(rootdir);
    for ( const file of files ) {
        const stat = fs.statSync(path_.join(rootdir, file));
        if ( stat.isDirectory() ) {
            continue;
        }
        if ( ! file.endsWith('.js') ) continue;
        
        const type =
            file.endsWith('Service.js') ? 'service' :
            file.endsWith('Module.js') ? 'module' :
            null;
            
        if ( type === null ) continue;
        
        console.log('file', file);
        const code = fs.readFileSync(path_.join(rootdir, file), 'utf8');

        const firstLine = code.slice(0, code.indexOf('\n'));
        let metadata = {};
        const METADATA_PREFIX = '// METADATA // ';
        if ( firstLine.startsWith(METADATA_PREFIX) ) {
            metadata = JSON.parse(firstLine.slice(METADATA_PREFIX.length));
        }
        
        const context = {
            metadata,
            type,
            doc_module,
            filename: file,
        };
        
        handle_file(code, context);
    }
}

// Library files
if ( fs.existsSync(path_.join(rootdir, 'lib')) ) {
    const files = fs.readdirSync(path_.join(rootdir, 'lib'));
    for ( const file of files ) {
        if ( file.startsWith('_') ) continue;
        
        const code = fs.readFileSync(path_.join(rootdir, 'lib', file), 'utf8');

        const firstLine = code.slice(0, code.indexOf('\n'));
        let metadata = {};
        const METADATA_PREFIX = '// METADATA // ';
        if ( firstLine.startsWith(METADATA_PREFIX) ) {
            metadata = JSON.parse(firstLine.slice(METADATA_PREFIX.length));
        }
        
        const doc_item = doc_module.add_lib();
        doc_item.name = metadata.def ?? file.slice(0, -3);
        
        const context = {
            metadata,
            type: 'lib',
            doc_module,
            doc_item,
            filename: file,
        };
        
        handle_file(code, context);
    }
}

const outfile = path_.join(rootdir, 'README.md');

const out = doc_module.toMarkdown();

fs.writeFileSync(outfile, out);