<?php
/**
 * Plugin activation and database setup
 *
 * @package WooAttioSync
 */

declare( strict_types=1 );

defined( 'ABSPATH' ) || exit;

/**
 * Handles database table creation and upgrade on activation
 */
class WooAttioSync_Install {

	private const DB_VERSION = '1.0.1';

	private const DB_VERSION_OPTION = 'wcattio_db_version';

	/**
	 * Run on plugin activation.
	 * Creates (or upgrades) the sync-log table using dbDelta so that
	 * re-activating the plugin never destroys existing data.
	 */
	public static function activate(): void {
		global $wpdb;

		$table_name      = $wpdb->prefix . 'wcattio_sync_log';
		$charset_collate = $wpdb->get_charset_collate();

		// dbDelta requires each column definition and index to be on its own
		// line, and the PRIMARY KEY line must not have a trailing comma
		$sql = "CREATE TABLE {$table_name} (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  object_type VARCHAR(50) NOT NULL,
  object_id VARCHAR(100) NOT NULL,
  attio_record_id VARCHAR(100) DEFAULT NULL,
  status VARCHAR(20) NOT NULL DEFAULT 'pending',
  error_message TEXT DEFAULT NULL,
  synced_at DATETIME DEFAULT NULL,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  UNIQUE KEY object_type_id (object_type, object_id),
  KEY status (status),
  KEY synced_at (synced_at)
) {$charset_collate};";

		require_once ABSPATH . 'wp-admin/includes/upgrade.php';
		dbDelta( $sql );

		update_option( self::DB_VERSION_OPTION, self::DB_VERSION );
	}

	/**
	 * Returns true when the installed schema is out of date or the table is missing
	 */
	public static function needs_upgrade(): bool {
		if ( version_compare( (string) get_option( self::DB_VERSION_OPTION, '0' ), self::DB_VERSION, '<' ) ) {
			return true;
		}

		// Safety net for a manually dropped table. Admin-only so the metadata
		// query stays off front-end/REST/cron requests, which run on every page load.
		if ( ! is_admin() ) {
			return false;
		}

		global $wpdb;
		$table = $wpdb->prefix . 'wcattio_sync_log';
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		return $table !== $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
	}

	/**
	 * Removes all plugin data. Fires on uninstall (see wcattio_uninstall_cleanup()
	 * in the main plugin file, registered both via register_uninstall_hook() and
	 * Freemius' after_uninstall action).
	 */
	public static function uninstall(): void {
		global $wpdb;

		$options = array(
			'wcattio_api_key',
			'wcattio_sync_statuses',
			'wcattio_field_mappings',
			'wcattio_list_rules',
			'wcattio_webhook_secret',
			'wcattio_task_rules',
			'wcattio_deals_object',
			'wcattio_deal_stage_map',
			'wcattio_enrichment_attrs_ready',
			'wcattio_last_customer_bulk_sync',
			'wcattio_last_order_bulk_sync',
			'wcattio_db_version',
		);

		if ( is_multisite() ) {
			// WordPress runs uninstall once, not per site, so each site's
			// prefixed table and options must be cleaned up explicitly
			$site_ids = get_sites(
				array(
					'fields' => 'ids',
					'number' => 0,
				)
			);
			foreach ( $site_ids as $site_id ) {
				switch_to_blog( (int) $site_id );
				self::uninstall_site( $options );
				restore_current_blog();
			}

			foreach ( $options as $option ) {
				delete_site_option( $option );
			}
		} else {
			self::uninstall_site( $options );
		}

		// User meta is network-global, so clean it once outside the per-site loop.
		// Remove per-user dismissal meta for the onboarding notice
		delete_metadata( 'user', 0, 'wcattio_dismissed_onboarding', '', true );

		// Two-way sync writes per-attribute keys under this prefix; the concrete key
		// names are workspace-defined, so only a prefix match can find them all
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE %s",
				$wpdb->esc_like( 'wcattio_attr_' ) . '%'
			)
		);
	}

	/**
	 * Remove one site's options, scheduled jobs, cron event, and sync-log table.
	 *
	 * @param string[] $options Option names to delete.
	 * @return void
	 */
	private static function uninstall_site( array $options ): void {
		global $wpdb;

		foreach ( $options as $option ) {
			delete_option( $option );
		}

		// Cancel all pending Action Scheduler jobs in the wcattio group
		if ( function_exists( 'as_unschedule_all_actions' ) ) {
			as_unschedule_all_actions( '', array(), 'wcattio' );
		}

		// Cancel the log-cleanup WP-Cron event
		wp_clear_scheduled_hook( 'wcattio_cleanup_logs' );

		// Drop the sync-log table
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		$wpdb->query(
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
			'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'wcattio_sync_log'
		);
	}
}
