<?php
/*
 * Copyright (C) 2026 Mamontov Roman Igorevich
 *
 * This file is part of "Mini-Bucket - NAS Control Panel".
 *
 * Mini-Bucket - NAS Control Panel is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version, with the plugin exception (see LICENSE file).
 * Commercial use requires purchasing a separate commercial license from the copyright holder.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * https://mini-b.itp-corp.ru/
 */

define('ROOT_PATH', dirname(dirname(__FILE__)));

if (file_exists(ROOT_PATH . '/config.php')) {
    require_once ROOT_PATH . '/config.php';
}

header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, X-API-Key, Authorization, X-Requested-With");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Max-Age: 86400");

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit();
}

header('Content-Type: application/json');

function validateApiKey() {
    global $db;
    
    if (!$db) {
        try {
            $db = getDB();
        } catch (Exception $e) {
            http_response_code(500);
            echo json_encode(['error' => 'Database connection failed']);
            exit;
        }
    }
    
    $headers = function_exists('getallheaders') ? getallheaders() : [];
    $apiKey = $headers['X-API-Key'] ?? $_GET['api_key'] ?? $_SERVER['HTTP_X_API_KEY'] ?? '';
    
    if (empty($apiKey)) {
        if (isset($_SESSION['user_id'])) {
            return true;
        }
        http_response_code(401);
        echo json_encode(['error' => 'API key required']);
        exit;
    }
    
    $stmt = $db->prepare("SELECT idHost, hostName FROM hosts WHERE hostApiKey = :key");
    $stmt->bindValue(':key', $apiKey, SQLITE3_TEXT);
    $result = $stmt->execute();
    $host = $result->fetchArray(SQLITE3_ASSOC);
    
    if (!$host) {
        http_response_code(403);
        echo json_encode(['error' => 'Invalid API key']);
        exit;
    }
    
    return true;
}

validateApiKey();

define('APACHE_SITES_AVAILABLE', '/etc/apache2/sites-available/');
define('APACHE_SITES_ENABLED', '/etc/apache2/sites-enabled/');
define('MINIB_CONFIG_FILE', APACHE_SITES_AVAILABLE . 'minib.conf');
define('MINIB_CONFIG_ENABLED', APACHE_SITES_ENABLED . 'minib.conf');
define('CERT_DIR', '/var/www/minib/certs/crt');

function getApacheStatus() {
    $status = shell_exec("systemctl is-active apache2 2>/dev/null");
    return trim($status) === 'active';
}

function restartApache() {
    shell_exec("sudo systemctl restart apache2 2>&1");
    sleep(2);
    return getApacheStatus();
}

function reloadApache() {
    shell_exec("sudo systemctl reload apache2 2>&1");
    return true;
}

function testApacheConfig() {
    exec("sudo apache2ctl configtest 2>&1", $output, $returnCode);
    $outputStr = implode("\n", $output);
    return [
        'success' => $returnCode === 0,
        'output' => $outputStr
    ];
}

function generateApacheConfig($port, $documentRoot, $sslEnabled = false, $sslCert = '', $sslKey = '', $sslChain = '') {
    $config = "# Mini-B Web Interface Configuration\n";
    $config .= "# Generated by Mini-B Panel on " . date('Y-m-d H:i:s') . "\n";
    $config .= "# Do not edit manually - changes may be overwritten\n\n";
    
    if ($sslEnabled && !empty($sslCert) && !empty($sslKey)) {
        $config .= "Listen $port\n\n";
        $config .= "<VirtualHost *:$port>\n";
        $config .= "    ServerAdmin webmaster@localhost\n";
        $config .= "    DocumentRoot $documentRoot\n\n";
        $config .= "    <Directory $documentRoot>\n";
        $config .= "        Options Indexes FollowSymLinks\n";
        $config .= "        AllowOverride All\n";
        $config .= "        Require all granted\n";
        $config .= "    </Directory>\n\n";
        $config .= "    # SSL Configuration\n";
        $config .= "    SSLEngine on\n";
        $config .= "    SSLCertificateFile $sslCert\n";
        $config .= "    SSLCertificateKeyFile $sslKey\n";
        if (!empty($sslChain)) {
            $config .= "    SSLCertificateChainFile $sslChain\n";
        }
        $config .= "    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1\n";
        $config .= "    SSLCipherSuite HIGH:!aNULL:!MD5\n\n";
        $config .= "    ErrorLog /var/www/minib/logs/web/admin_error.log\n";
        $config .= "    CustomLog /var/www/minib/logs/web/admin_access.log combined\n";
        $config .= "</VirtualHost>\n";
    } else {
        $config .= "Listen $port\n\n";
        $config .= "<VirtualHost *:$port>\n";
        $config .= "    ServerAdmin webmaster@localhost\n";
        $config .= "    DocumentRoot $documentRoot\n\n";
        $config .= "    <Directory $documentRoot>\n";
        $config .= "        Options Indexes FollowSymLinks\n";
        $config .= "        AllowOverride All\n";
        $config .= "        Require all granted\n";
        $config .= "    </Directory>\n\n";
        $config .= "    ErrorLog /var/www/minib/logs/web/admin_error.log\n";
        $config .= "    CustomLog /var/www/minib/logs/web/admin_access.log combined\n";
        $config .= "</VirtualHost>\n";
    }
    
    return $config;
}

