<?php

/**
 * LinkCentral Redirection Class
 *
 * This class handles the redirection and click tracking functionality for LinkCentral.
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
// Exit if accessed directly
class LinkCentral_Redirection {
    private $url_prefix;

    private $visitor_manager;

    private $device_detector;

    private $geolocation;

    private $dynamic_rules;

    private $click_tracker;

    private $webhook_handler;

    /**
     * Constructor.
     */
    public function __construct(
        $url_prefix,
        $visitor_manager,
        $device_detector,
        $geolocation,
        $dynamic_rules,
        $click_tracker,
        $webhook_handler
    ) {
        $this->url_prefix = $url_prefix;
        $this->visitor_manager = $visitor_manager;
        $this->device_detector = $device_detector;
        $this->geolocation = $geolocation;
        $this->dynamic_rules = $dynamic_rules;
        $this->click_tracker = $click_tracker;
        $this->webhook_handler = $webhook_handler;
    }

    /**
     * Initialize the redirection functionality.
     * 
     * This uses a two-hook approach for optimal performance and WordPress compatibility:
     * 
     * 1. PREFIXED REDIRECTS (early 'init' hook):
     *    - URLs like /{linkcentral_url_prefix}/slug are clearly LinkCentral links
     *    - Caught early before WordPress parses queries, loads themes, etc.
     *    - Maximum performance: checks if the link is a valid LinkCentral link (eg did not disable slug prefix) and redirects immediately if it is
     * 
     * 2. DIRECT REDIRECTS (late 'template_redirect' hook):
     *    - URLs like /slug could be pages, posts, or LinkCentral links
     *    - Let WordPress do its routing first to determine if content exists. If it does, those have priority over LinkCentral links.
     *    - Check for LinkCentral when WordPress finds nothing (is_404)
     * 
     */
    public function init() {
        // Hook early in WordPress process to catch prefixed redirects
        add_action( 'init', array($this, 'maybe_handle_prefixed_redirect'), 1 );
        // Hook later to catch direct slugs only when WordPress finds nothing
        add_action( 'template_redirect', array($this, 'maybe_handle_direct_redirect') );
    }

    /**
     * Check if the current request is for a prefixed LinkCentral link and handle redirection if needed.
     */
    public function maybe_handle_prefixed_redirect() {
        // Don't run in admin or for other special requests
        if ( is_admin() || defined( 'DOING_AJAX' ) && DOING_AJAX || defined( 'DOING_CRON' ) && DOING_CRON ) {
            return;
        }
        // Get current request path
        $request_uri = ( isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '' );
        $path = parse_url( $request_uri, PHP_URL_PATH );
        // Get the URL prefix from options
        $url_prefix = trim( $this->url_prefix, '/' );
        // Get WordPress site URL information
        $site_url = parse_url( site_url(), PHP_URL_PATH );
        $site_path = ( !empty( $site_url ) ? rtrim( $site_url, '/' ) : '' );
        // Create the pattern to match, accounting for WordPress in subdirectory
        $prefix_pattern = $site_path . '/' . $url_prefix . '/';
        $prefix_pattern_no_slash = $site_path . '/' . $url_prefix;
        // Check if the path starts with our prefix pattern or equals just the prefix
        if ( strpos( $path, $prefix_pattern ) === 0 || $path === $prefix_pattern_no_slash ) {
            // Extract the slug from the URL
            $slug = '';
            if ( $path === $prefix_pattern_no_slash ) {
                // No slug provided, just the prefix - set 404 and let WordPress handle it
                global $wp_query;
                $wp_query->set_404();
                return;
            } else {
                // Extract everything after the prefix
                $slug = substr( $path, strlen( $prefix_pattern ) );
                $slug = trim( $slug, '/' );
            }
            // If there's no slug, set 404 and let WordPress handle it
            if ( empty( $slug ) ) {
                global $wp_query;
                $wp_query->set_404();
                return;
            }
            // Check if this is a valid LinkCentral link and if prefix is enabled
            global $wpdb;
            $case_sensitive = get_option( 'linkcentral_case_sensitive_redirects', false );
            $collate = ( $case_sensitive ? 'COLLATE utf8mb4_bin' : '' );
            // First check if the link exists at all
            $link = $wpdb->get_row( $wpdb->prepare( "SELECT p.ID, pm.meta_value as prefix_disabled \n                 FROM {$wpdb->posts} p\n                 LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_linkcentral_disable_slug_prefix'\n                 WHERE p.post_name = %s {$collate}\n                 AND p.post_type = %s\n                 AND p.post_status IN ('publish', 'private', 'draft')\n                 LIMIT 1", $slug, 'linkcentral_link' ) );
            // If link doesn't exist or has prefix disabled, show 404
            if ( !$link || !empty( $link->prefix_disabled ) ) {
                global $wp_query;
                $wp_query->set_404();
                return;
            }
            // Process the redirect using the correct link ID we already found
            $this->process_redirect( $slug, $link->ID );
            exit;
        }
    }

    /**
     * Check if the current request should be handled as a direct LinkCentral slug.
     * Only runs when WordPress hasn't found any content (404 situation).
     */
    public function maybe_handle_direct_redirect() {
        // Only check for direct slugs if WordPress is about to show a 404
        if ( !is_404() ) {
            return;
        }
        // Don't run in admin or for other special requests
        if ( is_admin() || defined( 'DOING_AJAX' ) && DOING_AJAX || defined( 'DOING_CRON' ) && DOING_CRON ) {
            return;
        }
        // Get current request path
        $request_uri = ( isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '' );
        $path = parse_url( $request_uri, PHP_URL_PATH );
        // Get WordPress site URL information
        $site_url = parse_url( site_url(), PHP_URL_PATH );
        $site_path = ( !empty( $site_url ) ? rtrim( $site_url, '/' ) : '' );
        // Extract the potential slug from the path (remove site path)
        $potential_slug = ( $site_path ? substr( $path, strlen( $site_path ) ) : $path );
        $potential_slug = trim( $potential_slug, '/' );
        if ( empty( $potential_slug ) || strlen( $potential_slug ) > 200 || strpos( $potential_slug, '.' ) !== false ) {
            return;
        }
        // Check if this slug exists as a LinkCentral link with prefix disabled
        global $wpdb;
        $case_sensitive = get_option( 'linkcentral_case_sensitive_redirects', false );
        $collate = ( $case_sensitive ? 'COLLATE utf8mb4_bin' : '' );
        $link_id = $wpdb->get_var( $wpdb->prepare(
            "SELECT p.ID FROM {$wpdb->posts} p \n             INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id \n             WHERE p.post_name = %s {$collate}\n             AND p.post_type = %s \n             AND p.post_status IN ('publish', 'private', 'draft')\n             AND pm.meta_key = %s \n             AND pm.meta_value = '1'\n             LIMIT 1",
            $potential_slug,
            'linkcentral_link',
            '_linkcentral_disable_slug_prefix'
        ) );
        if ( !empty( $link_id ) ) {
            $this->process_redirect( $potential_slug, $link_id );
            exit;
        }
    }

    /**
     * Process the redirect for a LinkCentral link.
     */
    private function process_redirect( $slug, $link_id ) {
        $link = get_post( $link_id );
        if ( !$link || $link->post_type !== 'linkcentral_link' ) {
            // Invalid link ID or wrong post type
            return;
        }
        // Fire a hook that developers can use when a valid link is clicked
        // Passes: link ID, slug, and the full link post object
        do_action(
            'linkcentral_link_clicked',
            $link->ID,
            $slug,
            $link
        );
        // Check if the link is a draft, set as private, or scheduled for future
        if ( $link->post_status === 'draft' ) {
            wp_die( '<h3>Inactive link</h3>This link is currently not accessible because it is saved as a <strong>draft</strong>.', 'Inaccessible Link', array(
                'response' => 404,
            ) );
        } elseif ( $link->post_status === 'private' || $link->post_status === 'future' ) {
            if ( !current_user_can( 'edit_posts' ) ) {
                wp_die( 'This link is currently not accessible.', 'Inaccessible Link', array(
                    'response' => 404,
                ) );
            }
        }
        // Check if the link is password protected
        if ( post_password_required( $link->ID ) ) {
            // Display the custom password form
            include LINKCENTRAL_PLUGIN_DIR . 'views/password-form.php';
            exit;
        }
        // Get destination URL
        $reporting_disabled = get_option( 'linkcentral_disable_reporting', false );
        // Try to get cached URL (only when reporting enabled)
        if ( !$reporting_disabled ) {
            $destination_url = $this->visitor_manager->get_cached_destination_url( $link->ID );
        }
        // Calculate destination URL if not cached
        if ( empty( $destination_url ) ) {
            // Get the default destination URL
            $destination_url = get_post_meta( $link->ID, '_linkcentral_destination_url', true );
            // Check for dynamic rules
            if ( linkcentral_fs()->is_premium() ) {
                // specifically checking for premium version and NOT for active subscription so that dynamic rules keep working in case license briefly expires
                $dynamic_rules_data = get_post_meta( $link->ID, '_linkcentral_dynamic_rules', true );
                $dynamic_destination_url = $this->dynamic_rules->get_dynamic_destination_url( $link->ID, $dynamic_rules_data );
                if ( $dynamic_destination_url ) {
                    $destination_url = $dynamic_destination_url;
                }
            }
            // Cache the result (only when reporting enabled)
            if ( $destination_url && !$reporting_disabled ) {
                $this->visitor_manager->set_cached_destination_url( $link->ID, $destination_url );
            }
        }
        // If no destination URL is found, show an error
        if ( empty( $destination_url ) ) {
            wp_die( '<h3>Configuration Error</h3>This link has no destination URL configured.', 'Link Error', array(
                'response' => 500,
            ) );
        }
        $this->click_tracker->record_click( $link->ID, $link->post_name, $destination_url );
        $redirection_type = $this->get_redirection_type( $link->ID );
        // Fire a hook right before the redirect happens
        do_action(
            'linkcentral_before_redirect',
            $link->ID,
            $slug,
            $destination_url,
            $redirection_type,
            $link
        );
        // Set no-cache headers
        nocache_headers();
        header( 'X-Redirect-Powered-By: LinkCentral' );
        // Perform the redirection
        wp_redirect( $destination_url, $redirection_type );
        exit;
    }

    /**
     * Get the redirection type for a link.
     */
    private function get_redirection_type( $link_id ) {
        $redirection_type = get_post_meta( $link_id, '_linkcentral_redirection_type', true );
        if ( $redirection_type === 'default' || !$redirection_type ) {
            $redirection_type = get_option( 'linkcentral_global_redirection_type', '307' );
        }
        return intval( $redirection_type );
    }

}
