<?php
/**
 * Klasa Device - zarządzanie urządzeniami
 */

class Device
{
    private Database $db;
    private Logger $logger;

    public function __construct()
    {
        $this->db = Database::getInstance();
        $this->logger = new Logger();
    }

    /**
     * Pobierz urządzenia użytkownika
     */
    public function getByUserId(int $userId, int $limit = ITEMS_PER_PAGE, int $offset = 0): array
    {
        return $this->db->fetchAll(
            "SELECT d.*, 
                    (SELECT COUNT(*) FROM playlists p WHERE p.device_id = d.id) as playlists_count,
                    (SELECT COUNT(*) FROM entity_notes en WHERE en.entity_type = 'device' AND en.entity_id = d.id) as notes_count,
                    u.username
             FROM devices d 
             JOIN users u ON d.user_id = u.id
             WHERE d.user_id = :user_id 
             ORDER BY d.created_at DESC 
             LIMIT :limit OFFSET :offset",
            ['user_id' => $userId, 'limit' => $limit, 'offset' => $offset]
        );
    }

    /**
     * Pobierz wszystkie urządzenia (dla admina)
     */
    public function getAll(int $limit = ITEMS_PER_PAGE, int $offset = 0, ?string $search = null, ?int $userId = null): array
    {
        $sql = "SELECT d.*, 
                       (SELECT COUNT(*) FROM playlists p WHERE p.device_id = d.id) as playlists_count,
                       (SELECT COUNT(*) FROM entity_notes en WHERE en.entity_type = 'device' AND en.entity_id = d.id) as notes_count,
                       u.username
                FROM devices d 
                JOIN users u ON d.user_id = u.id";
        
        $params = [];
        $where = [];
        
        if ($userId !== null) {
            $where[] = "d.user_id = :user_id";
            $params['user_id'] = $userId;
        }
        
        if ($search) {
            $where[] = "(d.mac LIKE :search OR d.code LIKE :search2 OR d.name LIKE :search3 OR u.username LIKE :search4)";
            $params['search'] = "%{$search}%";
            $params['search2'] = "%{$search}%";
            $params['search3'] = "%{$search}%";
            $params['search4'] = "%{$search}%";
        }
        
        if (!empty($where)) {
            $sql .= " WHERE " . implode(' AND ', $where);
        }
        
        $sql .= " ORDER BY d.created_at DESC LIMIT :limit OFFSET :offset";
        $params['limit'] = $limit;
        $params['offset'] = $offset;
        
        return $this->db->fetchAll($sql, $params);
    }

    /**
     * Pobierz urządzenie po ID
     */
    public function getById(int $id): ?array
    {
        return $this->db->fetchOne(
            "SELECT d.*, u.username, u.id as owner_id,
                    (SELECT COUNT(*) FROM playlists WHERE device_id = d.id) as playlists_count
             FROM devices d 
             JOIN users u ON d.user_id = u.id
             WHERE d.id = :id",
            ['id' => $id]
        );
    }

    /**
     * Pobierz urządzenie po MAC i CODE
     */
    public function getByMacCode(string $mac, string $code): ?array
    {
        return $this->db->fetchOne(
            "SELECT d.*, u.is_active as user_active
             FROM devices d 
             JOIN users u ON d.user_id = u.id
             WHERE d.mac = :mac AND d.code = :code",
            ['mac' => strtoupper($mac), 'code' => $code]
        );
    }

