<?php

namespace FooPlugins\FooConvert;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( !class_exists( __NAMESPACE__ . '\Event' ) ) {

    /**
     * The event class that manages creating events for a popup.
     */
    class Event {
        /**
         * Creates a new event and inserts it into the database.
         *
         * @param array $data
         * @param array $meta
         * @return int|void|\WP_Error
         */
        public function create( $data, $meta = array() ) {
            if ( $this->can_create_event( $data, $meta ) ) {
                $user_id = isset( $data['user_id'] ) ? intval( $data['user_id'] ) : null;
                $anonymous_user_guid = isset( $data['anonymous_user_guid'] ) ? $data['anonymous_user_guid'] : null;

                if ( isset( $data['page_url'] ) ) {
                    $data['page_url'] = $this->clean_page_url( $data['page_url'] );
                }

                if ( is_null( $user_id ) && is_user_logged_in() ) {
                    $user_id = get_current_user_id();
                    $anonymous_user_guid = null;
                }

                if ( $user_id > 0 ) {
                    $data['user_id'] = $user_id;
                    $data['anonymous_user_guid'] = null;
                } else {
                    $data['user_id'] = 0;

                    $remote_addr = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
                    $user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';

                    if ( empty( $anonymous_user_guid ) && '' !== $remote_addr && '' !== $user_agent ) {
                        // Fall back to a stable anonymous fingerprint when the
                        // frontend did not supply a GUID from local storage/cookies.
                        $anonymous_user_guid = hash( 'sha256', $remote_addr . $user_agent );
                    }
                    $data['anonymous_user_guid'] = $anonymous_user_guid;
                }

                foreach ( $data as $key => $value ) {
                    if ( is_array( $value ) && empty( $value ) ) {
                        $data[$key] = null;
                    } else if ( $value === '' || ( $key !== 'event_value' && ( $value === 0 || $value === '0' ) ) ) {
                        $data[$key] = null;
                    }
                }

                $template = null;

                if ( is_array( $meta ) ) {
                    $template = isset( $meta['template'] ) ? $meta['template'] : null;
                }

                // Allow others to alter the event data.
                $data = apply_filters( 'fooconvert_event_data', $data, $meta );

                if ( !empty( $template ) ) {
                    $data = apply_filters( 'fooconvert_event_data_by_template-' . $template, $data, $meta );
                }

                if ( !empty( $data ) && is_array( $data ) ) {
                    return Data\Query::insert_event_data( $data );
                }

                return 0;
            }
        }

        /**
         * Will validate if the event can be created for a number of criteria.
         *
         * @param array $data
         * @param array $meta
         * @return true
         */
        private function can_create_event( $data, $meta ) {
            return apply_filters( 'fooconvert_can_create_event', true, $data, $meta );
        }

        /**
         * Cleans the page URL by removing the domain from it.
         *
         * @param string $page_url The URL of the page to clean.
         * @return string The cleaned URL.
         */
        private function clean_page_url( $page_url ) {
            // strip the domain from the URL
            $home_url = home_url();

            if ( strpos( $page_url, $home_url ) === 0 ) {
                return '/' . ltrim( substr( $page_url, strlen( $home_url ) ), '/' );
            }

            return $page_url;
        }

        /**
         * Get a summary of the events for a given popup.
         *
         * @param int $post_id The ID of the popup to get the summary for.
         * @param bool $force
         * @return array An associative array of event metric data.
         */
        public function get_popup_metrics( $post_id, $days = FOOCONVERT_METRICS_DAYS_DEFAULT ) {

            $metric_defaults = apply_filters( 'fooconvert_popup_metrics_defaults', [
                'total_events'          => 0,
                'total_views'           => 0,
                'total_unique_visitors' => 0,
                'total_unique_sessions' => 0,
                'total_returning_visitors' => 0,
                'total_engagements'     => 0,
            ] );

            return apply_filters( 'fooconvert_popup_metrics',
                array_merge( $metric_defaults, Data\Query::get_popup_metrics( $post_id, $days ) ),
                $post_id );
        }

        /**
         * Deletes all events from the database.
         *
         * @return int The number of events deleted.
         */
        public function delete_all_events() {
            return Data\Query::delete_all_events();
        }

        /**
         * Deletes all events for a given popup ID.
         *
         * @param int $post_id The ID of the popup to delete events for.
         *
         * @return int The number of rows deleted.
         */
        public function delete_popup_events( $post_id ) {
            return Data\Query::delete_popup_events( $post_id );
        }


        /**
         * Deletes all events that are not associated with a popup in the posts table.
         *
         * @return int The number of events deleted.
         */
        public function delete_orphaned_events() {
            return Data\Query::delete_orphaned_events();
        }


        /**
         * Retrieves stats we care about for the events table.
         *
         * @return array An associative array of data about the events table, with the following keys:
         *     'Table' => string The name of the table.
         *     'Size_in_MB' => float The size of the table in megabytes.
         *     'Number_of_Rows' => int The number of rows in the table.
         *     'Unique_Popups' => int The number of unique popups represented in the table.
         *     'Orphaned_Events' => int The number of events that are not associated with a popup in the posts table.
         *     'Unique_Orphaned_Popups' => int The number of unique popups that are not associated with a popup in the posts table.
         */
        public function get_event_table_stats() {
            return Data\Query::get_events_table_stats();
        }

