<?php
/**
 * Represents a key-value file.
 *
 * @package Ealab\Deployer
 */

namespace Ealab\Deployer\File;

use Ealab\Deployer\Exception\WpDeployException;

/**
 * Represents a key-value file.
 */
class KeyValueFile extends FileBase {
	/**
	 * The decoded file content.
	 *
	 * @var array<string,mixed>
	 */
	protected array $decoded_file_content = array();

	/**
	 * Whether the file content has been modified by a setter on the decoded file content.
	 *
	 * @var bool
	 */
	protected bool $is_dirty = false;

	/**
	 * {@inheritDoc}
	 */
	public function getRawFileContent(): string {
		if ( $this->is_dirty ) {
			// Rebuild the raw file content.
			ksort( $this->decoded_file_content, SORT_NATURAL );
			$this->raw_file_content = json_encode( $this->decoded_file_content, JSON_PRETTY_PRINT );
			$this->is_dirty         = false;
		}

		return $this->raw_file_content;
	}

	/**
	 * Gets the value for the given key.
	 *
	 * @param string $key The key.
	 * @param mixed  $fallback The fallback value.
	 *
	 * @return mixed|null The value for the given key, or the fallback value if the key is not set.
	 */
	public function getValue( string $key, $fallback = null ) {
		return $this->decoded_file_content[ $key ] ?? $fallback;
	}

	/**
	 * Gets all the values.
	 *
	 * @return array<string,mixed> The complete values.
	 */
	public function getValues(): array {
		return $this->decoded_file_content ?? array();
	}

	/**
	 * Merges the given values with the existing ones.
	 *
	 * @param array<string,mixed> $values The values to merge as an associative array.
	 */
	public function mergeValues( array $values ): void {
		$this->is_dirty = true;

		$this->decoded_file_content = array_merge( $this->decoded_file_content, $values );
	}

	/**
	 * {@inheritDoc}
	 */
	public function setRawFileContent( string $raw_file_content ): void {
		parent::setRawFileContent( $raw_file_content );
		$decoded = json_decode( $raw_file_content, true );

		if ( json_last_error() !== JSON_ERROR_NONE || null === $decoded || ! is_array( $decoded ) ) {
			// Could not decode the JSON.
			throw new WpDeployException( 'Could not decode a JSON file content.' );
		}

		$this->decoded_file_content = $decoded;

		// Setting the raw file content will reset the dirty flag.
		$this->is_dirty = false;
	}

	/**
	 * Sets the value for the given key.
	 *
	 * @param string $key The key.
	 * @param mixed  $value The value to set.
	 */
	public function setValue( string $key, $value ): void {
		$this->is_dirty = true;

		$this->decoded_file_content[ $key ] = $value;
	}

	/**
	 * Sets (replaces) the values.
	 *
	 * @param array<string,mixed> $values The values to set as an associative array.
	 */
	public function setValues( array $values ): void {
		$this->is_dirty = true;

		$this->decoded_file_content = $values;
	}
}
