<?php
/**
 * MainWP Database Site Actions
 *
 * This file handles all interactions with the Site Actions DB.
 *
 * @package MainWP/Dashboard
 */

namespace MainWP\Dashboard\Module\Log;

use MainWP\Dashboard\MainWP_DB;
use MainWP\Dashboard\MainWP_Logger;
use MainWP\Dashboard\MainWP_Utility;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}


/**
 * Class Log_Changes_Logs_Helper
 *
 * @package MainWP\Dashboard
 */
class Log_Changes_Logs_Helper {


    /**
     * Protected static variable to hold the single instance of the class.
     *
     * @var mixed Default null
     */
    protected static $instance = null;

    /**
     * Holds the last log created time.
     *
     * @var array
     */
    private static $last_log_created = array();

    /**
     * Return the single instance of the class.
     *
     * @return mixed $instance The single instance of the class.
     */
    public static function instance() {
        if ( is_null( static::$instance ) ) {
            static::$instance = new self();
        }
        return static::$instance;
    }

    /**
     * Plugin constructor.
     *
     * Run each time the class is called.
     */
    public function __construct() {
    }

    /**
     * Method get_sync_changes_logs_last_created().
     *
     * Sync site changes logs data.
     *
     * @param int $site_id site id.
     *
     * @return bool
     */
    public function get_sync_changes_logs_last_created( $site_id ) {

        if ( ! isset( static::$last_log_created[ $site_id ] ) ) {
            $site_opts = MainWP_DB::instance()->get_website_options_array( $site_id, array( 'changes_logs_sync_last_created' ) );

            if ( ! is_array( $site_opts ) ) {
                $site_opts = array();
            }

            static::$last_log_created[ $site_id ] = isset( $site_opts['changes_logs_sync_last_created'] ) ? $site_opts['changes_logs_sync_last_created'] : 0;

            // to sure.
            if ( empty( static::$last_log_created[ $site_id ] ) ) {
                static::$last_log_created[ $site_id ] = time() - DAY_IN_SECONDS; // to prevent "strange" issue.
                MainWP_DB::instance()->update_website_option( $site_id, 'changes_logs_sync_last_created', static::$last_log_created[ $site_id ] );
            }
        }

        return static::$last_log_created[ $site_id ];
    }