function getCurrentConfig() {
    $content = false;
    
    $content = shell_exec("sudo cat " . MINIB_CONFIG_FILE . " 2>/dev/null");
    
    if (empty($content)) {
        if (file_exists(MINIB_CONFIG_FILE)) {
            $content = @file_get_contents(MINIB_CONFIG_FILE);
        }
    }
    
    if (empty($content)) {
        $defaultConfig = generateApacheConfig(1488, '/var/www/html/admin', false, '', '', '');
        return [
            'enabled' => true,
            'port' => 1488,
            'document_root' => '/var/www/html/admin',
            'ssl_enabled' => false,
            'ssl_cert' => '',
            'ssl_key' => '',
            'ssl_chain' => '',
            'config_content' => $defaultConfig,
            'config_path' => MINIB_CONFIG_FILE,
            'last_modified' => null,
            'site_enabled' => file_exists(MINIB_CONFIG_ENABLED)
        ];
    }
    
    $port = 1488;
    $documentRoot = '/var/www/html/admin';
    $sslEnabled = false;
    $sslCert = '';
    $sslKey = '';
    $sslChain = '';
    
    if (preg_match('/Listen\s+(\d+)/', $content, $matches)) {
        $port = (int)$matches[1];
    }
    
    if (preg_match('/DocumentRoot\s+([^\s]+)/', $content, $matches)) {
        $documentRoot = trim($matches[1]);
    }
    
    if (strpos($content, 'SSLEngine on') !== false) {
        $sslEnabled = true;
        
        if (preg_match('/SSLCertificateFile\s+([^\s]+)/', $content, $matches)) {
            $sslCert = trim($matches[1]);
        }
        if (preg_match('/SSLCertificateKeyFile\s+([^\s]+)/', $content, $matches)) {
            $sslKey = trim($matches[1]);
        }
        if (preg_match('/SSLCertificateChainFile\s+([^\s]+)/', $content, $matches)) {
            $sslChain = trim($matches[1]);
        }
    }
    
    $lastModified = null;
    $statOutput = shell_exec("sudo stat -c %Y " . MINIB_CONFIG_FILE . " 2>/dev/null");
    if (!empty($statOutput) && is_numeric(trim($statOutput))) {
        $lastModified = date('Y-m-d H:i:s', (int)trim($statOutput));
    }
    
    return [
        'enabled' => file_exists(MINIB_CONFIG_ENABLED),
        'port' => $port,
        'document_root' => $documentRoot,
        'ssl_enabled' => $sslEnabled,
        'ssl_cert' => $sslCert,
        'ssl_key' => $sslKey,
        'ssl_chain' => $sslChain,
        'config_content' => $content,
        'config_path' => MINIB_CONFIG_FILE,
        'last_modified' => $lastModified,
        'site_enabled' => file_exists(MINIB_CONFIG_ENABLED)
    ];
}

