<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * NCS Billing System - License Checker Library
 *
 * This library handles license verification for the installed application.
 * It performs periodic checks against the license server to ensure license validity.
 */
class License_checker {

    private $CI;
    private $license_lock_file;
    private $license_server_url = 'https://khatali.jasiridynamics.com/api/verify-license.php';
    private $check_interval = 604800; // 7 days in seconds
    private $grace_period = 259200; // 3 days grace period after expiry

    public function __construct() {
        $this->CI =& get_instance();
        $this->license_lock_file = FCPATH . 'license.lock';
    }

    /**
     * Check if the license is valid
     * @return array ['valid' => bool, 'message' => string, 'data' => array]
     */
    public function check_license() {
        // Check if license lock file exists
        if (!file_exists($this->license_lock_file)) {
            // Check if we're in installation mode (install.lock doesn't exist)
            $install_lock = dirname($this->license_lock_file) . '/install.lock';
            if (!file_exists($install_lock)) {
                // During installation, allow access without license
                return [
                    'valid' => true,
                    'message' => 'Installation in progress - license will be configured during setup.',
                    'code' => 'INSTALLATION_MODE'
                ];
            }
            
            return [
                'valid' => false,
                'message' => 'License not found. Please reinstall the application with a valid license.',
                'code' => 'NO_LICENSE'
            ];
        }

        // Load license data
        if (!defined('NCS_BILLING')) {
            define('NCS_BILLING', true);
        }
        
        // Suppress warnings and capture any errors
        $license_data = @include($this->license_lock_file);

        if (!$license_data || !is_array($license_data)) {
            // Check if file is readable and contains valid PHP
            if (!is_readable($this->license_lock_file)) {
                // Check if we're in installation mode
                $install_lock = dirname($this->license_lock_file) . '/install.lock';
                if (!file_exists($install_lock)) {
                    return [
                        'valid' => true,
                        'message' => 'Installation in progress - license will be configured during setup.',
                        'code' => 'INSTALLATION_MODE'
                    ];
                }
                return [
                    'valid' => false,
                    'message' => 'License file is not readable. Please check file permissions.',
                    'code' => 'LICENSE_FILE_NOT_READABLE'
                ];
            }
            // Check if we're in installation mode
            $install_lock = dirname($this->license_lock_file) . '/install.lock';
            if (!file_exists($install_lock)) {
                return [
                    'valid' => true,
                    'message' => 'Installation in progress - license will be configured during setup.',
                    'code' => 'INSTALLATION_MODE'
                ];
            }
            return [
                'valid' => false,
                'message' => 'Invalid license file. Please contact support.',
                'code' => 'INVALID_LICENSE_FILE'
            ];
        }

        $is_trial = (!empty($license_data['license_type']) && $license_data['license_type'] === 'trial') ||
                    (!empty($license_data['license_key']) && strpos($license_data['license_key'], 'TRIAL-') === 0);
        $has_basic_data = !empty($license_data['license_key']) && !empty($license_data['system_id']);

        // For monthly/annual (non-lifetime): block access as soon as license has expired (no grace period)
        // Treat is_lifetime as true for boolean true or 1 (lock file / API / var_export)
        $is_lifetime = !empty($license_data['is_lifetime']) && ($license_data['is_lifetime'] === true || $license_data['is_lifetime'] === 1);
        if ($has_basic_data && !$is_lifetime) {
            $expiry_date = isset($license_data['expiry_date']) ? trim((string)$license_data['expiry_date']) : '';
            if ($expiry_date === '') {
                return [
                    'valid' => false,
                    'message' => 'License has expired. Consider renewing.',
                    'code' => 'LICENSE_EXPIRED',
                    'expiry_date' => null
                ];
            }
            $expiry_timestamp = strtotime($expiry_date);
            if ($expiry_timestamp === false || time() > $expiry_timestamp) {
                return [
                    'valid' => false,
                    'message' => 'License has expired. Consider renewing.',
                    'code' => 'LICENSE_EXPIRED',
                    'expiry_date' => $license_data['expiry_date'] ?? null
                ];
            }
        }

        // Allow access if basic license data exists (integrity checks are informational only)
        if ($has_basic_data) {
            $integrity_passed = true;
            if (!$is_trial && !empty($license_data['license_type']) && isset($license_data['checksum'])) {
                $integrity_passed = $this->verify_integrity($license_data);
            }
            return [
                'valid' => true,
                'message' => 'License verified successfully.',
                'code' => 'LICENSE_VALID',
                'warning' => !$integrity_passed && !$is_trial
            ];
        }

        // No basic data - check installation mode or return invalid
        $install_lock = dirname($this->license_lock_file) . '/install.lock';
        if (!file_exists($install_lock)) {
            return [
                'valid' => true,
                'message' => 'Installation in progress - license will be configured during setup.',
                'code' => 'INSTALLATION_MODE'
            ];
        }
        return [
            'valid' => false,
            'message' => 'Invalid license file. Missing required license data.',
            'code' => 'INVALID_LICENSE_DATA'
        ];
    }