    /**
     * Method sync_changes_logs().
     *
     * Sync site changes logs data.
     *
     * @param int    $site_id site id.
     * @param array  $sync_changes action data.
     * @param object $website website data.
     *
     * @return bool
     */
    public function sync_changes_logs( $site_id, $sync_changes, $website ) { // phpcs:ignore -- NOSONAR - complex.

        MainWP_Logger::instance()->log_events( 'sites-changes', 'Sync changes logs :: [site_id=' . $site_id . '] :: [count=' . ( is_array( $sync_changes ) ? count( $sync_changes ) : 0 ) . ']' );

        if ( empty( $sync_changes ) || ! is_array( $sync_changes ) ) {
            return false;
        }

        $sync_last_created          = $this->get_sync_changes_logs_last_created( $site_id );
        $new_last_created           = 0;
        $disabled_type_last_created = 0;

        $created_success = false;

        $ignored_meta_fields = apply_filters( 'mainwp_module_log_sync_ignored_meta_fields', array( 'userdata', 'user_meta' ) );

        if ( ! is_array( $ignored_meta_fields ) ) {
            $ignored_meta_fields = array();
        }

        foreach ( $sync_changes as $data ) {

            if ( ! is_array( $data ) || empty( $data['created_on'] ) || empty( $data['log_type_id'] ) ) {
                continue;
            }

            $item_created = round( (float) $data['created_on'], 4 ); // to fix float comparing issue.

            if ( $item_created <= $sync_last_created ) {
                continue;
            }

            // to fix issue of last sync value.
            if ( $new_last_created < $item_created ) {
                $new_last_created = $item_created;
            }

            $enable_log_type = apply_filters( 'mainwp_module_log_enable_insert_log_type', true, $data );
            if ( ! $enable_log_type ) {
                // to fix issue of last sync value.
                if ( $disabled_type_last_created < $item_created ) {
                    $disabled_type_last_created = $item_created;
                }
                continue;
            }

            $type_id    = intval( $data['log_type_id'] );
            $user_id    = ! empty( $data['user_id'] ) ? sanitize_text_field( $data['user_id'] ) : 0;
            $user_login = ! empty( $data['username'] ) ? sanitize_text_field( $data['username'] ) : '';

            if ( empty( $user_login ) ) {
                continue;
            }

            $meta_data = isset( $data['meta_data'] ) && is_array( $data['meta_data'] ) ? $data['meta_data'] : array();
            $user_meta = isset( $meta_data['user_meta'] ) && is_array( $meta_data['user_meta'] ) ? $meta_data['user_meta'] : array();

            $meta                   = array();
            $meta['user_meta_json'] = wp_json_encode( $user_meta );
            $meta['client_ip']      = $data['client_ip'];

            if ( is_array( $meta_data ) ) {
                foreach ( $meta_data as $key => $val ) {
                    if ( in_array( $key, $ignored_meta_fields ) ) {
                        continue;
                    }
                    $meta[ $key ] = $val;
                }
            }

            $action = isset( $data['action_name'] ) ? $this->map_changes_action( $data['action_name'] ) : '';

            $context = '';

            if ( isset( $data['context'] ) ) {
                $context = $this->map_change_logs_context( $data['context'] );
            } elseif ( isset( $data['log_type_id'] ) ) {
                $context = $this->map_change_logs_context( '', $data['log_type_id'] );
            }

            $duration = isset( $data['duration'] ) ? sanitize_text_field( $data['duration'] ) : 0; // sanitize_text_field for seconds.
            $created  = isset( $data['created_on'] ) ? (float) ( $data['created_on'] ) : microtime( true );

            $record_mapping = array(
                'site_id'     => $site_id,
                'user_id'     => $user_id,
                'user_login'  => $user_login,
                'created'     => $created,
                'context'     => $context,
                'action'      => $action,
                'state'       => 1,
                'duration'    => $duration,
                'meta'        => $meta,
                'log_type_id' => $type_id,
            );

            $sum = 'wordpress' !== $record_mapping['context'] ? esc_html( ucfirst( rtrim( $record_mapping['context'], 's' ) ) ) : 'WordPress'; //phpcs:ignore -- wordpress text.
            $record_mapping['item'] = $sum;
            $inserted_id            = apply_filters( 'mainwp_sync_site_log_changes_logs', false, $website, $record_mapping );
            if ( $inserted_id ) {
                $created_success = true;
            }
        }

        // to fix last created sync.
        $update_sync_last_created = 0;
        if ( $new_last_created && $created_success ) {
            $update_sync_last_created = $new_last_created;
        }

        if ( $disabled_type_last_created && $update_sync_last_created < $disabled_type_last_created ) {
            $update_sync_last_created = $disabled_type_last_created;
        }

        if ( ! empty( $update_sync_last_created ) ) {
            MainWP_DB::instance()->update_website_option( $site_id, 'changes_logs_sync_last_created', $update_sync_last_created );
        }

        return true;
    }

    /**
     * Method map_changes_action.
     *
     * @param string $event Logs event.
     *
     * @return string Dashboard logs actions to store in db.
     */
    public function map_changes_action( $event ) {

        $map_actions = array(
            'installed'   => 'install',
            'deleted'     => 'delete',
            'activated'   => 'activate',
            'deactivated' => 'deactivate',
        );

        return isset( $map_actions[ $event ] ) ? $map_actions[ $event ] : $event;
    }

    /**
     * Method map_change_logs_context()
     *
     * @param string $context Log context|object.
     * @param int    $type_id Logs type code.
     *
     * @return string Dashboard logs context to store in db.
     */
    public function map_change_logs_context( $context, $type_id = 0 ) {
        if ( ! empty( $type_id ) ) {
            $context = isset( static::get_changes_logs_types( $type_id )['context'] ) ? static::get_changes_logs_types( $type_id )['context'] : '';
        }
        $context = 'cron-job' === $context ? 'cron' : $context;
        return \apply_filters( 'mainwp_module_log_changes_logs_mapping_contexts', $context, $type_id );
    }

