<?php

/**
 * SquareTokenManager
 * ─────────────────────────────────────────────────────────
 * • Encrypts & stores BOTH access- and refresh-tokens
 * • Schedules a weekly cron that POSTs the refresh token to the
 *   central auth-proxy (remote server keeps client-id + secret)
 *
 * File is fully self-contained: include it once and `SquareTokenManager::init()`
 * wires up all hooks (activation, de-activation, cron schedule, job callback).
 *
 * @package Pixeldev\SquareWooSync\Security
 */

namespace Pixeldev\SquareWooSync\Security;

defined('ABSPATH') || exit;

use WP_Error;

class SquareTokenManager
{

  /* ─────────────────────────────  Config  ──────────────────────────── */
  private const OPTION          = 'squarewoosync_tokens';   // wp_options row
  private const CRON_HOOK       = 'sws_refresh_square_tokens_weekly';
  private const REMOTE_ENDPOINT = 'https://proxy.squaresyncforwoo.com/wp-json/square-oauth/v1/refresh-token';

  /* ───────────────────────────  Initialization  ───────────────────── */
  public static function init(): void
  {

    /* 1. make sure a weekly interval exists */
    add_filter('cron_schedules', static function ($s) {
      if (! isset($s['weekly'])) {
        $s['weekly'] = ['interval' => 7 * DAY_IN_SECONDS, 'display' => 'Once Weekly'];
      }
      return $s;
    });

    /* 2. schedule the job every normal request if missing */
    add_action('init', static function () {
      if (! wp_next_scheduled(self::CRON_HOOK)) {
        wp_schedule_event(time() + HOUR_IN_SECONDS, 'weekly', self::CRON_HOOK);
      }
    });

    /* 3. clear on plugin de-activation (must be called from main plugin file) */
    register_deactivation_hook(SQUAREWOOSYNC_FILE, function () {
      wp_clear_scheduled_hook(self::CRON_HOOK);
    });

    /* 4. register the callback */
    add_action(self::CRON_HOOK, [__CLASS__, 'refresh_tokens_job']);
  }


  private static function get_site_secret(): string
  {
    $secret = get_option('sws_site_secret');
    if ($secret) return $secret;

    $secret = wp_generate_password(64, true, true); // 64-char secure random key
    add_option('sws_site_secret', $secret, '', 'no'); // don't autoload
    return $secret;
  }

  public static function on_activation(): void
  {
    if (! wp_next_scheduled(self::CRON_HOOK)) {
      wp_schedule_event(time() + DAY_IN_SECONDS, 'weekly', self::CRON_HOOK);
    }
  }

  public static function on_deactivation(): void
  {
    wp_clear_scheduled_hook(self::CRON_HOOK);
  }

  /* ──────────────────────────  Encryption  ────────────────────────── */
  private static function encrypt(string $plain): string
  {
    $iv  = random_bytes(12);
    $tag = '';
    $key = self::get_site_secret();

    $c = openssl_encrypt($plain, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
    return base64_encode($iv . $tag . $c);
  }

  private static function decrypt(string $payload)
  {
    $raw = base64_decode($payload, true);
    if (! $raw || strlen($raw) < 29) {
      return false;
    }

    $iv  = substr($raw, 0, 12);
    $tag = substr($raw, 12, 16);
    $c   = substr($raw, 28);
    $key = self::get_site_secret();

    return openssl_decrypt($c, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
  }

  /* ───────────────────────────  CRUD  ─────────────────────────────── */
  public static function save(string $access, ?string $refresh = null): bool
  {

    $row = get_option(self::OPTION, []);
    $row['access'] = self::encrypt($access);
    if ($refresh !== null) {
      $row['refresh'] = self::encrypt($refresh);
    }
    $row['saved'] = time();

    return false === get_option(self::OPTION, false)
      ? add_option(self::OPTION, $row, '', 'no')
      : update_option(self::OPTION, $row);
  }

  public static function overwrite_access(string $access): void
  {
    $row = get_option(self::OPTION, []);
    $row['access'] = self::encrypt($access);
    $row['saved']  = time();
    update_option(self::OPTION, $row, false);
  }

  public static function get()
  {
    $row = get_option(self::OPTION);
    return $row['access'] ?? false ? self::decrypt($row['access']) : false;
  }

  public static function get_refresh()
  {
    $row = get_option(self::OPTION);
    return $row['refresh'] ?? null ? self::decrypt($row['refresh']) : null;
  }

  public static function clear(): bool
  {
    return delete_option(self::OPTION);
  }

  /* ───────────────────────────  CRON job  ─────────────────────────── */
  /**
   * Weekly job: exchange refresh_token for new pair via remote proxy.
   */
  public static function refresh_tokens_job(): void
  {

    $refresh = self::get_refresh();
    if (! $refresh) {
      return;                         // nothing stored yet
    }

    $settings = get_option('square-woo-sync_settings', []);
    $env      = ($settings['environment'] ?? 'live') === 'sandbox' ? 'sandbox' : 'live';

    $body = wp_json_encode(['env' => $env, 'refresh_token' => $refresh]);

    $response = wp_remote_post(self::REMOTE_ENDPOINT, [
      'headers' => ['Content-Type' => 'application/json'],
      'body'    => $body,
      'timeout' => 20,
    ]);

    if (is_wp_error($response)) {
      error_log('[Square] Refresh job WP_Error: ' . $response->get_error_message());
      return;
    }

    $code = wp_remote_retrieve_response_code($response);
    $data = json_decode(wp_remote_retrieve_body($response), true);

    if ($code === 200 && isset($data['access_token'], $data['refresh_token'])) {
      self::save($data['access_token'], $data['refresh_token']);
    } else {
      error_log('[Square] Refresh job failed: ' . print_r($data, true));
    }
  }
}
