<?php
/**
 * Costudio_Standalone_Sites
 *
 * Manages publication destinations for the standalone plugin.
 * Stores sites in 'costudio_sites' option.
 *
 * @package Contentosapp_Studio
 * @since   1.0.0
 */

defined( 'ABSPATH' ) || exit;

class Costudio_Standalone_Sites {

	/**
	 * Singleton instance.
	 */
	private static ?self $instance = null;

	/**
	 * Option name.
	 */
	private const OPTION_KEY = 'costudio_sites';

	/**
	 * In-memory cache.
	 */
	private ?array $cache = null;

	/**
	 * Private constructor.
	 */
	private function __construct() {}

	/**
	 * Get instance.
	 */
	public static function get_instance(): self {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Get all sites.
	 *
	 * @return array
	 */
	public function get_all(): array {
		if ( null !== $this->cache ) {
			return $this->cache;
		}

		$stored      = get_option( self::OPTION_KEY, array() );
		$this->cache = is_array( $stored ) ? $stored : array();

		return $this->cache;
	}

	/**
	 * Get a single site by key.
	 *
	 * @param string $site_key
	 * @return array|null
	 */
	public function get( string $site_key ): ?array {
		$all = $this->get_all();
		return isset( $all[ $site_key ] ) ? $all[ $site_key ] : null;
	}

	public function get_site_limit(): int {
		return PHP_INT_MAX;
	}

	/**
	 * Return all configured site keys.
	 *
	 * @return string[]
	 */
	public function get_entitled_site_keys(): array {
		return array_keys( $this->get_all() );
	}

	/**
	 * Get the 1-based position of a site in the saved destination list.
	 *
	 * Returns 0 when the site is not configured.
	 */
	public function get_site_position( string $site_key ): int {
		$position = 1;
		foreach ( array_keys( $this->get_all() ) as $key ) {
			if ( (string) $key === $site_key ) {
				return $position;
			}
			$position++;
		}

		return 0;
	}

	/**
	 * Whether the site is configured.
	 */
	public function is_site_entitled( string $site_key ): bool {
		return null !== $this->get( $site_key );
	}

	/**
	 * Whether the site exists and is user-enabled.
	 */
	public function is_site_operable( string $site_key ): bool {
		$site = $this->get( $site_key );
		if ( ! $site || empty( $site['active'] ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Standard message for operational attempts on unavailable sites.
	 */
	public function get_unavailable_site_message(): string {
		return __( 'This site is not available. Check the site configuration in Contentosapp Studio settings.', 'contentosapp-studio' );
	}

	/**
	 * Save a site.
	 *
	 * @param array $site_data Site configuration.
	 * @return bool|string True on success, error message on failure.
	 */
	public function save( array $site_data ) {
		$all    = $this->get_all();
		$key    = isset( $site_data['key'] ) ? sanitize_key( $site_data['key'] ) : 'site-' . time();

		$existing = isset( $all[ $key ] ) ? $all[ $key ] : array();

		// Encrypt password if provided
		$password = isset( $site_data['password'] ) ? $site_data['password'] : '';
		if ( '' !== $password ) {
			$encrypted_password = Costudio_Standalone_Crypto::encrypt( $password );
		} else {
			$encrypted_password = isset( $existing['password'] ) ? $existing['password'] : '';
		}

		$display_name = '';
		if ( isset( $site_data['label'] ) && '' !== $site_data['label'] ) {
			$display_name = sanitize_text_field( $site_data['label'] );
		} elseif ( isset( $site_data['name'] ) && '' !== $site_data['name'] ) {
			$display_name = sanitize_text_field( $site_data['name'] );
		} elseif ( isset( $existing['label'] ) && '' !== $existing['label'] ) {
			$display_name = sanitize_text_field( (string) $existing['label'] );
		} elseif ( isset( $existing['name'] ) && '' !== $existing['name'] ) {
			$display_name = sanitize_text_field( (string) $existing['name'] );
		}

		$all[ $key ] = array(
			'key'                    => $key,
			'name'                   => $display_name,
			'label'                  => $display_name,
			'url'                    => isset( $site_data['url'] ) ? esc_url_raw( rtrim( $site_data['url'], '/' ) ) : '',
			'user'                   => isset( $site_data['user'] ) ? sanitize_text_field( $site_data['user'] ) : '',
			'password'               => $encrypted_password,
			'color'                  => isset( $site_data['color'] ) ? sanitize_hex_color( $site_data['color'] ) : ( isset( $existing['color'] ) ? sanitize_hex_color( $existing['color'] ) : '#3b82f6' ),
			'categories'             => isset( $site_data['categories'] ) && is_array( $site_data['categories'] ) ? $site_data['categories'] : ( isset( $existing['categories'] ) ? $existing['categories'] : array() ),
			'active'                 => isset( $site_data['active'] ) ? (bool) $site_data['active'] : true,
			'last_connection_test'   => isset( $existing['last_connection_test'] ) ? $existing['last_connection_test'] : null,
			'last_connection_status' => isset( $existing['last_connection_status'] ) ? $existing['last_connection_status'] : null,
			'publication_recovery_status'    => isset( $existing['publication_recovery_status'] ) ? $existing['publication_recovery_status'] : null,
			'publication_recovery_tested_at' => isset( $existing['publication_recovery_tested_at'] ) ? $existing['publication_recovery_tested_at'] : null,
		);

		$this->cache = $all;
		update_option( self::OPTION_KEY, $all, false );

		return true;
	}

	/**
	 * Delete a site.
	 *
	 * @param string $site_key
	 * @return bool
	 */
	public function delete( string $site_key ): bool {
		$all = $this->get_all();
		if ( ! isset( $all[ $site_key ] ) ) {
			return false;
		}

		unset( $all[ $site_key ] );
		$this->cache = $all;
		return update_option( self::OPTION_KEY, $all, false );
	}

	/**
	 * Test connection to a site.
	 *
	 * @param string $site_key
	 * @return array { success: bool, message: string }
	 */
	public function test_connection( string $site_key ): array {
		if ( Costudio_WP_Publisher::LOCAL_SITE_KEY === $site_key || '__local__' === $site_key ) {
			$this->update_status( $site_key, 'success', 'ready' );
			return array( 'success' => true, 'message' => __( 'Connected · publication recovery ready', 'contentosapp-studio' ), 'publication_recovery_status' => 'ready' );
		}

		$site = $this->get( $site_key );
		if ( ! $site ) {
			return array( 'success' => false, 'message' => __( 'Site not found.', 'contentosapp-studio' ) );
		}

		$url      = costudio_safe_http_url( rtrim( (string) $site['url'], '/' ) );
		$user     = $site['user'];
		$password = Costudio_Standalone_Crypto::decrypt( $site['password'] );
		$auth     = 'Basic ' . base64_encode( "{$user}:{$password}" );

		if ( '' === $url ) {
			$this->update_status( $site_key, 'error', 'unavailable' );
			return array( 'success' => false, 'message' => __( 'Invalid or unsafe site URL.', 'contentosapp-studio' ) );
		}

		$response = wp_safe_remote_get( "{$url}/wp-json/wp/v2/users/me?context=edit", costudio_safe_remote_args( array(
			'timeout' => 15,
			'headers' => array(
				'Authorization' => $auth,
			),
		) ) );

		if ( is_wp_error( $response ) ) {
			$this->update_status( $site_key, 'error', 'unavailable' );
			return array( 'success' => false, 'message' => $response->get_error_message() );
		}

		$code = wp_remote_retrieve_response_code( $response );
		if ( 200 === $code ) {
			$probe_url = add_query_arg( array(
				'context'  => 'edit',
				'status'   => 'any',
				'search'   => 'costudio-recovery-probe-' . wp_generate_password( 12, false, false ),
				'per_page' => 1,
				'_fields'  => 'id,content',
			), "{$url}/wp-json/wp/v2/posts" );
			$probe = wp_safe_remote_get( $probe_url, costudio_safe_remote_args( array(
				'timeout' => 15,
				'headers' => array( 'Authorization' => $auth ),
			) ) );
			$recovery_ready = ! is_wp_error( $probe ) && 200 === (int) wp_remote_retrieve_response_code( $probe );
			$recovery_status = $recovery_ready ? 'ready' : 'limited';
			$this->update_status( $site_key, 'success', $recovery_status );
			return array(
				'success' => true,
				'message' => $recovery_ready
					? __( 'Connection successful · publication recovery ready', 'contentosapp-studio' )
					: __( 'Connected, but publication recovery is limited. Use an Editor or Administrator Application Password.', 'contentosapp-studio' ),
				'publication_recovery_status' => $recovery_status,
			);
		}

		$this->update_status( $site_key, 'error', 'unavailable' );
		$body = json_decode( wp_remote_retrieve_body( $response ), true );
		$msg  = isset( $body['message'] ) ? $body['message'] : 'HTTP ' . $code;

		return array( 'success' => false, 'message' => "Error: {$msg}" );
	}

	/**
	 * Update connection status in cache and DB.
	 *
	 * @param string $site_key
	 * @param string $status 'success' or 'error'.
	 */
	private function update_status( string $site_key, string $status, string $recovery_status = '' ): void {
		$all = $this->get_all();
		if ( isset( $all[ $site_key ] ) ) {
			$all[ $site_key ]['last_connection_test']   = time();
			$all[ $site_key ]['last_connection_status'] = $status;
			if ( '' !== $recovery_status ) {
				$all[ $site_key ]['publication_recovery_status']    = $recovery_status;
				$all[ $site_key ]['publication_recovery_tested_at'] = time();
			}
			$this->cache = $all;
			update_option( self::OPTION_KEY, $all, false );
		}
	}
}