    /**
     * Utwórz nowe urządzenie
     */
    public function create(array $data): array
    {
        // Walidacja MAC
        $mac = $this->formatMac($data['mac'] ?? '');
        if (!$this->validateMac($mac)) {
            return ['success' => false, 'error' => 'Nieprawidłowy format adresu MAC.'];
        }

        // Walidacja CODE - zawsze duże litery
        $code = strtoupper(trim($data['code'] ?? ''));
        if (strlen($code) < 3) {
            return ['success' => false, 'error' => 'Kod musi mieć minimum 3 znaki.'];
        }

        // Sprawdź limit urządzeń
        $user = $this->db->fetchOne(
            "SELECT id, role, device_limit, max_devices FROM users WHERE id = :id",
            ['id' => $data['user_id']]
        );

        if (!$user) {
            return ['success' => false, 'error' => 'Użytkownik nie istnieje.'];
        }

        // Admin nie ma limitów
        if ($user['role'] !== 'admin') {
            $currentCount = $this->countByUserId($data['user_id']);
            
            // Dla resellera - dynamiczny limit (max_devices - przydzielone userom)
            if ($user['role'] === 'reseller') {
                // Zlicz ile urządzeń przydzielił swoim userom
                $allocatedToUsers = $this->db->fetchOne(
                    "SELECT COALESCE(SUM(device_limit), 0) as total FROM users WHERE parent_id = :id",
                    ['id' => $data['user_id']]
                );
                $availableForReseller = $user['max_devices'] - (int)$allocatedToUsers['total'];
                
                if ($currentCount >= $availableForReseller) {
                    return ['success' => false, 'error' => "Osiągnięto limit urządzeń. Dostępne: {$availableForReseller} (pula: {$user['max_devices']}, przydzielone userom: {$allocatedToUsers['total']})."];
                }
            } else {
                // Dla zwykłego usera - standardowy limit
                if ($currentCount >= $user['device_limit']) {
                    return ['success' => false, 'error' => 'Osiągnięto limit urządzeń (' . $user['device_limit'] . ').'];
                }
            }
        }

        // Sprawdź unikalność MAC+CODE
        if ($this->getByMacCode($mac, $code)) {
            return ['success' => false, 'error' => 'Urządzenie z tym MAC i kodem już istnieje.'];
        }

        $id = $this->db->insert(
            "INSERT INTO devices (user_id, mac, code, name, is_blocked) 
             VALUES (:user_id, :mac, :code, :name, 0)",
            [
                'user_id' => $data['user_id'],
                'mac' => $mac,
                'code' => $code,
                'name' => trim($data['name'] ?? '')
            ]
        );

        $this->logger->log('device_created', "Dodano urządzenie: {$mac} (ID: {$id})", $data['logged_user_id'] ?? $data['user_id']);

        return ['success' => true, 'id' => $id];
    }

    /**
     * Aktualizuj urządzenie
     */
    public function update(int $id, array $data, ?int $loggedUserId = null): array
    {
        $device = $this->getById($id);
        
        if (!$device) {
            return ['success' => false, 'error' => 'Urządzenie nie istnieje.'];
        }

        $updates = [];
        $params = ['id' => $id];

        if (isset($data['mac'])) {
            $mac = $this->formatMac($data['mac']);
            if (!$this->validateMac($mac)) {
                return ['success' => false, 'error' => 'Nieprawidłowy format adresu MAC.'];
            }
            $updates[] = "mac = :mac";
            $params['mac'] = $mac;
        }

        if (isset($data['code'])) {
            $code = strtoupper(trim($data['code']));
            if (strlen($code) < 3) {
                return ['success' => false, 'error' => 'Kod musi mieć minimum 3 znaki.'];
            }
            $updates[] = "code = :code";
            $params['code'] = $code;
        }

        if (isset($data['name'])) {
            $updates[] = "name = :name";
            $params['name'] = trim($data['name']);
        }

        if (isset($data['is_blocked'])) {
            $updates[] = "is_blocked = :is_blocked";
            $params['is_blocked'] = $data['is_blocked'] ? 1 : 0;
        }

        // Sprawdź unikalność nowego MAC+CODE
        $newMac = $params['mac'] ?? $device['mac'];
        $newCode = $params['code'] ?? $device['code'];
        
        $existing = $this->db->fetchOne(
            "SELECT id FROM devices WHERE mac = :mac AND code = :code AND id != :id",
            ['mac' => $newMac, 'code' => $newCode, 'id' => $id]
        );
        
        if ($existing) {
            return ['success' => false, 'error' => 'Urządzenie z tym MAC i kodem już istnieje.'];
        }

        if (empty($updates)) {
            return ['success' => false, 'error' => 'Brak danych do aktualizacji.'];
        }

        $updates[] = "updated_at = datetime('now')";

        $this->db->execute(
            "UPDATE devices SET " . implode(', ', $updates) . " WHERE id = :id",
            $params
        );

        $this->logger->log('device_updated', "Zaktualizowano urządzenie ID: {$id}", $loggedUserId);

        return ['success' => true];
    }

    /**
     * Usuń urządzenie
     */
    public function delete(int $id, ?int $loggedUserId = null): array
    {
        $device = $this->getById($id);
        
        if (!$device) {
            return ['success' => false, 'error' => 'Urządzenie nie istnieje.'];
        }

        $this->db->execute("DELETE FROM devices WHERE id = :id", ['id' => $id]);

        $this->logger->log('device_deleted', "Usunięto urządzenie: {$device['mac']} (ID: {$id})", $loggedUserId);

        return ['success' => true];
    }

    /**
     * Przełącz blokadę urządzenia
     */
    public function toggleBlock(int $id, ?int $loggedUserId = null): array
    {
        $device = $this->getById($id);
        
        if (!$device) {
            return ['success' => false, 'error' => 'Urządzenie nie istnieje.'];
        }

        $newStatus = $device['is_blocked'] ? 0 : 1;
        
        $this->db->execute(
            "UPDATE devices SET is_blocked = :status, updated_at = datetime('now') WHERE id = :id",
            ['status' => $newStatus, 'id' => $id]
        );

        $action = $newStatus ? 'zablokowano' : 'odblokowano';
        $this->logger->log('device_block_toggled', "Urządzenie {$device['mac']} {$action}", $loggedUserId);

        return ['success' => true, 'is_blocked' => $newStatus];
    }

