<?php

namespace SugarCalendarBookings\Migration;

/**
 * Migration Issues Storage.
 *
 * Handles storage and retrieval of migration issues (skipped/failed items).
 * Issues are stored in a separate WordPress option per migration source.
 *
 * @since 1.3.0
 */
class MigrationIssuesStorage {

	/**
	 * Option key prefix for storing migration issues.
	 *
	 * Pattern: sugar_calendar_bookings_{source_id}_migration_issues
	 *
	 * @since 1.3.0
	 *
	 * @var string
	 */
	private const OPTION_PREFIX = 'sugar_calendar_bookings_';

	/**
	 * Option key suffix for migration issues.
	 *
	 * @since 1.3.0
	 *
	 * @var string
	 */
	private const OPTION_SUFFIX = '_migration_issues';

	/**
	 * Maximum number of issues to store per source.
	 *
	 * @since 1.3.0
	 *
	 * @var int
	 */
	private const MAX_ISSUES = 1000;

	/**
	 * Current source ID.
	 *
	 * @since 1.3.0
	 *
	 * @var string
	 */
	private string $source_id;

	/**
	 * Collected issues during migration.
	 *
	 * @since 1.3.0
	 *
	 * @var array<array<string, mixed>>
	 */
	private array $issues = [];

	/**
	 * Constructor.
	 *
	 * @since 1.3.0
	 *
	 * @param string $source_id     Migration source ID (e.g., 'amelia').
	 * @param bool   $load_existing Whether to load existing issues from database.
	 */
	public function __construct( string $source_id, bool $load_existing = false ) {

		$this->source_id = $source_id;

		if ( $load_existing ) {
			$this->issues = $this->get_stored();
		}
	}

	/**
	 * Get the option name for this source's migration issues.
	 *
	 * @since 1.3.0
	 *
	 * @return string
	 */
	public function get_option_name(): string {

		return self::OPTION_PREFIX . $this->source_id . self::OPTION_SUFFIX;
	}

	/**
	 * Clear all issues (both in memory and in database).
	 *
	 * @since 1.3.0
	 */
	public function clear(): void {

		$this->issues = [];

		delete_option( $this->get_option_name() );
	}

	/**
	 * Add an issue to the in-memory collection.
	 *
	 * @since 1.3.0
	 *
	 * @param string $status      Issue status: 'skipped' or 'failed'.
	 * @param string $entity_type Entity type.
	 * @param int    $source_id   Source item ID.
	 * @param string $name        Item name/identifier.
	 * @param string $reason      Reason for skip/failure.
	 */
	public function add( string $status, string $entity_type, int $source_id, string $name, string $reason ): void {

		$this->issues[] = [
			'status'      => $status,
			'entity_type' => $entity_type,
			'source_id'   => $source_id,
			'name'        => $name,
			'reason'      => $reason,
		];
	}

	/**
	 * Get the in-memory issues collection.
	 *
	 * @since 1.3.0
	 *
	 * @return array<array<string, mixed>>
	 */
	public function get(): array {

		return $this->issues;
	}

	/**
	 * Save the in-memory issues to the database.
	 *
	 * Enforces the maximum issues limit by dropping oldest entries.
	 *
	 * @since 1.3.0
	 */
	public function save(): void {

		$issues = $this->issues;

		// Enforce maximum limit - keep newest issues.
		if ( count( $issues ) > self::MAX_ISSUES ) {
			$issues = array_slice( $issues, -self::MAX_ISSUES );
		}

		update_option( $this->get_option_name(), $issues, false );
	}

	/**
	 * Get issues from the database.
	 *
	 * @since 1.3.0
	 *
	 * @return array<array<string, mixed>>
	 */
	public function get_stored(): array {

		$issues = get_option( $this->get_option_name(), [] );

		return is_array( $issues ) ? $issues : [];
	}

	/**
	 * Check if there are stored issues in the database.
	 *
	 * @since 1.3.0
	 *
	 * @return bool
	 */
	public function has_stored(): bool {

		return ! empty( $this->get_stored() );
	}

	/**
	 * Get stored issues for a source (static helper for external use).
	 *
	 * @since 1.3.0
	 *
	 * @param string $source_id Source adapter ID.
	 *
	 * @return array<array<string, mixed>>
	 */
	public static function get_stored_for_source( string $source_id ): array {

		$storage = new self( $source_id );

		return $storage->get_stored();
	}

	/**
	 * Check if there are stored issues for a source (static helper for external use).
	 *
	 * @since 1.3.0
	 *
	 * @param string $source_id Source adapter ID.
	 *
	 * @return bool
	 */
	public static function has_stored_for_source( string $source_id ): bool {

		$storage = new self( $source_id );

		return $storage->has_stored();
	}
}