    /**
     * Perform periodic license check with server
     */
    private function perform_periodic_check($license_data) {
        // Check if we need to verify with server
        $last_check_file = FCPATH . '.license_check';
        $last_check = 0;

        if (file_exists($last_check_file)) {
            $last_check = (int)file_get_contents($last_check_file);
        }

        // Skip server check if within interval (unless forced)
        if ((time() - $last_check) < $this->check_interval) {
            return [
                'valid' => true,
                'message' => 'License is valid.',
                'code' => 'LICENSE_VALID',
                'data' => [
                    'license_type' => $license_data['license_type_name'] ?? 'Unknown',
                    'expiry_date' => $license_data['expiry_date'] ?? null,
                    'is_lifetime' => $license_data['is_lifetime'] ?? false
                ]
            ];
        }

        // Perform server check
        $server_result = $this->verify_with_server($license_data);

        if ($server_result['success']) {
            // Update last check timestamp
            file_put_contents($last_check_file, time());
        }

        return [
            'valid' => $server_result['success'],
            'message' => $server_result['message'],
            'code' => $server_result['code'] ?? 'UNKNOWN',
            'data' => $server_result['data'] ?? []
        ];
    }

    /**
     * Verify license with remote server (heartbeat)
     */
    private function verify_with_server($license_data) {
        if (!function_exists('curl_init')) {
            // cURL not available - allow local operation
            return [
                'success' => true,
                'message' => 'Offline mode - license accepted.',
                'code' => 'OFFLINE_MODE'
            ];
        }

        $post_data = json_encode([
            'action' => 'heartbeat',
            'license_key' => $license_data['license_key'],
            'system_id' => $license_data['system_id'],
            'integrity_hash' => $license_data['integrity_hash']
        ]);

        $ch = curl_init($this->license_server_url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $post_data,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'Accept: application/json'
            ],
            CURLOPT_TIMEOUT => 10,
            CURLOPT_CONNECTTIMEOUT => 5,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2
        ]);

        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        // Handle connection errors gracefully
        if ($error || !$response) {
            // Allow offline operation if server is unreachable
            return [
                'success' => true,
                'message' => 'License server temporarily unavailable. Offline mode active.',
                'code' => 'SERVER_UNAVAILABLE'
            ];
        }

        $result = json_decode($response, true);

        if (!$result) {
            return [
                'success' => true,
                'message' => 'Invalid server response. Offline mode active.',
                'code' => 'INVALID_RESPONSE'
            ];
        }

        if ($result['status'] === 'valid') {
            return [
                'success' => true,
                'message' => 'License verified successfully.',
                'code' => $result['code'] ?? 'LICENSE_VALID',
                'data' => $result['data'] ?? []
            ];
        }

        // Handle specific error codes
        $action_required = $result['action_required'] ?? null;

        if ($action_required === 'reactivate') {
            return [
                'success' => false,
                'message' => $result['message'] ?? 'License needs reactivation.',
                'code' => $result['code'] ?? 'REACTIVATION_REQUIRED'
            ];
        }

        if ($action_required === 'renew') {
            return [
                'success' => false,
                'message' => $result['message'] ?? 'License has expired. Please renew.',
                'code' => 'LICENSE_EXPIRED'
            ];
        }

        return [
            'success' => false,
            'message' => $result['message'] ?? 'License verification failed.',
            'code' => $result['code'] ?? 'VERIFICATION_FAILED'
        ];
    }

    /**
     * Verify license file integrity
     */
    private function verify_integrity($license_data) {
        if (!isset($license_data['checksum'])) {
            return false;
        }

        $stored_checksum = $license_data['checksum'];
        $data_for_hash = $license_data;
        unset($data_for_hash['checksum']);

        // Match install.php: hash without key sort, same json_encode (no extra options)
        $calculated_checksum = hash('sha256', json_encode($data_for_hash) . 'NCS_LICENSE_LOCK_SECRET');

        return hash_equals($calculated_checksum, $stored_checksum);
    }

    /**
     * Get license information
     */
    public function get_license_info() {
        if (!file_exists($this->license_lock_file)) {
            return null;
        }

        if (!defined('NCS_BILLING')) {
            define('NCS_BILLING', true);
        }

        $license_data = include($this->license_lock_file);

        if (!$license_data || !is_array($license_data)) {
            return null;
        }

        // Calculate days remaining
        $days_remaining = null;
        if (!empty($license_data['expiry_date']) && !$license_data['is_lifetime']) {
            $days_remaining = max(0, ceil((strtotime($license_data['expiry_date']) - time()) / 86400));
        }

        return [
            'license_key' => substr($license_data['license_key'], 0, 10) . '****',
            'name' => $license_data['name'],
            'email' => $license_data['email'],
            'license_type' => $license_data['license_type_name'],
            'expiry_date' => $license_data['expiry_date'],
            'is_lifetime' => $license_data['is_lifetime'],
            'days_remaining' => $days_remaining,
            'activated_at' => $license_data['activated_at'],
            'domain' => $license_data['domain']
        ];
    }

    /**
     * Check if license allows a specific feature
     */
    public function has_feature($feature_name) {
        if (!file_exists($this->license_lock_file)) {
            return false;
        }

        if (!defined('NCS_BILLING')) {
            define('NCS_BILLING', true);
        }

        $license_data = include($this->license_lock_file);

        if (!$license_data || !isset($license_data['features'])) {
            return false;
        }

        $features = $license_data['features'];

        if (is_string($features)) {
            $features = json_decode($features, true);
        }

        return isset($features[$feature_name]) && $features[$feature_name];
    }

    /**
     * Get maximum allowed users
     */
    public function get_max_users() {
        if (!file_exists($this->license_lock_file)) {
            return 1;
        }

        if (!defined('NCS_BILLING')) {
            define('NCS_BILLING', true);
        }

        $license_data = include($this->license_lock_file);

        if (!$license_data || !isset($license_data['features'])) {
            return 1;
        }

        $features = $license_data['features'];

        if (is_string($features)) {
            $features = json_decode($features, true);
        }

        return isset($features['max_users']) ? (int)$features['max_users'] : 1;
    }

    /**
     * Generate system fingerprint (same logic as installer for consistency)
     */
    private function generate_system_id() {
        $data = array(
            $_SERVER['SERVER_NAME'] ?? 'unknown',
            $_SERVER['DOCUMENT_ROOT'] ?? 'unknown',
            php_uname('n'),
            php_uname('s'),
            PHP_VERSION,
            realpath(FCPATH)
        );
        return hash('sha256', implode('|', $data));
    }

    /**
     * Verify new license with server and save to license.lock (reactivate).
     * Supports Monthly, Annual, and Lifetime (no expiry).
     *
     * @param string $license_key
     * @param string $name
     * @param string $email
     * @return array ['success' => bool, 'message' => string, 'data' => array|null]
     */
    public function verify_and_reactivate($license_key, $name, $email) {
        $license_key = trim($license_key);
        $name = trim($name);
        $email = trim($email);
        if (empty($license_key) || empty($name) || empty($email)) {
            return array('success' => false, 'message' => 'License key, name and email are required.');
        }
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return array('success' => false, 'message' => 'Invalid email format.');
        }

        // Reject re-use of same old/expired license (user must enter a new license key)
        $current = $this->get_current_license_for_reactivate();
        if ($current) {
            $same_key = (strcasecmp($current['license_key'], $license_key) === 0);
            if ($same_key && $current['expired']) {
                return array(
                    'success' => false,
                    'message' => 'This is the same license that has expired. Please enter a new license key to reactivate.'
                );
            }
            if ($same_key && !$current['expired']) {
                return array(
                    'success' => false,
                    'message' => 'This license is already active on this system. To change license, enter a different (new) license key.'
                );
            }
        }

        $system_id = $this->get_system_id_for_reactivation();
        $domain = $this->get_current_domain();
        $server_info = array(
            'php_version' => PHP_VERSION,
            'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? 'unknown',
            'document_root' => $_SERVER['DOCUMENT_ROOT'] ?? 'unknown',
            'server_name' => $_SERVER['SERVER_NAME'] ?? 'unknown',
            'os' => php_uname('s') . ' ' . php_uname('r'),
            'hostname' => php_uname('n'),
            'install_path' => realpath(FCPATH)
        );

        $post_data = json_encode(array(
            'action' => 'verify',
            'license_key' => $license_key,
            'name' => $name,
            'email' => $email,
            'system_id' => $system_id,
            'domain' => $domain,
            'server_info' => $server_info
        ));

        if (!function_exists('curl_init')) {
            return array('success' => false, 'message' => 'cURL is required to verify the license.');
        }
        $ch = curl_init($this->license_server_url);
        curl_setopt_array($ch, array(
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $post_data,
            CURLOPT_HTTPHEADER => array('Content-Type: application/json', 'Accept: application/json'),
            CURLOPT_TIMEOUT => 30,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2
        ));
        $response = curl_exec($ch);
        $error = curl_error($ch);
        curl_close($ch);

        if ($error) {
            return array('success' => false, 'message' => 'Could not connect to license server. Please check your internet connection.');
        }
        $result = json_decode($response, true);
        if (!$result || empty($result['status'])) {
            return array('success' => false, 'message' => 'Invalid response from license server.');
        }
        if ($result['status'] !== 'valid' || empty($result['data'])) {
            return array('success' => false, 'message' => isset($result['message']) ? $result['message'] : 'License verification failed.');
        }

        $license_data = $result['data'];
        $license_data['domain'] = $domain;
        if (empty($license_data['system_id'])) {
            $license_data['system_id'] = $system_id;
        }

        $lock_data = array(
            'license_key' => $license_data['license_key'],
            'name' => $license_data['name'],
            'email' => $license_data['email'],
            'license_type' => isset($license_data['license_type']) ? $license_data['license_type'] : 'standard',
            'license_type_name' => isset($license_data['license_type_name']) ? $license_data['license_type_name'] : 'Standard',
            'system_id' => $license_data['system_id'],
            'domain' => $license_data['domain'],
            'expiry_date' => isset($license_data['expiry_date']) ? $license_data['expiry_date'] : null,
            'is_lifetime' => !empty($license_data['is_lifetime']),
            'activated_at' => isset($license_data['activated_at']) ? $license_data['activated_at'] : date('Y-m-d H:i:s'),
            'integrity_hash' => isset($license_data['integrity_hash']) ? $license_data['integrity_hash'] : hash('sha256', $license_data['system_id'] . $license_data['domain'] . ($license_data['activated_at'] ?? '')),
            'features' => isset($license_data['features']) ? $license_data['features'] : array('premium' => true, 'max_users' => 10),
            'created_at' => date('Y-m-d H:i:s')
        );
        $lock_data['checksum'] = hash('sha256', json_encode($lock_data) . 'NCS_LICENSE_LOCK_SECRET');

        $content = "<?php\n";
        $content .= "// NCS Billing System - License Lock File\n";
        $content .= "// DO NOT MODIFY OR DELETE THIS FILE\n";
        $content .= "// Generated: " . date('Y-m-d H:i:s') . "\n\n";
        $content .= "if (!defined('NCS_BILLING')) { exit('Direct access not allowed'); }\n\n";
        $content .= "return " . var_export($lock_data, true) . ";\n";

        if (file_put_contents($this->license_lock_file, $content) === false) {
            return array('success' => false, 'message' => 'Failed to save license file. Please check write permissions.');
        }

        $cache_file = FCPATH . '.license_valid_cache';
        $check_file = FCPATH . '.license_check';
        if (file_exists($cache_file)) {
            @unlink($cache_file);
        }
        if (file_exists($check_file)) {
            @unlink($check_file);
        }

        $expiry_display = !empty($lock_data['is_lifetime']) ? 'Never (Lifetime)' : ($lock_data['expiry_date'] ?? 'N/A');
        return array(
            'success' => true,
            'message' => 'License activated successfully. Your license is valid until ' . $expiry_display . '.',
            'data' => array(
                'license_type' => $lock_data['license_type_name'],
                'expiry_date' => $lock_data['expiry_date'],
                'is_lifetime' => $lock_data['is_lifetime'],
                'expiry_display' => $expiry_display
            )
        );
    }

    /**
     * Get current license key and expiry state for reactivate form (detect "old license" re-use).
     * @return array|null ['license_key' => string, 'expired' => bool] or null if no lock file
     */
    private function get_current_license_for_reactivate() {
        if (!file_exists($this->license_lock_file)) {
            return null;
        }
        if (!defined('NCS_BILLING')) {
            define('NCS_BILLING', true);
        }
        $data = @include($this->license_lock_file);
        if (!$data || !is_array($data) || empty($data['license_key'])) {
            return null;
        }
        $is_lifetime = !empty($data['is_lifetime']) && ($data['is_lifetime'] === true || $data['is_lifetime'] === 1);
        $expiry = isset($data['expiry_date']) ? trim((string)$data['expiry_date']) : '';
        $expired = false;
        if (!$is_lifetime) {
            if ($expiry === '') {
                $expired = true;
            } else {
                $ts = strtotime($expiry);
                $expired = ($ts === false || time() > $ts);
            }
        }
        return array(
            'license_key' => $data['license_key'],
            'expired' => $expired
        );
    }

    /**
     * Get system_id from current license file if present, otherwise generate (for reactivation same machine)
     */
    private function get_system_id_for_reactivation() {
        if (file_exists($this->license_lock_file)) {
            if (!defined('NCS_BILLING')) {
                define('NCS_BILLING', true);
            }
            $data = @include($this->license_lock_file);
            if (!empty($data['system_id'])) {
                return $data['system_id'];
            }
        }
        return $this->generate_system_id();
    }

    /**
     * Current domain for license (match installer logic)
     */
    private function get_current_domain() {
        $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) ? 'https://' : 'http://';
        $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
        $uri = $_SERVER['REQUEST_URI'] ?? '';
        $base = $protocol . $host;
        if (preg_match('#^(.*?)/index\.php#', $uri, $m)) {
            $base .= rtrim($m[1], '/');
        }
        return rtrim($base, '/');
    }
}