    /**
     * Zlicz urządzenia użytkownika
     */
    public function countByUserId(int $userId): int
    {
        $result = $this->db->fetchOne(
            "SELECT COUNT(*) as count FROM devices WHERE user_id = :user_id",
            ['user_id' => $userId]
        );
        return $result['count'] ?? 0;
    }

    /**
     * Zlicz wszystkie urządzenia
     */
    public function count(?string $search = null, ?int $userId = null): int
    {
        $sql = "SELECT COUNT(*) as count FROM devices d JOIN users u ON d.user_id = u.id";
        $params = [];
        $where = [];
        
        if ($userId !== null) {
            $where[] = "d.user_id = :user_id";
            $params['user_id'] = $userId;
        }
        
        if ($search) {
            $where[] = "(d.mac LIKE :search OR d.code LIKE :search2 OR d.name LIKE :search3)";
            $params['search'] = "%{$search}%";
            $params['search2'] = "%{$search}%";
            $params['search3'] = "%{$search}%";
        }
        
        if (!empty($where)) {
            $sql .= " WHERE " . implode(' AND ', $where);
        }
        
        $result = $this->db->fetchOne($sql, $params);
        return $result['count'] ?? 0;
    }

    /**
     * Pobierz statystyki urządzeń
     */
    public function getStats(): array
    {
        $total = $this->db->fetchOne("SELECT COUNT(*) as count FROM devices");
        $blocked = $this->db->fetchOne("SELECT COUNT(*) as count FROM devices WHERE is_blocked = 1");
        $today = $this->db->fetchOne(
            "SELECT COUNT(*) as count FROM devices WHERE date(created_at) = date('now')"
        );
        
        return [
            'total' => $total['count'] ?? 0,
            'blocked' => $blocked['count'] ?? 0,
            'today' => $today['count'] ?? 0
        ];
    }

    /**
     * Formatuj adres MAC
     */
    private function formatMac(string $mac): string
    {
        // Usuń wszystkie separatory i zamień na wielkie litery
        $mac = strtoupper(preg_replace('/[^A-Fa-f0-9]/', '', $mac));
        
        // Dodaj dwukropki co 2 znaki
        if (strlen($mac) === 12) {
            return implode(':', str_split($mac, 2));
        }
        
        return $mac;
    }

    /**
     * Waliduj adres MAC
     */
    private function validateMac(string $mac): bool
    {
        return preg_match('/^([0-9A-F]{2}:){5}[0-9A-F]{2}$/', $mac) === 1;
    }

    /**
     * Pobierz urządzenia dla listy user_ids (dla resellera)
     */
    public function getByUserIds(array $userIds, int $limit = ITEMS_PER_PAGE, int $offset = 0, ?string $search = null): array
    {
        if (empty($userIds)) {
            return [];
        }

        $placeholders = implode(',', array_fill(0, count($userIds), '?'));
        $params = $userIds;
        
        $sql = "SELECT d.*, u.username,
                       (SELECT COUNT(*) FROM playlists p WHERE p.device_id = d.id) as playlists_count
                FROM devices d
                JOIN users u ON d.user_id = u.id
                WHERE d.user_id IN ({$placeholders})";
        
        if ($search) {
            $sql .= " AND (d.name LIKE ? OR d.mac LIKE ?)";
            $params[] = "%{$search}%";
            $params[] = "%{$search}%";
        }
        
        $sql .= " ORDER BY d.created_at DESC LIMIT ? OFFSET ?";
        $params[] = $limit;
        $params[] = $offset;
        
        return $this->db->fetchAll($sql, $params);
    }

    /**
     * Zlicz urządzenia dla listy user_ids (dla resellera)
     */
    public function countByUserIds(array $userIds, ?string $search = null): int
    {
        if (empty($userIds)) {
            return 0;
        }

        $placeholders = implode(',', array_fill(0, count($userIds), '?'));
        $params = $userIds;
        
        $sql = "SELECT COUNT(*) as count FROM devices WHERE user_id IN ({$placeholders})";
        
        if ($search) {
            $sql .= " AND (name LIKE ? OR mac LIKE ?)";
            $params[] = "%{$search}%";
            $params[] = "%{$search}%";
        }
        
        $result = $this->db->fetchOne($sql, $params);
        return $result['count'] ?? 0;
    }
}
