<?php
/**
 * Sends admin email and optional Slack alert when a sync action fails permanently.
 *
 * @package StoreSheet
 */

declare( strict_types=1 );

namespace StoreSheet\Notifications;

defined( 'ABSPATH' ) || exit;

use StoreSheet\Emails\SyncFailureEmail;

/**
 * Hooks into Action Scheduler's final-failure event and dispatches notifications.
 */
class FailureNotifier {

	public function init(): void {
		add_action( 'action_scheduler_failed_action', array( $this, 'on_action_failed' ), 10, 2 );
		add_filter( 'woocommerce_email_classes', array( $this, 'register_email' ) );
	}

	/**
	 * Register the WooCommerce email for sync failures so it shows up under
	 * WooCommerce → Settings → Emails and sends through WC's mailer.
	 *
	 * @param array<string, \WC_Email> $emails
	 * @return array<string, \WC_Email>
	 */
	public function register_email( array $emails ): array {
		$emails['StoreSheet_Sync_Failure'] = new SyncFailureEmail();
		return $emails;
	}

	/**
	 * @param int        $action_id Action Scheduler action ID.
	 * @param \Exception $exception The exception that caused the failure.
	 */
	public function on_action_failed( int $action_id, \Exception $exception ): void {
		$hook = $this->get_action_hook( $action_id );
		if ( ! $hook || ! str_starts_with( $hook, 'storesheet_' ) ) {
			return;
		}

		$settings = get_option( 'storesheet_settings', array() );
		if ( ! empty( $settings['slack_webhook_url'] ) ) {
			( new SlackNotifier() )->notify( $hook, $exception->getMessage() );
		}

		/**
		 * Fires when a StoreSheet sync action fails permanently. Add-ons hook this
		 * to run their own failure handling (e.g. Pro's custom failure-email).
		 *
		 * @since 1.1.0
		 * @param string $resource    Resource slug (product|order|coupon|…), or '' if not a push job.
		 * @param int    $resource_id The failed job's target ID, or 0 if unavailable.
		 * @param string $error       The failure message.
		 */
		do_action( 'storesheet_sync_failed', $this->resource_from_hook( $hook ), $this->first_arg_id( $action_id ), $exception->getMessage() );

		storesheet()->log(
			sprintf( 'Action %d (%s) failed permanently: %s', $action_id, $hook, $exception->getMessage() ),
			'error'
		);
	}

	/**
	 * Derive the resource slug from a push hook name (storesheet_push_order → order).
	 */
	private function resource_from_hook( string $hook ): string {
		$prefix = 'storesheet_push_';
		return str_starts_with( $hook, $prefix ) ? substr( $hook, strlen( $prefix ) ) : '';
	}

	/**
	 * Read the failed action's first argument (the resource ID) from Action Scheduler.
	 */
	private function first_arg_id( int $action_id ): int {
		global $wpdb;

		$args = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
			$wpdb->prepare(
				"SELECT args FROM {$wpdb->prefix}actionscheduler_actions WHERE action_id = %d LIMIT 1",
				$action_id
			)
		);

		$decoded = is_string( $args ) ? json_decode( $args, true ) : null;
		if ( is_array( $decoded ) && isset( $decoded[0] ) && is_numeric( $decoded[0] ) ) {
			return (int) $decoded[0];
		}

		return 0;
	}

	private function get_action_hook( int $action_id ): ?string {
		global $wpdb;

		$hook = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
			$wpdb->prepare(
				"SELECT hook FROM {$wpdb->prefix}actionscheduler_actions WHERE action_id = %d LIMIT 1",
				$action_id
			)
		);

		return $hook ?: null;
	}
}
