import path from "path";
import {readFile} from "fs/promises";
import {fileURLToPath} from 'url';
import {execFile} from 'child_process';
import {promisify} from 'util';
import {getSitesMap} from "../utils/sites-map.js";
import {computeHash, deepCopy, getNowTime, urljoin} from "../utils/utils.js";
import {prepareBinary} from "../utils/binHelper.js";
import {md5} from "../libs_drpy/crypto-util.js";
import {fastify} from "../controllers/fastlogger.js";
// import dotenv from 'dotenv';
//
// dotenv.config({ path: path.join(process.cwd(), '.env.development') });

const execFileAsync = promisify(execFile);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const _config_path = path.join(__dirname, '../config');
const _bridge_path = path.join(__dirname, '../spider/php/_bridge.php');

// Cache for module objects
const moduleCache = new Map();

// Mapping from JS method names to PHP Spider method names
const methodMapping = {
    'init': 'init',
    'home': 'homeContent',
    'homeVod': 'homeVideoContent',
    'category': 'categoryContent',
    'detail': 'detailContent',
    'search': 'searchContent',
    'play': 'playerContent',
    'proxy': 'proxy', // Not standard in BaseSpider, but might exist
    'action': 'action' // Not standard
};

// Helper to stringify args for CLI
function stringify(arg) {
    if (arg === undefined) return 'null';
    return JSON.stringify(arg);
}

// Helper to parse JSON output
function json2Object(json) {
    if (!json) return {};
    if (typeof json === 'object') return json;
    try {
        return JSON.parse(json);
    } catch (e) {
        return json;
    }
}

// Execute PHP bridge
const callPhpMethod = async (filePath, methodName, env, ...args) => {
    let phpPath = process.env.PHP_PATH || 'php';
    
    const validPath = prepareBinary(phpPath);
    if (!validPath) {
         throw new Error(`PHP executable not found or invalid: ${phpPath}`);
    }
    phpPath = validPath;

    const phpMethodName = methodMapping[methodName] || methodName;

    const cliArgs = [
        _bridge_path,
        filePath,
        phpMethodName,
        JSON.stringify(env),
        ...args.map(stringify)
    ];

    try {
        // fastify.log.info(`Calling PHP: ${phpPath} ${cliArgs.join(' ')}`);
        const {stdout, stderr} = await execFileAsync(phpPath, cliArgs, {
            encoding: 'utf8',
            maxBuffer: 10 * 1024 * 1024, // 10MB buffer
            env: {
                ...process.env,
                PYTHONIOENCODING: 'utf-8', // Just in case
                // Add any PHP specific env vars if needed
            }
        });

        if (stderr) {
            // Log stderr but don't fail immediately unless stdout is empty or error
            // fastify.log.warn(`PHP Stderr: ${stderr}`);
            console.error(`PHP Stderr: ${stderr}`);
        }

        const result = json2Object(stdout.trim());

        if (result && result.error) {
            throw new Error(`PHP Error: ${result.error}\nTrace: ${result.traceback}`);
        }

        return result;

    } catch (error) {
        console.error(`Error calling PHP method ${methodName}:`, error);
        throw error;
    }
};

const loadEsmWithHash = async function (filePath, fileHash, env) {
    const spiderProxy = {};
    const spiderMethods = Object.keys(methodMapping);

    spiderMethods.forEach(method => {
        spiderProxy[method] = async (...args) => {
            return callPhpMethod(filePath, method, env, ...args);
        };
    });

    return spiderProxy;
};

const init = async function (filePath, env = {}, refresh) {
    try {
        const fileContent = await readFile(filePath, 'utf-8');
        const fileHash = computeHash(fileContent);
        const moduleName = path.basename(filePath, '.php'); // .php extension
        let moduleExt = env.ext || '';

        // Logic for SitesMap and moduleExt (similar to hipy.js)
        let SitesMap = getSitesMap(_config_path);
        if (moduleExt && SitesMap[moduleName]) {
            // ... logic for compressed ext ...
            // Simplified for now, assuming plain string or handled by caller
        }

        let hashMd5 = md5(filePath + '#php#' + moduleExt);

        if (moduleCache.has(hashMd5) && !refresh) {
            const cached = moduleCache.get(hashMd5);
            if (cached.hash === fileHash) {
                return cached.moduleObject;
            }
        }

        fastify.log.info(`Loading PHP module: ${filePath}`);
        let t1 = getNowTime();

        const module = await loadEsmWithHash(filePath, fileHash, env);
        const rule = module;

        // Initialize the spider
        const initValue = await rule.init(moduleExt) || {};

        let t2 = getNowTime();
        const moduleObject = deepCopy(rule);
        moduleObject.cost = t2 - t1;

        moduleCache.set(hashMd5, {moduleObject, hash: fileHash});
        return {...moduleObject, ...initValue};

    } catch (error) {
        fastify.log.error(`Error in php.init :${filePath}`, error);
        throw new Error(`Failed to initialize PHP module:${error.message}`);
    }
};

const getRule = async function (filePath, env) {
    const moduleObject = await init(filePath, env);
    return JSON.stringify(moduleObject);
};

const home = async function (filePath, env, filter = 1) {
    const moduleObject = await init(filePath, env);
    return json2Object(await moduleObject.home(filter));
};

const homeVod = async function (filePath, env) {
    const moduleObject = await init(filePath, env);
    const homeVodResult = json2Object(await moduleObject.homeVod());
    return homeVodResult && homeVodResult.list ? homeVodResult.list : homeVodResult;
};

const category = async function (filePath, env, tid, pg = 1, filter = 1, extend = {}) {
    const moduleObject = await init(filePath, env);
    return json2Object(await moduleObject.category(tid, pg, filter, extend));
};

const detail = async function (filePath, env, ids) {
    const moduleObject = await init(filePath, env);
    return json2Object(await moduleObject.detail(ids));
};

const search = async function (filePath, env, wd, quick = 0, pg = 1) {
    const moduleObject = await init(filePath, env);
    return json2Object(await moduleObject.search(wd, quick, pg));
};

const play = async function (filePath, env, flag, id, flags) {
    const moduleObject = await init(filePath, env);
    return json2Object(await moduleObject.play(flag, id, flags));
};

const proxy = async function (filePath, env, params) {
    const moduleObject = await init(filePath, env);
    return json2Object(await moduleObject.proxy(params));
};

const action = async function (filePath, env, action, value) {
    const moduleObject = await init(filePath, env);
    return json2Object(await moduleObject.action(action, value));
};

export default {
    getRule,
    init,
    home,
    homeVod,
    category,
    detail,
    search,
    play,
    proxy,
    action
};
