<?php
/**
 * Plugin Name:       ideo Field Porter
 * Description:       Import and export any custom post type via Excel (.xlsx), CSV or XML files, with full ACF field and taxonomy support. No external dependencies.
 * Version:           1.1.8
 * Requires at least: 6.2
 * Tested up to:      7.0
 * Requires PHP:      7.2
 * Author:            ideo
 * Author URI:        https://www.studio-ideo.com
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       ideo-field-porter
 * Domain Path:       /languages
 */

defined( 'ABSPATH' ) || exit;

// ── Freemius SDK ──────────────────────────────────────────────────────────
// Initialized unconditionally, before the collision guard below — the free
// version also carries the SDK (per Freemius's own guidance for products
// with separate free/premium codebases), with is_premium set to false. As
// in the PRO file, we deliberately skip Freemius's built-in free/premium
// "sibling" auto-deactivation (set_basename()) and keep our own tested
// mutual-exclusion system instead — see the PRO file for the full rationale.
if ( ! function_exists( 'ideofp_fs' ) ) {
	// Create a helper function for easy SDK access.
	function ideofp_fs() {
		global $ideofp_fs;

		if ( ! isset( $ideofp_fs ) ) {
			// Include Freemius SDK.
			require_once dirname( __FILE__ ) . '/vendor/freemius/start.php';

			$ideofp_fs = fs_dynamic_init( array(
				'id'                  => '34453',
				'slug'                => 'ideo-field-porter',
				'premium_slug'        => 'ideo-field-porter-pro',
				'type'                => 'plugin',
				'public_key'          => 'pk_5401db70ab8f58a72e4f1669f7b30',
				'is_premium'          => false,
				'has_premium_version' => true,
				'has_addons'          => false,
				'has_paid_plans'      => true,
				'is_org_compliant'    => true,
				'menu'                => array(
					'support' => false,
				),
			) );
		}

		return $ideofp_fs;
	}

	// Init Freemius.
	ideofp_fs();
	// Signal that SDK was initiated.
	do_action( 'ideofp_fs_loaded' );

	// Point the SDK's icon (used on the in-dashboard pricing page, opt-in
	// screen, etc.) to our own bundled icon rather than relying on Freemius's
	// default auto-fetch from the WordPress.org listing — which has nothing
	// to fetch yet while this plugin is still awaiting its initial review
	// (no /assets/ icon on WordPress.org SVN until then).
	ideofp_fs()->add_filter( 'plugin_icon', function () {
		return dirname( __FILE__ ) . '/includes/images/field-porter-icon.svg';
	} );

	// Suppress Freemius's own sticky admin notices (opt-in confirmation, etc.)
	// specifically on our own plugin pages — our Settings page header is
	// `position: fixed`, and these notices otherwise render in normal
	// document flow and get visually trapped underneath it, close button
	// included. They still show normally everywhere else in wp-admin (e.g.
	// the Plugins page) until dismissed there.
	ideofp_fs()->add_filter( 'show_admin_notice', function ( $show ) {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page identification, no state change.
		$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
		if ( 'ideo-field-porter-settings' === $page || 0 === strpos( $page, 'spreadsheet-importer-' ) ) {
			return false;
		}
		return $show;
	} );
}

// Guarded: Free and PRO share these constant names (PRO is built from the
// same codebase). If PRO is active, its file already defined them earlier
// in this same request — a second define() would emit a PHP "Constant
// already defined" warning, which is exactly the "unexpected output during
// activation" WordPress warns about.
defined( 'IDEO_FP_VERSION' ) || define( 'IDEO_FP_VERSION', '1.1.8' );
defined( 'IDEO_FP_PATH' )    || define( 'IDEO_FP_PATH', plugin_dir_path( __FILE__ ) );
defined( 'IDEO_FP_URL' )     || define( 'IDEO_FP_URL', plugin_dir_url( __FILE__ ) );

// Basename of the PRO plugin (separate slug — see ideo Field Porter PRO's own
// main file for the matching check in the other direction).
define( 'IDEO_FP_PRO_BASENAME', 'ideo-field-porter-pro/ideo-field-porter-pro.php' );

// PRO purchase/checkout link used in "upgrade to PRO" upsells throughout the
// plugin (Settings page teaser, Export tab warning, header button…).
// Generated dynamically by the Freemius SDK rather than hard-coded, so it
// automatically starts pointing to the real, live checkout the moment
// "Release Plans to Users" is turned on in the Freemius dashboard — no code
// change needed at that point.
define( 'IDEO_FP_PRO_URL', ideofp_fs()->get_upgrade_url( WP_FS__PERIOD_LIFETIME ) );