    /**
     * Get custom event title.
     *
     * @param int    $log_type_id    - Event type id.
     * @param array  $data    - Event data.
     * @param string $type    - Title type.
     *
     * @return string
     */
    public static function get_changes_log_title( $log_type_id, $data, $type ) { // phpcs:ignore -- NOSONAR - complex.

        if ( ! is_array( $data ) ) {
            $data = array();
        }

        $title = '';

        if ( 'action' === $type ) {
            if ( 1965 === (int) $log_type_id ) {
                $title = isset( static::get_changes_logs_types( $log_type_id )['action_name'] ) ? static::get_changes_logs_types( $log_type_id )['action_name'] : '';
                if ( ! empty( $title ) ) {
                    $title = ucfirst( $title );
                }
            } else {
                $title = isset( static::get_changes_logs_types( $log_type_id )['msg'] ) ? static::get_changes_logs_types( $log_type_id )['msg'] : '';
            }
        } elseif ( 'object' === $type ) {
            $title = isset( static::get_changes_logs_types( $log_type_id )['object_msg'] ) ? static::get_changes_logs_types( $log_type_id )['object_msg'] : '';
        }

        if ( ! empty( $title ) ) {
            if ( isset( $data['action'] ) ) {
                $title = str_replace( '%action%', $data['action'], $title );
            }
            if ( isset( $data['plugin'] ) ) {
                $title = str_replace( '%plugin%', $data['plugin'], $title );
            }
            if ( isset( $data['theme'] ) ) {
                $title = str_replace( '%theme%', $data['theme'], $title );
            }
            if ( isset( $data['name'] ) ) {
                $title = str_replace( '%name%', $data['name'], $title );
            }
            return $title;
        }
        return '';
    }

    /**
     * Method get_changes_logs_types().
     *
     * @param int $type_id Logs type code.
     *
     * @return array data.
     */
    public static function get_changes_logs_types( $type_id = null ) { //phpcs:ignore -- NOSONAR - long function.

        static $defaults;
        if ( null === $defaults ) {
            $defaults = include MAINWP_MODULES_DIR . 'logs/includes/default-logs.php'; //phpcs:ignore -- NOSONAR ok.
        }

        if ( ! is_array( $defaults ) ) {
            $defaults = array();
        }

        if ( null !== $type_id ) {
            return is_scalar( $type_id ) && isset( $defaults[ $type_id ] ) ? $defaults[ $type_id ] : array();
        }
        return $defaults;
    }