function saveApacheConfig($config) {
    $dir = dirname(MINIB_CONFIG_FILE);
    if (!is_dir($dir)) {
        exec("sudo mkdir -p $dir 2>&1");
    }
    
    $tempFile = '/tmp/minib_temp.conf';
    file_put_contents($tempFile, $config);
    
    exec("sudo cp $tempFile " . MINIB_CONFIG_FILE . " 2>&1", $output, $returnCode);
    unlink($tempFile);
    
    if ($returnCode !== 0) {
        return false;
    }
    
    return true;
}

function enableSite() {
    exec("sudo a2ensite minib.conf 2>&1", $output, $returnCode);
    return $returnCode === 0;
}

function disableSite() {
    exec("sudo a2dissite minib.conf 2>&1", $output, $returnCode);
    return $returnCode === 0;
}

function ensureSSLModuleEnabled() {
    $enabledModules = shell_exec("sudo a2query -m ssl 2>/dev/null");
    if (trim($enabledModules) !== 'enabled') {
        exec("sudo a2enmod ssl 2>&1", $output, $returnCode);
        return $returnCode === 0;
    }
    return true;
}

function getCertificatesList() {
    $certs = [];
    if (!is_dir(CERT_DIR)) return $certs;
    
    $files = glob(CERT_DIR . '/*.crt');
    
    foreach ($files as $crtFile) {
        $baseName = pathinfo($crtFile, PATHINFO_FILENAME);
        $keyFile = CERT_DIR . '/' . $baseName . '.key';
        $chainFile = CERT_DIR . '/' . $baseName . '.chain.pem';
        
        $certInfo = [
            'name' => $baseName,
            'crt_path' => $crtFile,
            'key_path' => file_exists($keyFile) ? $keyFile : null,
            'chain_path' => file_exists($chainFile) ? $chainFile : null,
            'has_key' => file_exists($keyFile)
        ];
        
        $certContent = file_get_contents($crtFile);
        $parsed = openssl_x509_parse($certContent);
        if ($parsed) {
            $certInfo['subject'] = $parsed['subject']['CN'] ?? 'Unknown';
            $certInfo['valid_from'] = date('Y-m-d H:i:s', $parsed['validFrom_time_t']);
            $certInfo['valid_to'] = date('Y-m-d H:i:s', $parsed['validTo_time_t']);
            $certInfo['days_left'] = floor(($parsed['validTo_time_t'] - time()) / 86400);
            $certInfo['is_valid'] = $parsed['validTo_time_t'] > time();
        }
        
        $certs[] = $certInfo;
    }
    
    return $certs;
}


function getRotationSetting() {
    global $db;
    
    try {
        $stmt = $db->prepare("SELECT setValue FROM settings WHERE setId = 4");
        $result = $stmt->execute();
        $row = $result->fetchArray(SQLITE3_ASSOC);
        
        $days = $row ? (int)$row['setValue'] : 0;
        
        return [
            'enabled' => $days > 0,
            'days' => $days
        ];
    } catch (Exception $e) {
        return ['enabled' => false, 'days' => 0, 'error' => $e->getMessage()];
    }
}

function saveRotationSetting($enabled, $days) {
    global $db;
    
    try {
        $value = $enabled ? max(1, $days) : 0;
        
        $stmt = $db->prepare("INSERT OR REPLACE INTO settings (setId, setValue, setCom) VALUES (4, :value, 'API key rotation days (0=disabled)')");
        $stmt->bindValue(':value', (string)$value, SQLITE3_TEXT);
        $stmt->execute();
        
        return true;
    } catch (Exception $e) {
        return false;
    }
}

function getRotationHistory($limit = 50) {
    global $db;
    
    try {
        $result = $db->query("SELECT name FROM sqlite_master WHERE type='table' AND name='key_rotation_tasks'");
        if (!$result->fetchArray()) {
            return [];
        }
        
        $stmt = $db->prepare("SELECT * FROM key_rotation_tasks ORDER BY taskId DESC LIMIT :limit");
        $stmt->bindValue(':limit', $limit, SQLITE3_INTEGER);
        $result = $stmt->execute();
        
        $tasks = [];
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
            $tasks[] = $row;
        }
        
        return $tasks;
    } catch (Exception $e) {
        return [];
    }
}

