<?php
/**
 * ChainPay License System
 *
 * Business model:
 *  FREE  — 50 payments/month, BTC + LTC only, no license key needed
 *  PRO   — Unlimited payments, all 14 coins, $49/yr, domain-locked
 *
 * Domain locking: once a key is activated on a domain, it only works
 * on that domain. The API enforces this server-side; the plugin also
 * checks locally for fast feedback.
 */
defined( 'ABSPATH' ) || exit;

class ChainPay_License {

    // WP option keys
    const OPT_KEY          = 'chainpay_license_key';
    const OPT_STATUS       = 'chainpay_license_status';     // 'active' | 'inactive'
    const OPT_EXPIRES      = 'chainpay_license_expires';
    const OPT_PLAN         = 'chainpay_license_plan';       // 'free' | 'pro'
    const OPT_DOMAIN       = 'chainpay_license_domain';     // domain this key is bound to
    const OPT_CHECKED      = 'chainpay_license_last_check';
    const OPT_MONTHLY      = 'chainpay_monthly_count';      // payments this month
    const OPT_MONTH        = 'chainpay_monthly_reset';      // 'YYYY-MM'
    const OPT_USAGE_SYNC   = 'chainpay_usage_last_sync';

    const FREE_LIMIT       = CHAINPAY_FREE_LIMIT; // 50
    const CACHE_DAYS       = 7;    // offline grace period
    const VERIFY_INTERVAL  = DAY_IN_SECONDS;

    /* ── Is licensed and active? ──────────────────────────── */
    public static function is_active() {
        $key = (string) get_option( self::OPT_KEY, '' );
        if ( '' === $key ) return false;

        // Expiry check from cache
        $expires = (string) get_option( self::OPT_EXPIRES, '' );
        if ( $expires && strtotime( $expires ) < time() ) {
            self::set_inactive( 'License expired.' );
            return false;
        }

        // Domain check — license must match current domain
        $locked_domain = (string) get_option( self::OPT_DOMAIN, '' );
        if ( $locked_domain && $locked_domain !== home_url() && $locked_domain !== rtrim( home_url(), '/' ) ) {
            // Domain mismatch — don't silently pass
            return false;
        }

        // Periodic re-verify
        $last = (int) get_option( self::OPT_CHECKED, 0 );
        if ( ( time() - $last ) > self::VERIFY_INTERVAL ) {
            $result = self::verify( $key );
            if ( ! $result['reached_api'] ) {
                // API unreachable — grace period
                $grace_ok = ( time() - $last ) < ( self::CACHE_DAYS * DAY_IN_SECONDS );
                return $grace_ok && ( 'active' === get_option( self::OPT_STATUS, 'inactive' ) );
            }
        }

        return 'active' === get_option( self::OPT_STATUS, 'inactive' );
    }

    /* ── Plan string ──────────────────────────────────────── */
    public static function plan() {
        return self::is_active() ? (string) get_option( self::OPT_PLAN, 'pro' ) : 'free';
    }

    /* ── Monthly usage ────────────────────────────────────── */
    public static function get_monthly_count() {
        self::maybe_reset_monthly();
        return (int) get_option( self::OPT_MONTHLY, 0 );
    }

    public static function increment_monthly() {
        self::maybe_reset_monthly();
        $count = (int) get_option( self::OPT_MONTHLY, 0 );
        update_option( self::OPT_MONTHLY, $count + 1 );
        return $count + 1;
    }

    public static function is_over_free_limit() {
        if ( self::is_active() ) return false; // Pro = unlimited
        return self::get_monthly_count() >= self::FREE_LIMIT;
    }

    public static function remaining_free() {
        if ( self::is_active() ) return PHP_INT_MAX;
        return max( 0, self::FREE_LIMIT - self::get_monthly_count() );
    }

    private static function maybe_reset_monthly() {
        $current_month = gmdate( 'Y-m' );
        $stored_month  = (string) get_option( self::OPT_MONTH, '' );
        if ( $stored_month !== $current_month ) {
            update_option( self::OPT_MONTHLY, 0 );
            update_option( self::OPT_MONTH, $current_month );
        }
    }