    /**
     * Method get_history_changes().
     *
     * @param array $args Arguments.
     *
     * @return array Results.
     */
    public function get_history_changes( $args ) { // phpcs:ignore -- NOSONAR - complex.

        if ( ! is_array( $args ) ) {
            $args = array();
        }

        $results = Log_DB_Helper::instance()->get_changes_logs_by( $args );

        if ( ! is_array( $results ) ) {
            $results = array();
        }

        $lists = array();

        $manager = Log_Manager::instance();

        $index_by_date = ! empty( $args['target_date'] ) ? 'name' : 'date';

        $get_name = ! empty( $args['slug'] ) || ! empty( $args['name'] );

        $name_title = '';

        $tbl_list = new Log_Events_List_Table( $manager );

        if ( ! empty( $results['items'] ) ) {
            foreach ( $results['items'] as $item ) {
                if ( empty( $item->log_id ) ) {
                    continue;
                }
                $created_micro = (int) $item->created;
                $record        = new Log_Record( $item );

                $created_sec = (int) $record->created;

                $details = array(
                    'author_name' => $tbl_list->get_log_author_name( $record ),
                    'source'      => 'non-mainwp-changes' === $record->connector ? 'WP Admin' : 'Dashboard',
                    'at_hour'     => MainWP_Utility::format_time( $created_sec ),
                    'connector'   => $record->connector,
                    'context'     => $record->context,
                    'action'      => $record->action,
                    'event'       => $tbl_list->get_event_title( $record, 'action' ),
                    'log_id'      => $item->log_id,
                );

                $slugidx   = '';
                $slug      = '';
                $name_meta = '';

                if ( ! empty( $record->meta ) && is_array( $record->meta ) ) {
                    $slug      = ! empty( $record->meta['slug'] ) ? $record->meta['slug'] : '';
                    $name_meta = ! empty( $record->meta['name'] ) ? $record->meta['name'] : '';
                }

                if ( empty( $slug ) && ! empty( $record->extra_meta ) ) {
                    $extra_data = json_decode( $record->extra_meta, true );
                    if ( is_array( $extra_data ) && ! empty( $extra_data['slug'] ) ) {
                        $slug = $extra_data['slug'];
                    }
                }

                if ( ! empty( $slug ) ) {
                    if ( false !== strpos( $slug, '/' ) ) {
                        $parts   = explode( '/', $slug );
                        $slugidx = $parts[0];
                    } else {
                        $slugidx = basename( $slug, '.php' );
                    }
                    $slugidx = strtolower( $slugidx );
                } elseif ( ! empty( $name_meta ) ) {
                    $slugidx = strtolower( sanitize_title( $name_meta ) );
                }

                if ( empty( $slugidx ) && ! empty( $record->item ) ) {
                    $slugidx = strtolower( sanitize_title( $record->item ) );
                }

                if ( 'date' === $index_by_date ) {

                    $day = MainWP_Utility::get_local_date_by_utc_timestamp( $created_micro / 1000000 ); // float.

                    if ( ! isset( $lists[ $day ] ) ) {
                        $lists[ $day ] = array();
                    }

                    $list_item = array(
                        'date'    => MainWP_Utility::format_date( $created_sec ),
                        'details' => $details,
                    );

                    if ( $get_name && empty( $name_title ) ) {
                        $name_title = $tbl_list->get_object_title( $record, false, false );
                    }

                    if ( 'updated' === $record->action || 'update' === $record->action ) {

                        if ( ! empty( $record->extra_meta ) ) {
                            $meta = json_decode( $record->extra_meta, true );
                        } else {
                            $meta = $record->meta;
                        }

                        if ( ! is_array( $meta ) ) {
                            $meta = array();
                        }

                        if ( ! empty( $meta['version'] ) ) {
                            $list_item['details']['version'] = $meta['version'];
                        }
                        if ( ! empty( $meta['old_version'] ) ) {
                            $list_item['details']['old_version'] = $meta['old_version'];
                        }
                    }

                    $lists[ $day ][] = $list_item;

                } else { // group by plugin/theme name.

                    $group_key = $slugidx . '_' . $record->site_id;

                    if ( ! isset( $lists[ $group_key ] ) ) {
                        $lists[ $group_key ] = array();
                    }

                    $list_item = array(
                        'name'      => $tbl_list->get_object_title( $record ),
                        'site_id'   => $record->site_id,
                        'item_name' => $name_meta,
                        'item_slug' => $slug,
                        'details'   => $details,
                    );

                    if ( 'updated' === $record->action || 'update' === $record->action ) {

                        if ( ! empty( $record->extra_meta ) ) {
                            $meta = json_decode( $record->extra_meta, true );
                        } else {
                            $meta = $record->meta;
                        }

                        if ( ! is_array( $meta ) ) {
                            $meta = array();
                        }

                        if ( ! empty( $meta['version'] ) ) {
                            $list_item['details']['version'] = $meta['version'];
                        }
                        if ( ! empty( $meta['old_version'] ) ) {
                            $list_item['details']['old_version'] = $meta['old_version'];
                        }
                    }

                    $lists[ $group_key ][] = $list_item;

                }
            }
        }

        $resp = array(
            'list' => $lists,
        );

        if ( ! empty( $results['onward_time'] ) ) {
            $resp['onward_date'] = MainWP_Utility::format_timezone( $results['onward_time'] );
        }

        if ( ! empty( $name_title ) ) {
            $resp['name_title'] = $name_title;
        }

        return $resp;
    }
}
