<?php

defined('BASEPATH') OR exit('No direct script access allowed');

class Cloudlog_hooks {

    private $CI;
    private $plugin_instances = array();

    public function __construct()
    {
        $this->CI = &get_instance();
        $this->CI->load->model('plugins_model');
    }

    public function apply_filters($hook_name, $payload, $context = array())
    {
        $handlers = $this->get_handlers_for_hook($hook_name);
        if (empty($handlers)) {
            return $payload;
        }

        $current = $payload;
        foreach ($handlers as $handler) {
            $plugin_instance = $this->load_plugin_instance($handler['plugin'], $handler['manifest']);
            if (!$plugin_instance) {
                continue;
            }

            $method = $handler['method'];
            if (!method_exists($plugin_instance, $method)) {
                $this->disable_plugin_after_failure(
                    $handler['plugin']->plugin_slug,
                    'Hook method not found [' . $hook_name . '] method=' . $method
                );
                continue;
            }

            try {
                $returned = $plugin_instance->$method($current, $context);
                if ($returned !== null) {
                    $current = $returned;
                }
            } catch (Throwable $e) {
                log_message('error', 'Plugin filter failed [' . $hook_name . '] plugin=' . $handler['plugin']->plugin_slug . ' error=' . $e->getMessage());
                $this->disable_plugin_after_failure(
                    $handler['plugin']->plugin_slug,
                    'Hook filter exception [' . $hook_name . '] ' . $e->getMessage()
                );
            }
        }

        return $current;
    }

    public function do_action($hook_name, $payload = array(), $context = array())
    {
        $handlers = $this->get_handlers_for_hook($hook_name);
        if (empty($handlers)) {
            return;
        }

        foreach ($handlers as $handler) {
            $plugin_instance = $this->load_plugin_instance($handler['plugin'], $handler['manifest']);
            if (!$plugin_instance) {
                continue;
            }

            $method = $handler['method'];
            if (!method_exists($plugin_instance, $method)) {
                $this->disable_plugin_after_failure(
                    $handler['plugin']->plugin_slug,
                    'Hook method not found [' . $hook_name . '] method=' . $method
                );
                continue;
            }

            try {
                $plugin_instance->$method($payload, $context);
            } catch (Throwable $e) {
                log_message('error', 'Plugin action failed [' . $hook_name . '] plugin=' . $handler['plugin']->plugin_slug . ' error=' . $e->getMessage());
                $this->disable_plugin_after_failure(
                    $handler['plugin']->plugin_slug,
                    'Hook action exception [' . $hook_name . '] ' . $e->getMessage()
                );
            }
        }
    }

    private function get_handlers_for_hook($hook_name)
    {
        if (!$this->CI->plugins_model->table_exists()) {
            return array();
        }

        $enabled_plugins = $this->CI->plugins_model->get_enabled();
        if (empty($enabled_plugins)) {
            return array();
        }

        $handlers = array();

        foreach ($enabled_plugins as $plugin) {
            $manifest = json_decode((string)$plugin->plugin_manifest, true);
            if (!is_array($manifest) || !isset($manifest['hooks']) || !is_array($manifest['hooks'])) {
                continue;
            }

            if (!isset($manifest['hooks'][$hook_name])) {
                continue;
            }

            $method = $manifest['hooks'][$hook_name];
            if (!is_string($method) || $method === '') {
                continue;
            }

            $handlers[] = array(
                'plugin' => $plugin,
                'manifest' => $manifest,
                'method' => $method,
            );
        }

        return $handlers;
    }

    private function load_plugin_instance($plugin, $manifest)
    {
        $slug = $plugin->plugin_slug;
        if (isset($this->plugin_instances[$slug])) {
            return $this->plugin_instances[$slug];
        }

        $entry_file = isset($manifest['entry']) ? trim((string)$manifest['entry']) : 'Plugin.php';
        $class_name = isset($manifest['class']) ? trim((string)$manifest['class']) : 'Plugin';

        if (!preg_match('/^[A-Za-z0-9_\/.-]+$/', $entry_file)) {
            log_message('error', 'Plugin entry path invalid for ' . $slug);
            $this->disable_plugin_after_failure($slug, 'Invalid plugin entry path');
            return null;
        }

        if (!preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/', $class_name)) {
            log_message('error', 'Plugin class invalid for ' . $slug);
            $this->disable_plugin_after_failure($slug, 'Invalid plugin class name');
            return null;
        }

        $plugin_path = APPPATH . 'plugins' . DIRECTORY_SEPARATOR . $slug . DIRECTORY_SEPARATOR;
        $entry_path = realpath($plugin_path . $entry_file);
        $plugin_root = realpath($plugin_path);

        if ($plugin_root === false || $entry_path === false || strpos($entry_path, $plugin_root) !== 0) {
            log_message('error', 'Plugin entry file missing or outside plugin root for ' . $slug);
            $this->disable_plugin_after_failure($slug, 'Plugin entry file missing or outside plugin root');
            return null;
        }

        try {
            require_once $entry_path;
        } catch (Throwable $e) {
            log_message('error', 'Plugin entry include failed (' . $slug . '): ' . $e->getMessage());
            $this->disable_plugin_after_failure($slug, 'Plugin entry include failed: ' . $e->getMessage());
            return null;
        }

        if (!class_exists($class_name)) {
            log_message('error', 'Plugin class not found: ' . $class_name . ' (' . $slug . ')');
            $this->disable_plugin_after_failure($slug, 'Plugin class not found: ' . $class_name);
            return null;
        }

        try {
            $instance = new $class_name($this->CI);
        } catch (Throwable $e) {
            log_message('error', 'Plugin construction failed (' . $slug . '): ' . $e->getMessage());
            $this->disable_plugin_after_failure($slug, 'Plugin construction failed: ' . $e->getMessage());
            return null;
        }

        $this->plugin_instances[$slug] = $instance;

        return $instance;
    }

    private function disable_plugin_after_failure($slug, $reason)
    {
        if (!preg_match('/^[a-z0-9_-]+$/', (string)$slug)) {
            return;
        }

        if ($this->CI->plugins_model->table_exists()) {
            $this->CI->plugins_model->set_status($slug, 'disabled');
        }

        unset($this->plugin_instances[$slug]);
        log_message('error', 'Plugin auto-disabled: ' . $slug . ' reason=' . $reason);
    }
}
