<?php

namespace WHPBackup;

require_once('/docker/whp/web/libs/mysqlmgmt.php');

use PDO;
use Exception;

class BackupTarget {
    private $db;
    private $id;
    private $data = [];
    
    public function __construct(PDO $db = null, $id = null) {
        if ($db === null) {
            $mysql = new \mysqlmgmt();
            $this->db = $mysql->getMySQLConnection();
            // Select the whp database
            $this->db->exec("USE whp");
        } else {
            $this->db = $db;
        }
        
        if ($id) {
            $this->load($id);
        }
    }
    
    public function load($id) {
        $stmt = $this->db->prepare("SELECT * FROM backup_targets WHERE id = ?");
        $stmt->execute([$id]);
        $this->data = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($this->data) {
            $this->id = $id;
            return true;
        }
        return false;
    }
    
    public function create($data) {
        $type = $data['type'] ?? 's3';

        // Validate target type
        $allowedTypes = ['s3', 'ftp', 'sftp'];
        if (!in_array($type, $allowedTypes, true)) {
            throw new Exception("Invalid target type: $type");
        }

        // Validate required fields based on type
        $required = ['name', 'type'];
        if ($type === 's3') {
            $required = array_merge($required, ['endpoint', 'bucket', 'access_key', 'secret_key']);
        } elseif ($type === 'ftp' || $type === 'sftp') {
            $required = array_merge($required, ['host', 'username', 'password']);
        }

        foreach ($required as $field) {
            if (!isset($data[$field]) || $data[$field] === '') {
                throw new Exception("Missing required field: $field");
            }
        }

        // Encrypt sensitive data
        if (!empty($data['secret_key'])) {
            $data['secret_key'] = $this->encryptData($data['secret_key']);
        }
        if (!empty($data['password'])) {
            $data['password'] = $this->encryptData($data['password']);
        }

        $fields = ['name', 'type', 'host', 'port', 'username', 'password', 'remote_path',
                   'endpoint', 'bucket', 'access_key', 'secret_key',
                   'region', 'path_prefix', 'retention_days', 'max_backups', 'is_global', 'owner'];
        $placeholders = array_fill(0, count($fields), '?');

        $sql = "INSERT INTO backup_targets (" . implode(', ', $fields) . ") VALUES (" . implode(', ', $placeholders) . ")";
        $stmt = $this->db->prepare($sql);

        $values = [];
        foreach ($fields as $field) {
            $values[] = $data[$field] ?? null;
        }

        if ($stmt->execute($values)) {
            $this->id = $this->db->lastInsertId();
            $this->load($this->id);
            return true;
        }
        return false;
    }
    
    public function update($data) {
        if (!$this->id) {
            throw new Exception("Cannot update non-existent target");
        }

        // Validate target type if provided
        if (isset($data['type'])) {
            $allowedTypes = ['s3', 'ftp', 'sftp'];
            if (!in_array($data['type'], $allowedTypes, true)) {
                throw new Exception("Invalid target type: " . $data['type']);
            }
        }

        // Encrypt secret key if provided
        if (isset($data['secret_key']) && !empty($data['secret_key'])) {
            $data['secret_key'] = $this->encryptData($data['secret_key']);
        }
        // Encrypt password if provided
        if (isset($data['password']) && !empty($data['password'])) {
            $data['password'] = $this->encryptData($data['password']);
        }

        $fields = [];
        $values = [];

        $allowed = ['name', 'type', 'host', 'port', 'username', 'password', 'remote_path',
                    'endpoint', 'bucket', 'access_key', 'secret_key',
                    'region', 'path_prefix', 'retention_days', 'max_backups', 'is_global'];
        
        foreach ($allowed as $field) {
            if (isset($data[$field])) {
                $fields[] = "$field = ?";
                $values[] = $data[$field];
            }
        }
        
        if (empty($fields)) {
            return true;
        }
        
        $values[] = $this->id;
        $sql = "UPDATE backup_targets SET " . implode(', ', $fields) . " WHERE id = ?";
        $stmt = $this->db->prepare($sql);
        
        if ($stmt->execute($values)) {
            $this->load($this->id);
            return true;
        }
        return false;
    }
    
    public function delete() {
        if (!$this->id) {
            throw new Exception("Cannot delete non-existent target");
        }
        
        $stmt = $this->db->prepare("DELETE FROM backup_targets WHERE id = ?");
        return $stmt->execute([$this->id]);
    }
    
    public function canAccess($username) {
        if (!$this->id) {
            return false;
        }
        
        // Root can access all
        if ($username === 'root') {
            return true;
        }
        
        // Global targets can be accessed by all
        if ($this->data['is_global']) {
            return true;
        }
        
        // Owner can access their own targets
        return $this->data['owner'] === $username;
    }
    
