<?php
/**
 * Storage Exception Class
 *
 * Exception for database and storage-related failures.
 *
 * @package Sequensy
 * @since 2.0.0
 */

declare(strict_types=1);

namespace Sequensy\Exceptions;

use Throwable;

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * Class StorageException
 *
 * Thrown when database operations fail.
 */
class StorageException extends SequensyException {

	/**
	 * Database operation that failed.
	 *
	 * @var string
	 */
	protected string $operation;

	/**
	 * Table name involved.
	 *
	 * @var string|null
	 */
	protected ?string $table;

	/**
	 * Constructor.
	 *
	 * @param string         $message   Error message.
	 * @param string         $operation Database operation (SELECT, INSERT, UPDATE, DELETE).
	 * @param string|null    $table     Table name.
	 * @param array          $context   Additional context data.
	 * @param Throwable|null $previous  Previous exception.
	 */
	public function __construct(
		string $message = '',
		string $operation = '',
		?string $table = null,
		array $context = array(),
		?Throwable $previous = null
	) {
		$this->operation = $operation;
		$this->table     = $table;

		// Ensure storage info is in context.
		$context['operation'] = $operation;
		if ( null !== $table ) {
			$context['table'] = $table;
		}

		parent::__construct( $message, 0, $context, $previous );
	}

	/**
	 * Get the database operation that failed.
	 *
	 * @return string Database operation.
	 */
	public function get_operation(): string {
		return $this->operation;
	}

	/**
	 * Get table name.
	 *
	 * @return string|null Table name.
	 */
	public function get_table(): ?string {
		return $this->table;
	}

	/**
	 * Get the WP_Error code for this exception.
	 *
	 * @return string Error code.
	 */
	protected function get_error_code(): string {
		return 'sequensy_storage_error';
	}

	/**
	 * Create exception for query failure.
	 *
	 * @param string $operation Database operation.
	 * @param string $table     Table name.
	 * @param string $message   Error message from database.
	 * @param array  $context   Additional context.
	 * @return static Exception instance.
	 */
	public static function queryFailed(
		string $operation,
		string $table,
		string $message,
		array $context = array()
	): static {
		$context['failure_type'] = 'query_failed';

		return new static(
			sprintf( '%s query failed on %s: %s', strtoupper( $operation ), $table, $message ),
			$operation,
			$table,
			$context
		);
	}

	/**
	 * Create exception for constraint violation.
	 *
	 * @param string      $message Error message.
	 * @param string|null $table   Table name.
	 * @param array       $context Additional context.
	 * @return static Exception instance.
	 */
	public static function constraintViolation(
		string $message,
		?string $table = null,
		array $context = array()
	): static {
		$context['failure_type'] = 'constraint_violation';

		return new static(
			sprintf( 'Database constraint violation: %s', $message ),
			'INSERT',
			$table,
			$context
		);
	}

	/**
	 * Create exception for serialization failure.
	 *
	 * @param string $message Error message.
	 * @param array  $context Additional context.
	 * @return static Exception instance.
	 */
	public static function serializationFailed( string $message, array $context = array() ): static {
		$context['failure_type'] = 'serialization_failed';

		return new static(
			sprintf( 'Data serialization failed: %s', $message ),
			'',
			null,
			$context
		);
	}
}
