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

namespace Hafenstudios\Kurato\Cockpit;

defined( 'ABSPATH' ) || exit;

/**
 * Fuehrt das Pairing durch: nimmt den Pairing-Code entgegen, ruft
 * Server-zu-Server POST {dienst}/v1/pairing/complete und speichert den
 * empfangenen site_key ueber Config/Secrets (COCKPIT-API.md, Pairing-Ablauf).
 *
 * Dieser erste Call ist noch NICHT signiert (die Site hat den site_key erst
 * danach); der Pairing-Code ist der Nachweis. Der Dienst verifiziert die Site
 * anschliessend per signiertem Rueckruf auf GET /verify.
 *
 * HTTPS ist Pflicht: ohne HTTPS bricht das Pairing hart ab (https_required).
 * Solange der Dienst (CP2) nicht existiert, muss der Fehlerfall sauber
 * aussehen: Ursache und naechster Schritt.
 */
class PairingService {

	private Config $config;

	private HttpClient $http;

	private ShadowUserStore $users;

	/**
	 * @param Config               $config Kopplungs-Zustand.
	 * @param HttpClient           $http   HTTP-Ausgang (Seam).
	 * @param ShadowUserStore|null $users  Schattennutzer-Speicher (Seam; Default WP).
	 */
	public function __construct( Config $config, HttpClient $http, ?ShadowUserStore $users = null ) {
		$this->config = $config;
		$this->http   = $http;
		$this->users  = $users ?? new WpShadowUserStore();
	}

	/**
	 * Schliesst das Pairing mit einem Code ab.
	 *
	 * @param string $code Pairing-Code (KURATO-XXXX-XXXX).
	 * @return true|\WP_Error
	 */
	public function complete( string $code ) {
		$code = strtoupper( trim( $code ) );
		if ( '' === $code ) {
			return new \WP_Error(
				'pairing_code_invalid',
				__( 'Bitte geben Sie den Pairing-Code aus dem Cockpit ein. Naechster Schritt: Kopieren Sie den Code aus dem Cockpit (Format KURATO-XXXX-XXXX) und tragen Sie ihn hier ein.', 'kurato' )
			);
		}

		if ( ! $this->is_https() ) {
			return new \WP_Error(
				'https_required',
				__( 'Die Kopplung braucht HTTPS. Ursache: Ihre Site laeuft nicht ueber eine verschluesselte Verbindung. Naechster Schritt: Aktivieren Sie ein SSL-Zertifikat (bei den meisten Hostern kostenlos) und rufen Sie diese Seite ueber https auf.', 'kurato' )
			);
		}

		$body = (string) wp_json_encode(
			array(
				'code'             => $code,
				'site_url'         => home_url(),
				'contract'         => 'v1',
				'kurato_version'   => defined( 'KURATO_VERSION' ) ? KURATO_VERSION : '',
				'plan'             => $this->plan(),
				// CP2-9: Der koppelnde wp-admin-Nutzer wird Inhaber-Konto der Site.
				// Der Dienst merkt sich diese ID am Site-Datensatz und stellt damit
				// Inhaber-Tickets aus (statt 0), damit der Inhaber die App nutzen kann.
				'owner_wp_user_id' => get_current_user_id(),
			)
		);

		$response = $this->http->post(
			$this->config->service_url() . '/v1/pairing/complete',
			array(
				'headers' => array( 'Content-Type' => 'application/json' ),
				'body'    => $body,
				'timeout' => 15,
			)
		);

		if ( is_wp_error( $response ) ) {
			return new \WP_Error(
				'service_unreachable',
				__( 'Der Cockpit-Dienst ist nicht erreichbar. Ursache: Die Verbindung zu kurato.cloud kam nicht zustande. Naechster Schritt: Pruefen Sie die Internetverbindung des Servers und versuchen Sie es in einigen Minuten erneut.', 'kurato' )
			);
		}

		$data = json_decode( $response['body'], true );
		if ( 200 !== $response['status'] || ! is_array( $data ) || empty( $data['ok'] ) ) {
			return $this->service_error( is_array( $data ) ? $data : array() );
		}

		if ( '' === (string) ( $data['site_id'] ?? '' ) || '' === (string) ( $data['site_key'] ?? '' ) ) {
			return new \WP_Error(
				'pairing_incomplete',
				__( 'Die Antwort des Dienstes war unvollstaendig. Naechster Schritt: Erzeugen Sie im Cockpit einen neuen Pairing-Code und versuchen Sie es erneut.', 'kurato' )
			);
		}

		$this->config->save_pairing(
			array(
				'site_id'     => (string) $data['site_id'],
				'site_key'    => (string) $data['site_key'],
				'key_version' => (int) ( $data['key_version'] ?? 1 ),
				'plan'        => $this->plan(),
			)
		);

		return true;
	}

	/**
	 * Trennt die Kopplung.
	 *
	 * @return void
	 */
	public function disconnect(): void {
		$this->config->clear();
		// CP1-9: Trennen entrechtet alle Schattennutzer (Sperr-Meta + Rollen-Entzug).
		// Ohne diesen Schritt koennte sich ein Ex-Agent nach dem Trennen per
		// Passwort-Reset ein nutzbares Konto mit Kontakt-Rechten verschaffen,
		// weil AdminGuard und Login-Block nur bei bestehender Kopplung aktiv sind.
		$this->users->deactivate_all();
	}

	/**
	 * Uebersetzt eine Fehlerantwort des Dienstes in eine UI-taugliche Meldung
	 * (Ursache + naechster Schritt).
	 *
	 * @param array<string, mixed> $data Dekodierte Antwort.
	 * @return \WP_Error
	 */
	private function service_error( array $data ): \WP_Error {
		$code = '';
		if ( isset( $data['error']['code'] ) ) {
			$code = (string) $data['error']['code'];
		}

		switch ( $code ) {
			case 'pairing_code_invalid':
				$message = __( 'Der Pairing-Code ist ungueltig, abgelaufen oder bereits verbraucht. Naechster Schritt: Erzeugen Sie im Cockpit einen neuen Code und tragen Sie ihn hier ein.', 'kurato' );
				break;
			case 'contract_mismatch':
				$message = __( 'Die Vertragsversion passt nicht zusammen. Naechster Schritt: Aktualisieren Sie das Kurato-Plugin auf die neueste Version und versuchen Sie es erneut.', 'kurato' );
				break;
			default:
				$message = __( 'Die Kopplung ist fehlgeschlagen. Ursache: Der Cockpit-Dienst hat die Anfrage abgelehnt. Naechster Schritt: Erzeugen Sie im Cockpit einen neuen Pairing-Code und versuchen Sie es erneut.', 'kurato' );
		}

		return new \WP_Error( '' !== $code ? $code : 'pairing_failed', $message );
	}

	/**
	 * Gemeldeter Plan der Site. In CP1 ohne Freemius-Logik; Default free, per
	 * Filter setzbar.
	 *
	 * @return string
	 */
	private function plan(): string {
		/**
		 * Filtert den beim Pairing gemeldeten Plan (free|pro).
		 *
		 * @param string $plan Default free.
		 */
		$plan = apply_filters( 'kurato_cockpit_plan', 'free' );
		return in_array( $plan, array( 'free', 'pro' ), true ) ? $plan : 'free';
	}

	/**
	 * Laeuft die Site ueber HTTPS?
	 *
	 * @return bool
	 */
	private function is_https(): bool {
		return 0 === strpos( home_url(), 'https://' );
	}
}