    public function canManage($username) {
        if (!$this->id) {
            return false;
        }
        
        // Root can manage all
        if ($username === 'root') {
            return true;
        }
        
        // Only owner can manage non-global targets
        if (!$this->data['is_global']) {
            return $this->data['owner'] === $username;
        }
        
        return false;
    }
    
    public function getCredentials() {
        if (!$this->id) {
            throw new Exception("No target loaded");
        }

        $type = $this->data['type'] ?? 's3';
        $creds = [
            'type' => $type,
            'path_prefix' => $this->data['path_prefix'] ?? ''
        ];

        if ($type === 's3') {
            $creds['endpoint'] = $this->data['endpoint'];
            $creds['bucket'] = $this->data['bucket'];
            $creds['access_key'] = $this->data['access_key'];
            $creds['secret_key'] = $this->decryptData($this->data['secret_key']);
            $creds['region'] = $this->data['region'] ?? 'us-east-1';
        } elseif ($type === 'ftp' || $type === 'sftp') {
            $creds['host'] = $this->data['host'];
            $creds['port'] = $this->data['port'] ?? ($type === 'ftp' ? 21 : 22);
            $creds['username'] = $this->data['username'];
            $creds['password'] = $this->decryptData($this->data['password']);
            $creds['remote_path'] = $this->data['remote_path'] ?? '/';
        }

        return $creds;
    }
    
    public function getData() {
        $data = $this->data;
        // Don't expose sensitive fields
        if (isset($data['secret_key'])) {
            $data['secret_key'] = '***';
        }
        if (isset($data['password'])) {
            $data['password'] = '***';
        }
        return $data;
    }

    public function getDataForEdit() {
        $data = $this->data;
        // Decrypt sensitive fields for editing
        if (!empty($data['secret_key'])) {
            $data['secret_key'] = $this->decryptData($data['secret_key']);
        }
        if (!empty($data['password'])) {
            $data['password'] = $this->decryptData($data['password']);
        }
        return $data;
    }
    
    public function getId() {
        return $this->id;
    }
    
    private function encryptData($data) {
        // Simple encryption using base64 and server key
        // In production, use proper encryption
        $key = $this->getEncryptionKey();
        return base64_encode(openssl_encrypt($data, 'AES-256-CBC', $key, 0, substr($key, 0, 16)));
    }
    
    private function decryptData($data) {
        $key = $this->getEncryptionKey();
        return openssl_decrypt(base64_decode($data), 'AES-256-CBC', $key, 0, substr($key, 0, 16));
    }
    
    private function getEncryptionKey() {
        // Use a consistent key that works in both web and CLI contexts
        // Using NONCE_SALT from config.php ensures consistency
        return hash('sha256', 'WHPBackup2025' . NONCE_SALT . 'SecretSalt');
    }
    
    public static function listTargets(PDO $db = null, $username = null, $includeGlobal = true) {
        if ($db === null) {
            $mysql = new \mysqlmgmt();
            $db = $mysql->getMySQLConnection();
            $db->exec("USE whp");
        }
        $sql = "SELECT * FROM backup_targets WHERE 1=1";
        $params = [];
        
        if ($username && $username !== 'root') {
            if ($includeGlobal) {
                $sql .= " AND (owner = ? OR is_global = 1)";
            } else {
                $sql .= " AND owner = ?";
            }
            $params[] = $username;
        }
        
        $sql .= " ORDER BY is_global DESC, name ASC";
        
        $stmt = $db->prepare($sql);
        $stmt->execute($params);
        
        $targets = [];
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
            // Don't expose sensitive fields
            $row['secret_key'] = '***';
            if (isset($row['password'])) {
                $row['password'] = '***';
            }
            $targets[] = $row;
        }
        
        return $targets;
    }
    
    public static function getDefault(PDO $db = null, $username) {
        if ($db === null) {
            $mysql = new \mysqlmgmt();
            $db = $mysql->getMySQLConnection();
            $db->exec("USE whp");
        }
        // First try to get a global target
        $stmt = $db->prepare("SELECT id FROM backup_targets WHERE is_global = 1 LIMIT 1");
        $stmt->execute();
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($result) {
            return new self($db, $result['id']);
        }
        
        // Then try user's own target
        $stmt = $db->prepare("SELECT id FROM backup_targets WHERE owner = ? LIMIT 1");
        $stmt->execute([$username]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($result) {
            return new self($db, $result['id']);
        }
        
        return null;
    }
}