<?php
namespace Pay4All\Pro\License;

if ( ! defined( 'ABSPATH' ) ) { exit; }

/**
 * Read/write accessor around the wp_options rows owned by the License layer.
 * Every getter has a sensible default so the plugin keeps rendering even on a
 * brand-new install where none of the options have been created yet.
 *
 * The `installation_uuid` is minted locally on first access — it's a stable
 * identifier for this WordPress install. The `installation_secret` is NOT
 * generated locally : it must come from the SaaS `/installations/register`
 * response so the server and the plugin share the same value.
 */
final class Store {

	public static function installation_uuid() : string {
		$uuid = (string) get_option( Constants::OPT_INSTALLATION_UUID, '' );
		if ( $uuid === '' ) {
			$uuid = wp_generate_uuid4();
			update_option( Constants::OPT_INSTALLATION_UUID, $uuid, false );
		}
		return $uuid;
	}

	public static function installation_secret() : string {
		return (string) get_option( Constants::OPT_INSTALLATION_SECRET, '' );
	}

	public static function set_installation_secret( string $secret ) : void {
		update_option( Constants::OPT_INSTALLATION_SECRET, $secret, false );
	}

	/**
	 * The trial anchor is authoritative on the SaaS side (see the
	 * `domains.trial_started_at` column, populated on the first
	 * /domains/check without a subscription). The local plugin caches the
	 * signed `trial_started_at` / `trial_ends_at` echoed by /check but
	 * NEVER writes an anchor of its own : erasing the local wp_option
	 * cannot regenerate a fresh 30-day window because the next check
	 * receives the SAME server-side anchor back.
	 */
	public static function trial_started_at() : int {
		$iso = (string) get_option( Constants::OPT_TRIAL_STARTED_SAAS, '' );
		return $iso === '' ? 0 : (int) strtotime( $iso );
	}

	public static function set_trial_started_at( string $iso ) : void {
		update_option( Constants::OPT_TRIAL_STARTED_SAAS, $iso, false );
	}

	public static function trial_ends_at() : int {
		$iso = (string) get_option( Constants::OPT_TRIAL_ENDS_AT, '' );
		return $iso === '' ? 0 : (int) strtotime( $iso );
	}

	public static function set_trial_ends_at( string $iso ) : void {
		update_option( Constants::OPT_TRIAL_ENDS_AT, $iso, false );
	}

	public static function del_trial() : void {
		delete_option( Constants::OPT_TRIAL_STARTED_SAAS );
		delete_option( Constants::OPT_TRIAL_ENDS_AT );
	}

	public static function days_left_in_trial() : int {
		$ends = self::trial_ends_at();
		if ( $ends <= 0 ) { return 0; }
		return max( 0, (int) ceil( ( $ends - time() ) / DAY_IN_SECONDS ) );
	}

	/**
	 * True only when a signed server-side trial anchor exists AND is still
	 * within its 30-day window. Returns FALSE when no check has ever
	 * succeeded — the merchant must reach pay4all.net at least once to
	 * unlock the trial. That closes the "wipe local options + reactivate"
	 * bypass.
	 */
	public static function is_trial_active() : bool {
		$ends = self::trial_ends_at();
		return $ends > 0 && time() < $ends;
	}

	public static function status() : string {
		// Empty status means "no successful SaaS check yet" → do NOT
		// silently claim a local trial ; LicenseManager treats this
		// as "unknown" and denies Pro features until registration completes.
		return (string) get_option( Constants::OPT_STATUS, '' );
	}

	public static function set_status( string $status ) : void {
		update_option( Constants::OPT_STATUS, $status, false );
	}

	public static function valid_until() : int {
		// Setter accepts either an ISO-8601 string ("2026-08-12T…") or a
		// raw epoch — normalise on read so callers always get a usable
		// timestamp instead of PHP's implicit `(int) "2026-08-12"` = 2026.
		$raw = (string) get_option( Constants::OPT_VALID_UNTIL, '' );
		if ( $raw === '' ) { return 0; }
		if ( ctype_digit( $raw ) ) { return (int) $raw; }
		$ts = strtotime( $raw );
		return $ts === false ? 0 : $ts;
	}

	public static function set_valid_until( string $iso_or_epoch ) : void {
		update_option( Constants::OPT_VALID_UNTIL, $iso_or_epoch, false );
	}

	public static function grace_until() : int {
		return (int) get_option( Constants::OPT_GRACE_UNTIL, 0 );
	}

	public static function set_grace_until( string $iso_or_epoch ) : void {
		update_option( Constants::OPT_GRACE_UNTIL, $iso_or_epoch, false );
	}