        /**
         * Returns an array of daily activity for the given popup.
         *
         * @param int $post_id The ID of the popup.
         * @param int $days The number of days to fetch (default is 7).
         *
         * @return array An array of daily activity, with the following structure:
         *     'event_date' => string The date of the event (format: 'Y-m-d')
         *     'views' => int The number of views
         *     'clicks' => int The number of clicks
         *     'unique_visitors' => int The number of unique visitors
         */
        public function get_popup_daily_activity( $post_id, $days = FOOCONVERT_METRICS_DAYS_DEFAULT ) {
            $post_id = intval( $post_id );

            $results = Data\Query::get_popup_daily_activity( $post_id, $days );

            if ( count( $results ) > 0 && $days < 0 ) {
                // calculate how many days ago the first event was
                $first = new \DateTime( $results[0]['event_date'] );
                $today = new \DateTime('now');
                $interval = $today->diff($first);
                $days = (int)$interval->format('%a');
            }

            if ( $days > 180 ) {
                $days = 180; // ensure we never go back more than 180 days!
            }

            // Loop through the data and ensure dates with no data are set to 0
            $final_data = [];
            for ( $i = $days - 1; $i >= 0; $i-- ) {
                $date = gmdate( 'Y-m-d', strtotime( "-$i days" ) );

                $matching_data = $this->find_row_from_results( $results, 'event_date', $date );

                $default_data = apply_filters( 'fooconvert_popup_daily_activity_default', [
                    'event_date'      => $date,
                    'events'          => 0,
                    'views'           => 0,
                    'engagements'     => 0,
                    'unique_visitors' => 0,
                ] );

                $final_data[] = $matching_data ?? $default_data;
            }

            return apply_filters( 'fooconvert_popup_daily_activity', $final_data, $post_id, $days );
        }

        /**
         * Search for data matching a specific row, based on the key and value.
         *
         * @param array $results The array of data to search.
         * @param string $key The key to search for.
         * @param string $value The value to search for.
         * @return array|null The matching data or null if not found.
         */
        private function find_row_from_results( $results, $key, $value ) {
            foreach ( $results as $row ) {
                if ( isset( $row[$key] ) && $row[$key] === $value ) {
                    return $row;
                }
            }
            return null;
        }

        /**
         * Gets all popup IDs with events.
         *
         * @return int[] The IDs of all popups with events.
         */
        public function get_all_popups_with_events() {
            $popups = Data\Query::get_popups_with_events();
            if ( empty( $popups ) ) {
                return [];
            }

            return array_column( $popups, 'post_id' );
        }

        /**
         * Gets all popup IDs with no events.
         *
         * @return int[] The IDs of all popups with ZERO events.
         */
        public function get_all_popups_with_no_events() {
            $popups = Data\Query::get_popups_with_no_events();
            if ( empty( $popups ) ) {
                return [];
            }

            return array_column( $popups, 'post_id' );
        }

        /**
         * Gets all popup metrics for all popups.
         *
         * @return array An associative array of popup metrics.
         */
        public function get_all_popup_metrics() {
            $metrics = Data\Query::get_all_popup_metrics();

            if ( empty( $metrics ) ) {
                return [];
            }

            $enriched_metrics = [];

            foreach( $metrics as $metric ) {
                $enriched_metrics[] = apply_filters( 
                    'fooconvert_popup_metrics',
                    $metric,
                    $metric['post_id']
                );
            }

            return $enriched_metrics;
        }

        /**
         * Deletes events older than the retention period.
         *
         * @return boolean True on success, false on failure.
         */
        public function delete_old_events() {
            //TODO : how do we ensure that we dont use a retention period if FooConvert PRO is not active?
            $retention = fooconvert_retention();

            return Data\Query::delete_old_events( $retention );
        }

        /**
         * Checks if the events table exists in the database.
         *
         * @return bool True if the table exists, false otherwise.
         */
        public function does_table_exist() {
            $table_name = Data\Schema::get_table_name( FOOCONVERT_DB_TABLE_EVENTS );
            return Data\Schema::does_table_exist( $table_name );
        }

        /**
         * Gets all events of a given type for a given popup, for the last X days.
         *
         * @param int $post_id The ID of the popup.
         * @param string $event_type The type of event to retrieve (e.g. 'view', 'click', 'conversion', 'dismiss').
         * @param int $days The number of days to fetch (default is 7).
         *
         * @return array An array of events for the popup
         */
        public function get_popup_events_of_type( $post_id, $event_type, $days = FOOCONVERT_METRICS_DAYS_DEFAULT ) {
            $post_id = intval( $post_id );
            $days = max( 1, (int) $days );

            return Data\Query::get_popup_events_of_type( $post_id, $event_type, $days );
        }
    }
}