$action = $_GET['action'] ?? $_POST['action'] ?? '';

switch ($action) {
    case 'get_status':
        echo json_encode([
            'success' => true,
            'running' => getApacheStatus()
        ]);
        break;
        
    case 'get_config':
        $config = getCurrentConfig();
        echo json_encode([
            'success' => true,
            'enabled' => $config['enabled'],
            'port' => $config['port'],
            'document_root' => $config['document_root'],
            'ssl_enabled' => $config['ssl_enabled'],
            'ssl_cert' => $config['ssl_cert'],
            'ssl_key' => $config['ssl_key'],
            'ssl_chain' => $config['ssl_chain'],
            'config_content' => $config['config_content'],
            'config_path' => $config['config_path'],
            'last_modified' => $config['last_modified'],
            'site_enabled' => $config['site_enabled']
        ]);
        break;
        
    case 'apply_config':
        $enabled = isset($_POST['enabled']) && $_POST['enabled'] == '1';
        $port = (int)($_POST['port'] ?? 1488);
        $documentRoot = $_POST['document_root'] ?? '/var/www/html/admin';
        $sslEnabled = isset($_POST['ssl_enabled']) && $_POST['ssl_enabled'] == '1';
        $sslCert = $_POST['ssl_cert'] ?? '';
        $sslKey = $_POST['ssl_key'] ?? '';
        $sslChain = $_POST['ssl_chain'] ?? '';
        
        if ($port < 1 || $port > 65535) {
            echo json_encode(['success' => false, 'error' => 'Invalid port number']);
            break;
        }
        
        if ($sslEnabled && (empty($sslCert) || empty($sslKey))) {
            echo json_encode(['success' => false, 'error' => 'SSL certificate and key are required when HTTPS is enabled']);
            break;
        }
        
        if ($sslEnabled && !empty($sslCert) && !file_exists($sslCert)) {
            echo json_encode(['success' => false, 'error' => 'SSL certificate file not found: ' . $sslCert]);
            break;
        }
        
        if ($sslEnabled && !empty($sslKey) && !file_exists($sslKey)) {
            echo json_encode(['success' => false, 'error' => 'SSL key file not found: ' . $sslKey]);
            break;
        }
        
		
		if (!empty($sslChain) && !file_exists($sslChain)) {
			$sslChain = '';
		}
		
        if ($sslEnabled) {
            ensureSSLModuleEnabled();
        }
        
        $config = generateApacheConfig($port, $documentRoot, $sslEnabled, $sslCert, $sslKey, $sslChain);
        
        if (!saveApacheConfig($config)) {
            echo json_encode(['success' => false, 'error' => 'Failed to save configuration file']);
            break;
        }
        
        if ($enabled) {
            if (!enableSite()) {
                echo json_encode(['success' => false, 'error' => 'Failed to enable site']);
                break;
            }
        } else {
            if (!disableSite()) {
                echo json_encode(['success' => false, 'error' => 'Failed to disable site']);
                break;
            }
        }
        
        $testResult = testApacheConfig();
        if (!$testResult['success']) {
            echo json_encode([
                'success' => false,
                'error' => 'Apache configuration test failed',
                'details' => $testResult['output']
            ]);
            break;
        }
        
        reloadApache();
        
        echo json_encode([
            'success' => true,
            'message' => $enabled ? 'Configuration applied and site enabled' : 'Site disabled successfully',
            'config_test' => $testResult['output']
        ]);
        break;
        
    case 'service_action':
        $actionType = $_POST['action'] ?? '';
        
        if ($actionType === 'restart') {
            restartApache();
            echo json_encode(['success' => true, 'message' => 'Apache restarted successfully']);
        } elseif ($actionType === 'reload') {
            reloadApache();
            echo json_encode(['success' => true, 'message' => 'Apache reloaded successfully']);
        } else {
            echo json_encode(['success' => false, 'error' => 'Invalid action']);
        }
        break;
        
    case 'restore_default':
        $defaultPort = 1488;
        $defaultDocRoot = '/var/www/html/admin';
        $config = generateApacheConfig($defaultPort, $defaultDocRoot, false, '', '', '');
        
        if (saveApacheConfig($config)) {
            enableSite();
            $testResult = testApacheConfig();
            if ($testResult['success']) {
                reloadApache();
                echo json_encode(['success' => true, 'message' => 'Default configuration restored and site enabled']);
            } else {
                echo json_encode(['success' => false, 'error' => 'Default config test failed: ' . $testResult['output']]);
            }
        } else {
            echo json_encode(['success' => false, 'error' => 'Failed to restore default configuration']);
        }
        break;
        
    case 'list_certificates':
        $certs = getCertificatesList();
        echo json_encode(['success' => true, 'certificates' => $certs]);
        break;
    
	case 'get_rotation_settings':
		$settings = getRotationSetting();
		echo json_encode([
			'success' => true,
			'enabled' => $settings['enabled'],
			'days' => $settings['days']
		]);
		break;
		
	case 'save_rotation_settings':
		$enabled = isset($_POST['enabled']) && ($_POST['enabled'] == '1' || $_POST['enabled'] === true);
		$days = (int)($_POST['days'] ?? 30);
		
		if ($enabled && ($days < 1 || $days > 365)) {
			echo json_encode(['success' => false, 'error' => 'Days must be between 1 and 365']);
			break;
		}
		
		if (saveRotationSetting($enabled, $days)) {
			echo json_encode(['success' => true, 'message' => 'Rotation settings saved']);
		} else {
			echo json_encode(['success' => false, 'error' => 'Failed to save settings']);
		}
		break;
		
	case 'get_rotation_history':
		$limit = (int)($_GET['limit'] ?? 50);
		$history = getRotationHistory($limit);
		echo json_encode(['success' => true, 'history' => $history]);
		break;
		
	case 'process_rotation':
		$settings = getRotationSetting();
		
		if (!$settings['enabled'] || $settings['days'] <= 0) {
			echo json_encode(['success' => false, 'message' => 'Rotation is disabled (setId=4 is 0)']);
			break;
		}
		
		$rotationDays = $settings['days'];
		$cutoffDate = date('Y-m-d H:i:s', strtotime("-$rotationDays days"));
		
		$stmt = $db->prepare("
			SELECT idHost, hostName, hostApiKey, dateUpdateKey 
			FROM hosts 
			WHERE hostApiKey IS NOT NULL 
			AND hostApiKey != '' 
			AND (dateUpdateKey IS NULL OR dateUpdateKey < :cutoff)
			AND idHost != 1
		");
		$stmt->bindValue(':cutoff', $cutoffDate, SQLITE3_TEXT);
		$result = $stmt->execute();
		
		$tasksCreated = 0;
		while ($host = $result->fetchArray(SQLITE3_ASSOC)) {
			$checkStmt = $db->prepare("
				SELECT taskId FROM key_rotation_tasks 
				WHERE targetSn = :sn AND status IN ('pending', 'in_progress', 'awaiting_confirmation')
			");
			$checkStmt->bindValue(':sn', $host['hostName'], SQLITE3_TEXT);
			$checkResult = $checkStmt->execute();
			
			if (!$checkResult->fetchArray()) {
				$newKey = bin2hex(random_bytes(32));
				$insertStmt = $db->prepare("
					INSERT INTO key_rotation_tasks (
						targetSn, oldApiKey, newApiKey, status, maxAttempts, dateCreate
					) VALUES (
						:sn, :oldKey, :newKey, 'pending', 3, datetime('now')
					)
				");
				$insertStmt->bindValue(':sn', $host['hostName'], SQLITE3_TEXT);
				$insertStmt->bindValue(':oldKey', $host['hostApiKey'], SQLITE3_TEXT);
				$insertStmt->bindValue(':newKey', $newKey, SQLITE3_TEXT);
				$insertStmt->execute();
				$tasksCreated++;
			}
		}
		
		echo json_encode([
			'success' => true,
			'message' => 'Rotation processed',
			'tasks_created' => $tasksCreated,
			'cutoff_date' => $cutoffDate,
			'rotation_days' => $rotationDays,
			'setting_value' => $rotationDays
		]);
		break;
    
    default:
        echo json_encode(['success' => false, 'error' => 'Unknown action']);
}