<?php

namespace WHPBackup;

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

use PDO;
use Exception;

class BackupHistory {
    private $db;
    private $id;
    private $data = [];
    
    public function __construct(PDO $db = null, $id = null) {
        if ($db === null) {
            $mysql = new \mysqlmgmt();
            $this->db = $mysql->getMySQLConnection();
            $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_history 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) {
        $required = ['target_id', 'user', 'backup_type', 'backup_name', 'backup_path'];
        foreach ($required as $field) {
            if (!isset($data[$field]) || empty($data[$field])) {
                throw new Exception("Missing required field: $field");
            }
        }

        // Set default status if not provided
        if (!isset($data['status'])) {
            $data['status'] = 'pending';
        }

        $fields = ['target_id', 'user', 'backup_type', 'backup_name', 'backup_path',
                   'backup_size', 'status', 'metadata'];
        $placeholders = array_fill(0, count($fields), '?');

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

        $values = [];
        foreach ($fields as $field) {
            if ($field === 'metadata' && isset($data[$field])) {
                $values[] = json_encode($data[$field]);
            } else {
                $values[] = $data[$field] ?? null;
            }
        }

        if ($stmt->execute($values)) {
            $this->id = $this->db->lastInsertId();
            $this->load($this->id);
            return true;
        }

        return false;
    }
    
    public function updateStatus($status, $error = null) {
        if (!$this->id) {
            throw new Exception("Cannot update non-existent backup");
        }

        $sql = "UPDATE backup_history SET status = ?, error_message = ?";
        $values = [$status, $error];

        if ($status === 'completed' || $status === 'failed') {
            $sql .= ", completed_at = NOW()";
        }

        $sql .= " WHERE id = ?";
        $values[] = $this->id;

        $stmt = $this->db->prepare($sql);
        if ($stmt->execute($values)) {
            $this->load($this->id);
            // Fire per-site notification on terminal states. The lookup
            // accepts that some backups won't match a wordpress_sites row
            // (userfiles/database backups, sites not under WP management).
            // Never throw out of a notification — it must not break backups.
            if ($status === 'completed' || $status === 'failed') {
                $this->maybeNotifyWordPressSite($status, $error);
            }
            return true;
        }
        return false;
    }

    /**
     * Look up the wordpress_sites row matching this backup (by user + domain
     * from the metadata blob) and fire a notification email if the per-event
     * flag is on. No-op for non-WP backups.
     */
    private function maybeNotifyWordPressSite($status, $error) {
        try {
            $user = $this->data['user'] ?? null;
            if (!$user) return;

            // Only WP-relevant backup types. 'site' covers WP sites that were
            // imported as plain sites, 'wordpress' is the explicit WP type.
            $type = $this->data['backup_type'] ?? '';
            if (!in_array($type, ['site', 'wordpress'], true)) return;

            $metadata = isset($this->data['metadata']) ? json_decode($this->data['metadata'], true) : [];
            $domain = $metadata['domain'] ?? null;
            if (!$domain) return;

            $stmt = $this->db->prepare(
                "SELECT id FROM wordpress_sites WHERE user = ? AND domain = ? LIMIT 1"
            );
            $stmt->execute([$user, $domain]);
            $row = $stmt->fetch(PDO::FETCH_ASSOC);
            if (!$row) return;

            require_once(__DIR__ . '/WordPressManager.php');
            $manager = new \WHP\WordPressManager();
            $event = $status === 'completed' ? 'backup_complete' : 'backup_failed';
            $context = [
                'backup_name' => $this->data['backup_name'] ?? '',
            ];
            if ($status === 'failed' && $error) {
                $context['error'] = $error;
            }
            $manager->sendSiteNotification((int)$row['id'], $event, $context);
        } catch (Exception $e) {
            error_log("BackupHistory::maybeNotifyWordPressSite: " . $e->getMessage());
        }
    }
    
    public function resetForRetry() {
        if (!$this->id) {
            throw new Exception("Cannot reset non-existent backup");
        }

        if ($this->data['status'] !== 'failed') {
            throw new Exception("Only failed backups can be retried");
        }

        $stmt = $this->db->prepare("
            UPDATE backup_history
            SET status = 'pending',
                creation_status = 'pending',
                upload_status = NULL,
                error_message = NULL,
                completed_at = NULL,
                local_path = NULL,
                local_size = NULL,
                backup_size = 0,
                upload_attempts = 0
            WHERE id = ? AND status = 'failed'
        ");
        $result = $stmt->execute([$this->id]);

        if ($result && $stmt->rowCount() > 0) {
            $this->load($this->id);
            return true;
        }
        return false;
    }

    public function updateSize($size) {
        if (!$this->id) {
            throw new Exception("Cannot update non-existent backup");
        }
        
        $stmt = $this->db->prepare("UPDATE backup_history SET backup_size = ? WHERE id = ?");
        return $stmt->execute([$size, $this->id]);
    }
    
    public function delete() {
        if (!$this->id) {
            throw new Exception("Cannot delete non-existent backup");
        }
        
        // Mark as deleted instead of actually deleting
        return $this->updateStatus('deleted');
    }
    
    public function canAccess($username) {
        if (!$this->id) {
            return false;
        }
        
        // Root can access all
        if ($username === 'root') {
            return true;
        }
        
        // User can access their own backups
        return $this->data['user'] === $username;
    }
    
    public function getData() {
        $data = $this->data;
        if (isset($data['metadata']) && is_string($data['metadata'])) {
            $data['metadata'] = json_decode($data['metadata'], true);
        }
        return $data;
    }
    
    public function getId() {
        return $this->id;
    }
    
    public static function listBackups(PDO $db = null, $filters = []) {
        if ($db === null) {
            $mysql = new \mysqlmgmt();
            $db = $mysql->getMySQLConnection();
            $db->exec("USE whp");
        }
        
        $sql = "SELECT bh.*, bt.name as target_name, bt.type as target_type 
                FROM backup_history bh 
                JOIN backup_targets bt ON bh.target_id = bt.id 
                WHERE 1=1";
        $params = [];
        
        if (isset($filters['user'])) {
            $sql .= " AND bh.user = ?";
            $params[] = $filters['user'];
        }
        
        if (isset($filters['target_id'])) {
            $sql .= " AND bh.target_id = ?";
            $params[] = $filters['target_id'];
        }
        
        if (isset($filters['backup_type'])) {
            $sql .= " AND bh.backup_type = ?";
            $params[] = $filters['backup_type'];
        }
        
        if (isset($filters['status'])) {
            $sql .= " AND bh.status = ?";
            $params[] = $filters['status'];
        }
        
        if (isset($filters['exclude_deleted']) && $filters['exclude_deleted']) {
            $sql .= " AND bh.status != 'deleted'";
        }
        
        // Search filter
        if (isset($filters['search']) && !empty($filters['search'])) {
            $sql .= " AND bh.backup_name LIKE ?";
            $params[] = '%' . $filters['search'] . '%';
        }
        
        // Date range filters
        if (isset($filters['start_date'])) {
            $sql .= " AND bh.started_at >= ?";
            $params[] = $filters['start_date'];
        }
        
        if (isset($filters['end_date'])) {
            $sql .= " AND bh.started_at <= ?";
            $params[] = $filters['end_date'];
        }
        
        // Sorting — allowlist column names and direction to prevent SQL
        // injection via $filters['order_by']/$filters['order_dir']. These
        // identifiers cannot be parameterized via PDO bindings, so they
        // must be validated against a fixed list before interpolation.
        $allowedOrder = ['started_at', 'completed_at', 'backup_name', 'backup_type', 'status', 'backup_size', 'user', 'target_id', 'id'];
        $orderBy = isset($filters['order_by']) ? $filters['order_by'] : 'started_at';
        $orderBy = in_array($orderBy, $allowedOrder, true) ? $orderBy : 'started_at';
        $orderDir = isset($filters['order_dir']) && strtoupper($filters['order_dir']) === 'ASC' ? 'ASC' : 'DESC';
        $sql .= " ORDER BY bh.{$orderBy} {$orderDir}";
        
        // Pagination
        if (isset($filters['limit'])) {
            $sql .= " LIMIT " . intval($filters['limit']);
            if (isset($filters['offset'])) {
                $sql .= " OFFSET " . intval($filters['offset']);
            }
        }
        
        $stmt = $db->prepare($sql);
        $stmt->execute($params);
        
        $backups = [];
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
            if (isset($row['metadata']) && is_string($row['metadata'])) {
                $row['metadata'] = json_decode($row['metadata'], true);
            }
            $backups[] = $row;
        }
        
        return $backups;
    }
    
    public static function countBackups(PDO $db = null, $filters = []) {
        if ($db === null) {
            $mysql = new \mysqlmgmt();
            $db = $mysql->getMySQLConnection();
            $db->exec("USE whp");
        }
        
        $sql = "SELECT COUNT(*) as count FROM backup_history bh WHERE 1=1";
        $params = [];
        
        if (isset($filters['user'])) {
            $sql .= " AND bh.user = ?";
            $params[] = $filters['user'];
        }
        
        if (isset($filters['target_id'])) {
            $sql .= " AND bh.target_id = ?";
            $params[] = $filters['target_id'];
        }
        
        if (isset($filters['exclude_deleted']) && $filters['exclude_deleted']) {
            $sql .= " AND bh.status != 'deleted'";
        }
        
        if (isset($filters['status'])) {
            $sql .= " AND bh.status = ?";
            $params[] = $filters['status'];
        }
        
        if (isset($filters['backup_type'])) {
            $sql .= " AND bh.backup_type = ?";
            $params[] = $filters['backup_type'];
        }
        
        // Search filter
        if (isset($filters['search']) && !empty($filters['search'])) {
            $sql .= " AND bh.backup_name LIKE ?";
            $params[] = '%' . $filters['search'] . '%';
        }
        
        $stmt = $db->prepare($sql);
        $stmt->execute($params);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        return $result['count'] ?? 0;
    }
    
    public static function getTotalSize(PDO $db = null, $filters = []) {
        if ($db === null) {
            $mysql = new \mysqlmgmt();
            $db = $mysql->getMySQLConnection();
            $db->exec("USE whp");
        }
        
        $sql = "SELECT SUM(backup_size) as total_size FROM backup_history bh WHERE status = 'completed'";
        $params = [];
        
        if (isset($filters['user'])) {
            $sql .= " AND bh.user = ?";
            $params[] = $filters['user'];
        }
        
        if (isset($filters['target_id'])) {
            $sql .= " AND bh.target_id = ?";
            $params[] = $filters['target_id'];
        }
        
        $stmt = $db->prepare($sql);
        $stmt->execute($params);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        return $result['total_size'] ?? 0;
    }
    
    public static function getOldBackups(PDO $db = null, $targetId, $retentionDays) {
        if ($db === null) {
            $mysql = new \mysqlmgmt();
            $db = $mysql->getMySQLConnection();
            $db->exec("USE whp");
        }
        
        $sql = "SELECT * FROM backup_history 
                WHERE target_id = ? 
                AND status = 'completed' 
                AND started_at < DATE_SUB(NOW(), INTERVAL ? DAY)
                ORDER BY started_at ASC";
        
        $stmt = $db->prepare($sql);
        $stmt->execute([$targetId, $retentionDays]);
        
        $backups = [];
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
            $backups[] = $row;
        }
        
        return $backups;
    }
    
    public static function getExcessBackups(PDO $db = null, $targetId, $user, $backupType, $maxBackups) {
        if ($db === null) {
            $mysql = new \mysqlmgmt();
            $db = $mysql->getMySQLConnection();
            $db->exec("USE whp");
        }
        
        $sql = "SELECT * FROM backup_history 
                WHERE target_id = ? 
                AND user = ? 
                AND backup_type = ?
                AND status = 'completed'
                ORDER BY started_at DESC
                LIMIT 18446744073709551615 OFFSET ?";
        
        $stmt = $db->prepare($sql);
        $stmt->execute([$targetId, $user, $backupType, $maxBackups]);
        
        $backups = [];
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
            $backups[] = $row;
        }
        
        return $backups;
    }
}