	public static function last_check_at() : int {
		return (int) get_option( Constants::OPT_LAST_CHECK_AT, 0 );
	}

	public static function set_last_check_at( int $ts ) : void {
		update_option( Constants::OPT_LAST_CHECK_AT, $ts, false );
	}

	public static function normalized_domain() : string {
		return (string) get_option( Constants::OPT_DOMAIN, '' );
	}

	public static function set_normalized_domain( string $domain ) : void {
		update_option( Constants::OPT_DOMAIN, $domain, false );
	}

	public static function previous_domain() : string {
		return (string) get_option( Constants::OPT_PREVIOUS_DOMAIN, '' );
	}

	public static function set_previous_domain( string $domain ) : void {
		update_option( Constants::OPT_PREVIOUS_DOMAIN, $domain, false );
	}

	public static function del_previous_domain() : void {
		delete_option( Constants::OPT_PREVIOUS_DOMAIN );
	}

	public static function registered_at() : string {
		return (string) get_option( Constants::OPT_REGISTERED_AT, '' );
	}

	public static function set_registered_at( string $iso ) : void {
		update_option( Constants::OPT_REGISTERED_AT, $iso, false );
	}

	public static function last_error() : string {
		return (string) get_option( Constants::OPT_LAST_ERROR, '' );
	}

	public static function set_last_error( string $msg ) : void {
		update_option( Constants::OPT_LAST_ERROR, $msg, false );
	}

	public static function del_last_error() : void {
		delete_option( Constants::OPT_LAST_ERROR );
	}

	// -----------------------------------------------------------------
	// Subscription-type mirror (display-only, driven by /domains/check).
	// -----------------------------------------------------------------

	public static function plan_interval() : string {
		return (string) get_option( Constants::OPT_PLAN_INTERVAL, '' );
	}

	public static function set_plan_interval( string $interval ) : void {
		update_option( Constants::OPT_PLAN_INTERVAL, $interval, false );
	}

	public static function plan_amount_cents() : int {
		return (int) get_option( Constants::OPT_PLAN_AMOUNT_CENTS, 0 );
	}

	public static function set_plan_amount_cents( int $cents ) : void {
		update_option( Constants::OPT_PLAN_AMOUNT_CENTS, $cents, false );
	}

	public static function plan_currency() : string {
		return (string) get_option( Constants::OPT_PLAN_CURRENCY, '' );
	}

	public static function set_plan_currency( string $currency ) : void {
		update_option( Constants::OPT_PLAN_CURRENCY, strtolower( $currency ), false );
	}

	public static function is_lifetime() : bool {
		return (string) get_option( Constants::OPT_IS_LIFETIME, '' ) === '1';
	}

	public static function set_is_lifetime( bool $isLifetime ) : void {
		update_option( Constants::OPT_IS_LIFETIME, $isLifetime ? '1' : '', false );
	}

	public static function del_plan() : void {
		delete_option( Constants::OPT_PLAN_INTERVAL );
		delete_option( Constants::OPT_PLAN_AMOUNT_CENTS );
		delete_option( Constants::OPT_PLAN_CURRENCY );
		delete_option( Constants::OPT_IS_LIFETIME );
	}

	/**
	 * Read-only snapshot used by LicenseManager / AdminPage so the caller
	 * doesn't need to know each individual option key.
	 *
	 * @return array{
	 *   installation_uuid:string, has_secret:bool, registered_at:string,
	 *   normalized_domain:string, previous_domain:string,
	 *   last_check_at:int, status:string,
	 *   valid_until:int|string, grace_until:int|string, last_error:string
	 * }
	 */
	public static function snapshot() : array {
		return [
			'installation_uuid' => self::installation_uuid(),
			'has_secret'        => self::installation_secret() !== '',
			'registered_at'     => self::registered_at(),
			'normalized_domain' => self::normalized_domain(),
			'previous_domain'   => self::previous_domain(),
			'last_check_at'     => self::last_check_at(),
			'status'            => self::status(),
			'valid_until'       => (string) get_option( Constants::OPT_VALID_UNTIL, '' ),
			'grace_until'       => (string) get_option( Constants::OPT_GRACE_UNTIL, '' ),
			'trial_started_at'  => (string) get_option( Constants::OPT_TRIAL_STARTED_SAAS, '' ),
			'trial_ends_at'     => (string) get_option( Constants::OPT_TRIAL_ENDS_AT, '' ),
			'last_error'        => self::last_error(),
		];
	}
}