    /* ── Verify license key with API ──────────────────────── */
    public static function verify( $key ) {
        if ( '' === trim( $key ) ) {
            self::set_inactive( 'No key provided.' );
            return array( 'success' => false, 'message' => 'No license key provided.', 'reached_api' => false );
        }

        $response = wp_remote_post(
            CHAINPAY_API_BASE . '/licenses/verify',
            array(
                'timeout' => 15,
                'headers' => array( 'Content-Type' => 'application/json' ),
                'body'    => wp_json_encode( array(
                    'license_key'      => $key,
                    'domain'           => home_url(),
                    'plugin_ver'       => CHAINPAY_VERSION,
                    'monthly_count'    => (int) get_option( self::OPT_MONTHLY, 0 ),
                ) ),
            )
        );

        update_option( self::OPT_CHECKED, time() );

        if ( is_wp_error( $response ) ) {
            return array(
                'success'     => ( 'active' === get_option( self::OPT_STATUS ) ),
                'message'     => 'Could not reach chainpay.store API. Using cached status.',
                'reached_api' => false,
            );
        }

        $body = json_decode( wp_remote_retrieve_body( $response ), true );

        if ( ! is_array( $body ) ) {
            return array( 'success' => false, 'message' => 'Invalid API response.', 'reached_api' => true );
        }

        if ( ! empty( $body['active'] ) ) {
            // Store license details
            update_option( self::OPT_KEY,     $key );
            update_option( self::OPT_STATUS,  'active' );
            update_option( self::OPT_EXPIRES, isset( $body['expires'] )         ? $body['expires']         : '' );
            update_option( self::OPT_PLAN,    isset( $body['plan'] )            ? $body['plan']            : 'pro' );
            update_option( self::OPT_DOMAIN,  isset( $body['domain'] )          ? $body['domain']          : home_url() );

            // Sync server-side monthly count
            if ( isset( $body['monthly_count'] ) ) {
                self::maybe_reset_monthly();
                update_option( self::OPT_MONTHLY, (int) $body['monthly_count'] );
                update_option( self::OPT_USAGE_SYNC, time() );
            }

            return array(
                'success'       => true,
                'message'       => 'License active.',
                'plan'          => $body['plan'] ?? 'pro',
                'expires'       => $body['expires'] ?? '',
                'domain'        => $body['domain'] ?? home_url(),
                'monthly_count' => $body['monthly_count'] ?? 0,
                'reached_api'   => true,
            );
        }

        // License invalid or domain mismatch
        $msg = $body['message'] ?? 'Invalid license key.';
        $code = $body['code'] ?? '';

        if ( 'DOMAIN_LOCKED' === $code ) {
            // Don't wipe the key — just mark inactive
            update_option( self::OPT_STATUS, 'inactive' );
        } else {
            self::set_inactive( $msg );
        }

        return array( 'success' => false, 'message' => $msg, 'code' => $code, 'reached_api' => true );
    }

    /* ── Deactivate / revoke ──────────────────────────────── */
    public static function deactivate() {
        $key = (string) get_option( self::OPT_KEY, '' );
        if ( '' !== $key ) {
            wp_remote_post(
                CHAINPAY_API_BASE . '/licenses/deactivate',
                array(
                    'timeout' => 10,
                    'headers' => array( 'Content-Type' => 'application/json' ),
                    'body'    => wp_json_encode( array( 'license_key' => $key, 'domain' => home_url() ) ),
                )
            );
        }
        self::set_inactive( 'Deactivated.' );
        delete_option( self::OPT_KEY );
        delete_option( self::OPT_DOMAIN );
    }

    private static function set_inactive( $reason = '' ) {
        update_option( self::OPT_STATUS, 'inactive' );
        update_option( self::OPT_PLAN,   'free' );
        if ( $reason ) update_option( 'chainpay_license_inactive_reason', $reason );
    }

    /* ── Summary (for admin display) ─────────────────────── */
    public static function summary() {
        self::maybe_reset_monthly();
        $key     = (string) get_option( self::OPT_KEY, '' );
        $is_pro  = self::is_active();
        $count   = (int) get_option( self::OPT_MONTHLY, 0 );
        return array(
            'key'              => self::mask( $key ),
            'raw_key'          => $key,
            'status'           => $is_pro ? 'active' : 'inactive',
            'plan'             => $is_pro ? (string) get_option( self::OPT_PLAN, 'pro' ) : 'free',
            'expires'          => (string) get_option( self::OPT_EXPIRES, '' ),
            'domain'           => (string) get_option( self::OPT_DOMAIN, '' ),
            'monthly_count'    => $count,
            'monthly_limit'    => $is_pro ? 'unlimited' : self::FREE_LIMIT,
            'monthly_remaining'=> $is_pro ? 'unlimited' : max( 0, self::FREE_LIMIT - $count ),
            'over_limit'       => ! $is_pro && $count >= self::FREE_LIMIT,
            'is_pro'           => $is_pro,
        );
    }

    public static function mask( $key ) {
        if ( strlen( $key ) < 8 ) return $key;
        return substr( $key, 0, 12 ) . str_repeat( '•', max( 0, strlen( $key ) - 16 ) ) . substr( $key, -4 );
    }
}
