<?php

/**
 * Main plugin bootstrap.
 *
 * @package Parkstatic
 */
declare (strict_types = 1);
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
require_once PARKSTATIC_PLUGIN_DIR . 'includes/class-static-file-server.php';
require_once PARKSTATIC_PLUGIN_DIR . 'includes/class-admin-page.php';
require_once PARKSTATIC_PLUGIN_DIR . 'includes/class-static-site-switcher.php';
require_once PARKSTATIC_PLUGIN_DIR . 'includes/class-meta-injector.php';
/**
 * Coordinates plugin lifecycle, options, and component registration.
 */
final class Parkstatic_Plugin {
    public const OPTION_STARTED = 'parkstatic_started';

    public const OPTION_ONBOARDING_COMPLETE = 'parkstatic_onboarding_complete';

    public const OPTION_REPO_URL = 'parkstatic_repo_url';

    public const OPTION_ONBOARDING_STEP = 'parkstatic_onboarding_step';

    public const OPTION_DEPLOY_SECRET = 'parkstatic_deploy_secret';

    public const OPTION_LAST_DEPLOY_AT = 'parkstatic_last_deploy_at';

    public const OPTION_LAST_DEPLOY_ERROR = 'parkstatic_last_deploy_error';

    public const OPTION_REGISTRATION_ERROR = 'parkstatic_registration_error';

    public const OPTION_AUTO_RELEASE = 'parkstatic_auto_release';

    /** Context label for the deterministic, non-secret instance id. */
    private const INSTANCE_CONTEXT = 'parkstatic-instance';

    /**
     * Fixed namespace for the deterministic (UUIDv5) instance id. Its value is
     * arbitrary but must never change, or every site's instance id would move.
     */
    private const INSTANCE_NAMESPACE = '7f3a9c2e-1b4d-5e6f-8a9b-0c1d2e3f4a5b';

    /** WordPress.org free build path. */
    private const FREE_PLUGIN_BASENAME = 'parkstatic/parkstatic.php';

    /**
     * Premium build paths. Freemius uses `premium_slug` (parkstatic_pro) but
     * older zips may have unpacked to parkstatic-premium/.
     *
     * @var string[]
     */
    private const PREMIUM_PLUGIN_BASENAMES = array('parkstatic_pro/parkstatic.php', 'parkstatic-premium/parkstatic.php');

    /** @var self|null */
    private static $instance = null;

    /** @var Parkstatic_Admin_Page */
    private $admin_page;

    /** @var Parkstatic_Endpoint */
    private $endpoint;

    public static function instance() : self {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {
        register_activation_hook( PARKSTATIC_PLUGIN_FILE, array($this, 'activate') );
        add_action(
            'upgrader_process_complete',
            array($this, 'after_plugin_upgrade'),
            10,
            2
        );
        add_action( 'init', array(Parkstatic_Storage::class, 'maybe_migrate_legacy_storage'), 0 );
        $this->admin_page = new Parkstatic_Admin_Page($this);
        Parkstatic_Static_Site_Switcher::register_hooks();
    }

    public function activate() : void {
        $this->ensure_parkstatic_directories();
        set_transient( 'parkstatic_activation_redirect', true, 30 );
    }

    /**
     * @return string[]
     */
    private static function known_plugin_basenames() : array {
        return array_merge( array(self::FREE_PLUGIN_BASENAME), self::PREMIUM_PLUGIN_BASENAMES );
    }

    private function reconcile_active_plugin_entries( string $canonical ) : void {
        $known = self::known_plugin_basenames();
        $active = get_option( 'active_plugins', array() );
        $changed = false;
        $seen = false;
        if ( !is_array( $active ) ) {
            return;
        }
        foreach ( $active as $index => $basename ) {
            if ( !in_array( $basename, $known, true ) ) {
                continue;
            }
            if ( $basename === $canonical ) {
                if ( $seen ) {
                    unset($active[$index]);
                    $changed = true;
                    continue;
                }
                $seen = true;
                continue;
            }
            unset($active[$index]);
            $changed = true;
        }
        if ( $changed ) {
            update_option( 'active_plugins', array_values( $active ) );
        }
        if ( !$seen && file_exists( WP_PLUGIN_DIR . '/' . $canonical ) ) {
            activate_plugin( $canonical );
        }
    }

    private function delete_plugin_directory_without_uninstall( string $directory ) : void {
        if ( !is_dir( $directory ) ) {
            return;
        }
        if ( !function_exists( 'WP_Filesystem' ) ) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }
        global $wp_filesystem;
        if ( null === $wp_filesystem ) {
            WP_Filesystem();
        }
        if ( $wp_filesystem instanceof WP_Filesystem_Base && $wp_filesystem->exists( $directory ) ) {
            $wp_filesystem->delete( $directory, true );
            return;
        }
        Parkstatic_Storage::remove_directory_tree( $directory );
    }