if ( ! function_exists( 'is_plugin_active' ) ) {
	require_once ABSPATH . 'wp-admin/includes/plugin.php';
}

// ── Mutual exclusion with ideo Field Porter PRO ─────────────────────────────
// Registered unconditionally — these closures only use core WP functions, no
// classes from this plugin, so they're safe to register even when the guard
// below skips the rest of the file. That matters because activating a plugin
// `require`s its file immediately in the *same* request as any already-active
// plugin, before this file's own "activate_{$plugin}" hook has a chance to
// run — so if PRO is already active, simply reaching the requires below would
// redeclare all of `IdeoFieldPorter\*` a second time and fatal error.

/**
 * Prevent activating the free version while PRO is already active — stop the
 * activation outright rather than letting both plugins register the same
 * menus/hooks side by side.
 *
 * deactivate_plugins() is called with $silent = true so the generic
 * 'deactivate_plugin' / 'deactivated_plugin' actions other active plugins may
 * listen to don't fire for this internal correction (avoids "unexpected
 * output during activation" warnings from unrelated plugins reacting to it).
 */
register_activation_hook( __FILE__, function () {
	if ( is_plugin_active( IDEO_FP_PRO_BASENAME ) ) {
		deactivate_plugins( plugin_basename( __FILE__ ), true );
		wp_die(
			wp_kses_post( __( '<strong>ideo Field Porter PRO</strong> is already active. The free version was not activated to avoid running both at the same time — your PRO settings and data are untouched.', 'ideo-field-porter' ) )
			. '<p><a href="' . esc_url( admin_url( 'plugins.php' ) ) . '">' . esc_html__( '« Back to Plugins', 'ideo-field-porter' ) . '</a></p>',
			esc_html__( 'Plugin already covered by PRO', 'ideo-field-porter' ),
			array( 'back_link' => false )
		);
	}
} );

/**
 * Clear the "auto-restored" flag if this plugin gets deactivated again before
 * that notice was ever shown (e.g. both plugins bulk-deactivated together
 * right after PRO's own deactivation hook silently reactivated this one).
 * Without this, the stale flag could resurface on a later, unrelated, fully
 * manual reactivation and falsely claim it happened automatically.
 */
register_deactivation_hook( __FILE__, function () {
	delete_transient( 'ideo_fp_free_restored' );
} );

/**
 * Safety net: if PRO gets activated afterwards (or both were force-activated
 * together, e.g. via WP-CLI or network activation), step aside automatically.
 */
add_action( 'admin_init', function () {
	if ( is_plugin_active( IDEO_FP_PRO_BASENAME ) ) {
		deactivate_plugins( plugin_basename( __FILE__ ), true );
		add_action( 'admin_notices', function () {
			echo '<div class="notice notice-info is-dismissible"><p>';
			echo wp_kses_post( __( '<strong>ideo Field Porter</strong> (free) has been deactivated automatically because <strong>ideo Field Porter PRO</strong> is active. Your settings were preserved.', 'ideo-field-porter' ) );
			echo '</p></div>';
		} );
	}
} );

/**
 * Shown when this plugin was reactivated automatically because PRO was just
 * deactivated (see ideo Field Porter PRO's own register_deactivation_hook()).
 */
add_action( 'admin_notices', function () {
	if ( get_transient( 'ideo_fp_free_restored' ) ) {
		delete_transient( 'ideo_fp_free_restored' );
		echo '<div class="notice notice-info is-dismissible"><p>';
		echo wp_kses_post( __( '<strong>ideo Field Porter PRO</strong> was deactivated — the free version (<strong>ideo Field Porter</strong>) has been reactivated automatically. Your settings were preserved.', 'ideo-field-porter' ) );
		echo '</p></div>';
	}
} );

// ── Fatal-collision guard ────────────────────────────────────────────────────
// PRO is built from this same codebase and declares the exact same
// `IdeoFieldPorter\*` classes. If both plugins' files ever get loaded in the same
// PHP process, PHP fatals with "Cannot declare class ... already in use".
// Bail out here rather than let that happen; the hooks above resolve which of
// the two stays active by the *next* request.
if ( is_plugin_active( IDEO_FP_PRO_BASENAME ) ) {
	return;
}

