<?php
/**
 * @package Hafenstudios\Kurato
 */

namespace Hafenstudios\Kurato\Cockpit;

use Hafenstudios\Kurato\Cockpit\Rest\AgentsSyncController;
use Hafenstudios\Kurato\Cockpit\Rest\DiagnoseController;
use Hafenstudios\Kurato\Cockpit\Rest\SessionController;
use Hafenstudios\Kurato\Cockpit\Rest\VerifyController;
use Hafenstudios\Kurato\Db\StageRepository;

defined( 'ABSPATH' ) || exit;

/**
 * Cockpit-Connector (Etappe CP1). Macht das Kurato-Plugin cockpit-faehig, ohne
 * dass es das Cockpit (Dienst/App) schon gibt.
 *
 * Null-Risiko-Default (Bauplan Versprechen 3 + Kap. 8): OHNE abgeschlossenes
 * Pairing registriert der Connector KEINE Auth-Hooks, KEINE REST-Routen und
 * sendet KEINE CORS-Header; das oeffentliche Verhalten der Site ist
 * byte-identisch zu vorher. Nur die Rolle (aus dem Installer) und die
 * Admin-Seite zum Eintragen des Pairing-Codes existieren.
 */
final class Connector {

	private Config $config;

	private Clock $clock;

	/**
	 * @param Config|null $config Kopplungs-Zustand (optional, fuer Tests).
	 * @param Clock|null  $clock  Zeit-Seam (optional, fuer Tests).
	 */
	public function __construct( ?Config $config = null, ?Clock $clock = null ) {
		$this->config = $config ?? new Config();
		$this->clock  = $clock ?? new SystemClock();
	}

	/**
	 * Verdrahtet den Connector.
	 *
	 * @return void
	 */
	public function register(): void {
		// Immer (auch ohne Pairing, aber nur im wp-admin sichtbar):
		// Rollen-Selbstheilung und die Cockpit-Admin-Seite.
		add_action( 'admin_init', array( CockpitRole::class, 'ensure' ) );

		$page = new CockpitPage(
			$this->config,
			new PairingService( $this->config, new WpHttpClient() ),
			new Diagnostics( $this->config, new WpHttpClient() )
		);
		$page->register();

		// Ab hier NUR bei abgeschlossenem Pairing (Null-Risiko-Default).
		if ( ! $this->config->is_paired() ) {
			return;
		}

		// Bearer-Ticket-Anmeldung (namespace-gebunden auf kurato/v1 + kurato-cockpit/v1).
		$authenticator = new Authenticator(
			new TicketVerifier( $this->config->site_key(), $this->config->key_version(), $this->clock ),
			new WpUserDirectory()
		);
		$authenticator->register();

		// REST-Routen kurato-cockpit/v1.
		add_action( 'rest_api_init', array( $this, 'register_routes' ) );

		// CORS nur auf den beiden Kurato-Namespaces.
		add_filter( 'rest_pre_serve_request', array( $this, 'send_cors_headers' ), 10, 4 );

		// wp-admin-Sperre, Admin-Bar aus, Passwort-Login der Schattennutzer aus.
		( new AdminGuard( $this->config ) )->register();
	}

	/**
	 * Registriert die kurato-cockpit/v1-Routen.
	 *
	 * @return void
	 */
	public function register_routes(): void {
		$guard = new SignatureGuard( $this->config, new SignatureVerifier( $this->clock ) );

		( new VerifyController( $guard ) )->register_routes();
		( new SessionController( new StageRepository() ) )->register_routes();
		( new AgentsSyncController( $guard, new AgentSync( new WpShadowUserStore() ) ) )->register_routes();
		( new DiagnoseController( new Diagnostics( $this->config, new WpHttpClient() ) ) )->register_routes();
	}

	/**
	 * Sendet die CORS-Kopfzeilen fuer die Kurato-Namespaces (rest_pre_serve_request).
	 *
	 * @param bool             $served  Ob die Antwort bereits gesendet wurde.
	 * @param mixed            $result  Antwort-Objekt (unbenutzt).
	 * @param \WP_REST_Request $request Aktueller Request.
	 * @param mixed            $server  REST-Server (unbenutzt).
	 * @return bool
	 */
	public function send_cors_headers( $served, $result, $request, $server ) {
		$route   = $request instanceof \WP_REST_Request ? (string) $request->get_route() : '';
		$headers = Cors::headers( $route, $this->config->is_paired(), $this->config->origin() );

		if ( ! headers_sent() ) {
			foreach ( $headers as $name => $value ) {
				header( $name . ': ' . $value );
			}
		}

		return $served;
	}
}