    /**
     * Keeps the plugin active and on the canonical folder after a zip upload update.
     *
     * WordPress replaces the plugin directory during manual uploads. Freemius
     * premium zips historically unpacked to parkstatic-premium/, which could
     * leave active_plugins pointing at a removed path even though the update
     * screen reports success.
     *
     * @param \WP_Upgrader $upgrader Upgrader instance.
     * @param array        $options  Upgrade context.
     */
    public function after_plugin_upgrade( $upgrader, array $options ) : void {
        unset($upgrader);
        $action = $options['action'] ?? '';
        if ( !in_array( $action, array('install', 'update'), true ) || ($options['type'] ?? '') !== 'plugin' || empty( $options['plugins'] ) || !is_array( $options['plugins'] ) ) {
            return;
        }
        $canonical = plugin_basename( PARKSTATIC_PLUGIN_FILE );
        $known = self::known_plugin_basenames();
        if ( !array_intersect( array_merge( array($canonical), $known ), $options['plugins'] ) ) {
            return;
        }
        delete_transient( 'parkstatic_activation_redirect' );
        if ( !file_exists( WP_PLUGIN_DIR . '/' . $canonical ) ) {
            return;
        }
        $this->reconcile_active_plugin_entries( $canonical );
        Parkstatic_Static_Site_Switcher::maybe_refresh_htaccess_for_storage();
    }

    /**
     * Stable, non-secret identity for this site. Derived from the site URL so
     * it survives plugin uninstall/reinstall (one domain == one instance,
     * which matches the licensing model). A domain change is treated as a new
     * instance by design.
     */
    public function get_instance_id() : string {
        return self::uuid5( self::INSTANCE_NAMESPACE, self::INSTANCE_CONTEXT . ':' . $this->get_site_identity() );
    }

    /**
     * RFC 4122 name-based (v5) UUID. Deterministic for a given namespace +
     * name, so the same site always produces the same id while remaining a
     * valid value for a `uuid`-typed column.
     */
    private static function uuid5( string $namespace, string $name ) : string {
        $nhex = str_replace( array('-', '{', '}'), '', $namespace );
        $nbytes = '';
        for ($i = 0, $len = strlen( $nhex ); $i < $len; $i += 2) {
            $nbytes .= chr( (int) hexdec( substr( $nhex, $i, 2 ) ) );
        }
        $hash = sha1( $nbytes . $name );
        return sprintf(
            '%08s-%04s-%04x-%04x-%12s',
            substr( $hash, 0, 8 ),
            substr( $hash, 8, 4 ),
            hexdec( substr( $hash, 12, 4 ) ) & 0xfff | 0x5000,
            hexdec( substr( $hash, 16, 4 ) ) & 0x3fff | 0x8000,
            substr( $hash, 20, 12 )
        );
    }

    private function get_site_identity() : string {
        /**
         * Filter the stable string the instance id is derived from.
         * Defaults to home_url(); override only if you need a
         * site identity that is independent of the home URL.
         *
         * @param string $identity Site identity string.
         */
        return (string) apply_filters( 'parkstatic_site_identity', home_url() );
    }

    /**
     * Public site URL, sent to the backend so it can match this site to its
     * Freemius install when verifying paid status against the Freemius API.
     */
    public function get_site_url() : string {
        return home_url();
    }

    public function ensure_parkstatic_directories() : void {
    }

}