add_action( 'admin_notices', function () {
	$missing = array();
	if ( ! class_exists( 'ZipArchive' ) )               { $missing[] = 'ZipArchive'; }
	if ( ! function_exists( 'simplexml_load_string' ) ) { $missing[] = 'SimpleXML'; }
	if ( ! empty( $missing ) ) {
		echo '<div class="notice notice-error"><p>';
		echo wp_kses_post( sprintf(
			/* translators: %s: comma-separated list of missing PHP extensions (e.g. "ZipArchive, SimpleXML") */
			__( '<strong>ideo Field Porter:</strong> Missing PHP extension(s): %s. Please contact your hosting provider.', 'ideo-field-porter' ),
			esc_html( implode( ', ', $missing ) )
		) );
		echo '</p></div>';
	}
} );

require_once IDEO_FP_PATH . 'includes/class-logger.php';
require_once IDEO_FP_PATH . 'includes/class-mapping-profiles.php';
require_once IDEO_FP_PATH . 'includes/class-spreadsheet-importer.php';
require_once IDEO_FP_PATH . 'includes/class-spreadsheet-exporter-standard.php';
require_once IDEO_FP_PATH . 'includes/class-admin-page.php';
require_once IDEO_FP_PATH . 'includes/class-settings-page.php';

// ── Settings page ─────────────────────────────────────────────────────────────
add_filter( 'admin_body_class', function( $classes ) {
	$screen = get_current_screen();
	if ( $screen && (
		strpos( $screen->id, 'spreadsheet-importer' ) !== false ||
		strpos( $screen->id, 'ideo-field-porter-settings' ) !== false
	) ) {
		$classes .= ' plugin-ideo-field-porter';
	}
	return $classes;
} );

add_action( 'plugins_loaded', function () {
	new IdeoFieldPorter\Settings_Page();
} );

// ── Capability ───────────────────────────────────────────────────────────────

/**
 * Sync the ideo_fp_access capability on activation, so Administrators (and
 * any role picked in Settings → Access) can use the plugin immediately —
 * without this, no role would have the capability until the Settings page
 * is saved at least once, locking everyone out from the very first request.
 */
register_activation_hook( __FILE__, function () {
	\IdeoFieldPorter\Settings_Page::sync_access_capability();
} );

// Safety net: re-sync if the Administrator role, or any role currently
// selected in Settings → Access, is ever found out of sync with that
// setting — e.g. after the plugin's files were replaced/updated without a
// clean deactivate → reactivate cycle (register_activation_hook() only
// fires on a genuine activation, not a plain file overwrite), or after a
// role/capability plugin reset roles to defaults.
add_action( 'admin_init', function () {
	$expected_roles = array_merge( array( 'administrator' ), \IdeoFieldPorter\Settings_Page::get_allowed_roles() );
	foreach ( $expected_roles as $role_slug ) {
		$role = get_role( $role_slug );
		if ( $role && ! $role->has_cap( \IdeoFieldPorter\Settings_Page::CAP_ACCESS ) ) {
			\IdeoFieldPorter\Settings_Page::sync_access_capability();
			break;
		}
	}
}, 1 );

// Register admin_post_* handlers on init for active post types.
add_action( 'init', function () {
	$active = \IdeoFieldPorter\Settings_Page::get_active_post_types();
	foreach ( $active as $slug ) {
		if ( post_type_exists( $slug ) ) {
			new IdeoFieldPorter\Admin_Page( $slug );
		}
	}
}, 20 );

// Register the submenu pages only for active post types.
add_action( 'admin_menu', function () {
	$active = \IdeoFieldPorter\Settings_Page::get_active_post_types();
	foreach ( $active as $slug ) {
		if ( post_type_exists( $slug ) ) {
			new IdeoFieldPorter\Admin_Page( $slug );
		}
	}
}, 5 );

// ── Plugins list row actions ─────────────────────────────────────────────────
// Adds "Settings" and "Upgrade" next to the default "Deactivate" link on the
// Plugins admin page (wp-admin/plugins.php).
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), function ( $links ) {
	$settings_url = admin_url( 'options-general.php?page=ideo-field-porter-settings' );

	$custom_links = array(
		'settings' => '<a href="' . esc_url( $settings_url ) . '">' . esc_html__( 'Settings', 'ideo-field-porter' ) . '</a>',
		'upgrade'  => '<a href="' . esc_url( IDEO_FP_PRO_URL ) . '" target="_blank" rel="noopener noreferrer" style="font-weight:600;">' . esc_html__( 'Upgrade', 'ideo-field-porter' ) . '</a>',
	);

	// Deactivate first (WP-provided), then our links.
	return array_merge( $links, $custom_links );
} );
