<?php
namespace Formipay;
use Exception;

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

class Token {

    private $table_name;
    
    public function __construct() {

        global $wpdb;
        $this->table_name = $wpdb->prefix . 'formipay_tokens';
        
        add_action( 'init',                     [$this, 'create_db'] );
        // Setup cleanup hook
        add_action( 'formipay_daily_cleanup', [$this, 'cleanup_expired_tokens']);

        if (!wp_next_scheduled('formipay_daily_cleanup')) {
            wp_schedule_event(time(), 'daily', 'formipay_daily_cleanup');
        }

    }

    public function create_db() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'formipay_tokens';
        
        $sql = "CREATE TABLE $table_name (
            token CHAR(64) NOT NULL,
            form_id BIGINT UNSIGNED NOT NULL,
            order_id BIGINT UNSIGNED NOT NULL,
            created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            expires_at DATETIME NOT NULL,
            used TINYINT(1) NOT NULL DEFAULT 0,
            use_count INT NOT NULL DEFAULT 0,
            PRIMARY KEY (token),
            KEY expires_at_idx (expires_at),
            KEY order_id_idx (order_id)
        ) {$wpdb->get_charset_collate()};";
        
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
    }

    /**
     * Generate cryptographically secure token
     * 
     * @param int $order_id
     * @param int $form_id
     * @param int $expires_in Seconds until expiration (default: 900 = 15 mins)
     * @return string
     */
    public function generate(int $order_id, int $form_id, int $expires_in = 900): string {
        global $wpdb;
        
        $token = bin2hex(random_bytes(32)); // 64-character token
        $expires_at = formipay_date('Y-m-d H:i:s', time() + $expires_in);
        
        $wpdb->insert($this->table_name, [
            'token' => $token,
            'form_id' => $form_id,
            'order_id' => $order_id,
            'expires_at' => $expires_at,
            'created_at' => formipay_date('Y-m-d H:i:s')
        ]);
        
        return $token;
    }

    /**
     * Validate token and retrieve order data
     * 
     * @param string $token
     * @return array|false
     */
    public function validate(string $token) {
        global $wpdb;
        
        // Validate token format
        if (!preg_match('/^[a-f0-9]{64}$/', $token)) return false;
        
        $query = $wpdb->prepare(
            "SELECT form_id, order_id 
             FROM {$this->table_name} 
             WHERE token = %s 
             AND expires_at > NOW() 
             AND used = 0",
            $token
        );
        
        return $wpdb->get_row($query, ARRAY_A);
    }

    /**
     * Mark token as used
     * 
     * @param string $token
     * @return bool
     */
    public function mark_used(string $token): bool {
        global $wpdb;
        return (bool) $wpdb->update(
            $this->table_name,
            ['used' => 1],
            ['token' => $token]
        );
    }

    /**
     * Cleanup expired tokens
     */
    public function cleanup_expired_tokens() {
        global $wpdb;
        $wpdb->query("DELETE FROM {$this->table_name} WHERE expires_at < NOW()");
    }

    /**
     * Get token usage count
     * 
     * @param string $token
     * @return int
     */
    public function get_usage_count(string $token): int {
        global $wpdb;
        return (int) $wpdb->get_var($wpdb->prepare(
            "SELECT use_count FROM {$this->table_name} WHERE token = %s",
            $token
        ));
    }

    /**
     * Increment token usage
     * 
     * @param string $token
     * @param int $max_uses
     * @return bool
     * @throws Exception
     */
    public function increment_usage(string $token, int $max_uses = 5): bool {
        global $wpdb;
        
        $current_count = $this->get_usage_count($token);
        if ($current_count >= $max_uses) {
            throw new Exception('Token usage limit reached');
        }
        
        return (bool) $wpdb->update(
            $this->table_name,
            ['use_count' => $current_count + 1],
            ['token' => $token]
        );
    }
}
