<?php
/**
 * Klasa User - zarządzanie użytkownikami
 * Obsługuje role: admin, reseller, user
 */

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

    // Role użytkowników
    const ROLE_ADMIN = 'admin';
    const ROLE_RESELLER = 'reseller';
    const ROLE_USER = 'user';

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

    /**
     * Pobierz wszystkich użytkowników (z opcjonalnym filtrem parent_id)
     */
    public function getAll(int $limit = ITEMS_PER_PAGE, int $offset = 0, ?string $search = null, ?int $parentId = null, ?string $role = null): array
    {
        $sql = "SELECT u.*, 
                       (SELECT COUNT(*) FROM devices d WHERE d.user_id = u.id) as devices_count,
                       (SELECT COUNT(*) FROM devices d2 
                        JOIN playlists p ON p.device_id = d2.id 
                        WHERE d2.user_id = u.id) as playlists_count,
                       (SELECT COUNT(*) FROM entity_notes en WHERE en.entity_type = 'user' AND en.entity_id = u.id) as notes_count,
                       parent.username as parent_username,
                       -- Dla resellerów: ile przydzielili userom
                       (SELECT COALESCE(SUM(device_limit), 0) FROM users WHERE parent_id = u.id) as devices_allocated,
                       (SELECT COALESCE(SUM(playlist_limit), 0) FROM users WHERE parent_id = u.id) as playlists_allocated,
                       (SELECT COUNT(*) FROM users WHERE parent_id = u.id) as users_count
                FROM users u
                LEFT JOIN users parent ON u.parent_id = parent.id";
        
        $where = [];
        $params = [];
        
        if ($search) {
            $where[] = "u.username LIKE :search";
            $params['search'] = "%{$search}%";
        }
        
        if ($parentId !== null) {
            $where[] = "u.parent_id = :parent_id";
            $params['parent_id'] = $parentId;
        }
        
        if ($role !== null) {
            $where[] = "u.role = :role";
            $params['role'] = $role;
        }
        
        if (!empty($where)) {
            $sql .= " WHERE " . implode(' AND ', $where);
        }
        
        $sql .= " ORDER BY u.created_at DESC LIMIT :limit OFFSET :offset";
        $params['limit'] = $limit;
        $params['offset'] = $offset;
        
        return $this->db->fetchAll($sql, $params);
    }

    /**
     * Pobierz użytkowników należących do resellera (lub admina)
     */
    public function getByParentId(int $parentId, int $limit = ITEMS_PER_PAGE, int $offset = 0, ?string $search = null): array
    {
        return $this->getAll($limit, $offset, $search, $parentId);
    }

    /**
     * Pobierz wszystkich użytkowników dostępnych dla danego managera (admin/reseller)
     */
    public function getVisibleUsers(array $currentUser, int $limit = ITEMS_PER_PAGE, int $offset = 0, ?string $search = null): array
    {
        if ($currentUser['role'] === self::ROLE_ADMIN) {
            return $this->getAll($limit, $offset, $search);
        } elseif ($currentUser['role'] === self::ROLE_RESELLER) {
            return $this->getByParentId($currentUser['id'], $limit, $offset, $search);
        }
        return [];
    }

    /**
     * Zlicz użytkowników widocznych dla managera
     */
    public function countVisible(array $currentUser, ?string $search = null): int
    {
        if ($currentUser['role'] === self::ROLE_ADMIN) {
            return $this->count($search);
        } elseif ($currentUser['role'] === self::ROLE_RESELLER) {
            return $this->countByParentId($currentUser['id'], $search);
        }
        return 0;
    }

    /**
     * Pobierz użytkownika po ID
     */
    public function getById(int $id): ?array
    {
        return $this->db->fetchOne(
            "SELECT u.*, 
                    (SELECT COUNT(*) FROM devices d WHERE d.user_id = u.id) as devices_count,
                    (SELECT COUNT(*) FROM devices d2 
                     JOIN playlists p ON p.device_id = d2.id 
                     WHERE d2.user_id = u.id) as playlists_count,
                    parent.username as parent_username
             FROM users u
             LEFT JOIN users parent ON u.parent_id = parent.id
             WHERE u.id = :id",
            ['id' => $id]
        );
    }

    /**
     * Pobierz użytkownika po nazwie
     */
    public function getByUsername(string $username): ?array
    {
        return $this->db->fetchOne(
            "SELECT * FROM users WHERE username = :username",
            ['username' => $username]
        );
    }

    /**
     * Utwórz nowego użytkownika
     */
    public function create(array $data, ?int $loggedUserId = null): array
    {
        // Walidacja
        if (empty($data['username']) || strlen($data['username']) < 3) {
            return ['success' => false, 'error' => 'Nazwa użytkownika musi mieć minimum 3 znaki.'];
        }

        if (empty($data['password']) || strlen($data['password']) < 6) {
            return ['success' => false, 'error' => 'Hasło musi mieć minimum 6 znaków.'];
        }

        if (!preg_match('/^[a-zA-Z0-9_-]+$/', $data['username'])) {
            return ['success' => false, 'error' => 'Nazwa użytkownika może zawierać tylko litery, cyfry, _ i -.'];
        }

        // Sprawdź czy nazwa jest unikalna
        if ($this->getByUsername($data['username'])) {
            return ['success' => false, 'error' => 'Nazwa użytkownika jest już zajęta.'];
        }

        $hash = password_hash($data['password'], PASSWORD_BCRYPT, ['cost' => BCRYPT_COST]);
        $role = $data['role'] ?? self::ROLE_USER;
        $parentId = $data['parent_id'] ?? null;
        $deviceLimit = (int)($data['device_limit'] ?? getSetting('default_device_limit', 3));
        $playlistLimit = (int)($data['playlist_limit'] ?? getSetting('default_playlist_limit', 100));

        // Walidacja roli
        $validRoles = [self::ROLE_ADMIN, self::ROLE_RESELLER, self::ROLE_USER];
        if (!in_array($role, $validRoles)) {
            $role = self::ROLE_USER;
        }

        // Dla resellera - pula = jego własny limit (może używać całej puli jeśli nie ma userów)
        $maxUsers = 0;
        $maxDevices = 0;
        $maxPlaylists = 0;
        
        if ($role === self::ROLE_RESELLER) {
            $maxUsers = (int)($data['max_users'] ?? 10);
            $maxDevices = (int)($data['max_devices'] ?? 50);
            $maxPlaylists = (int)($data['max_playlists'] ?? 200);
            // Reseller używa swojej puli - device_limit/playlist_limit = max wartości
            $deviceLimit = $maxDevices;
            $playlistLimit = $maxPlaylists;
        }

        // Sprawdź limity resellera jeśli tworzący jest resellerem
        if ($loggedUserId) {
            $creator = $this->getById($loggedUserId);
            if ($creator && $creator['role'] === self::ROLE_RESELLER) {
                $limitCheck = $this->checkResellerLimits($loggedUserId, $deviceLimit, $playlistLimit);
                if (!$limitCheck['success']) {
                    return $limitCheck;
                }
                // Reseller może tworzyć tylko userów
                $role = self::ROLE_USER;
                $parentId = $loggedUserId;
            }
        }

        // Buduj INSERT dynamicznie - tylko istniejące kolumny
        $columns = ['username', 'password_hash', 'role', 'parent_id', 'device_limit', 'playlist_limit', 
                    'max_users', 'max_devices', 'max_playlists'];
        $placeholders = [':username', ':password', ':role', ':parent_id', ':device_limit', ':playlist_limit',
                        ':max_users', ':max_devices', ':max_playlists'];
        $params = [
            'username' => $data['username'],
            'password' => $hash,
            'role' => $role,
            'parent_id' => $parentId,
            'device_limit' => max(1, $deviceLimit),
            'playlist_limit' => max(1, $playlistLimit),
            'max_users' => $maxUsers,
            'max_devices' => $maxDevices,
            'max_playlists' => $maxPlaylists
        ];

        // Sprawdź czy kolumna is_active istnieje
        $tableInfo = $this->db->fetchAll("PRAGMA table_info(users)");
        $existingColumns = array_column($tableInfo, 'name');
        
        if (in_array('is_active', $existingColumns)) {
            $columns[] = 'is_active';
            $placeholders[] = '1';
        }

        $sql = "INSERT INTO users (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $placeholders) . ")";
        
        $id = $this->db->insert($sql, $params);

        $roleLabel = $this->getRoleLabel($role);
        $this->logger->log('user_created', "Utworzono {$roleLabel}: {$data['username']} (ID: {$id})", $loggedUserId);

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

    /**
     * Aktualizuj użytkownika
     */
    public function update(int $id, array $data, ?int $loggedUserId = null): array
    {
        $user = $this->getById($id);
        
        if (!$user) {
            return ['success' => false, 'error' => 'Użytkownik nie istnieje.'];
        }

        // Jeśli zmienia się username, sprawdź unikalność
        if (!empty($data['username']) && $data['username'] !== $user['username']) {
            if (!preg_match('/^[a-zA-Z0-9_-]+$/', $data['username'])) {
                return ['success' => false, 'error' => 'Nazwa użytkownika może zawierać tylko litery, cyfry, _ i -.'];
            }
            
            if ($this->getByUsername($data['username'])) {
                return ['success' => false, 'error' => 'Nazwa użytkownika jest już zajęta.'];
            }
        }

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

        if (!empty($data['username'])) {
            $updates[] = "username = :username";
            $params['username'] = $data['username'];
        }

        if (isset($data['role']) && in_array($data['role'], [self::ROLE_ADMIN, self::ROLE_RESELLER, self::ROLE_USER])) {
            $updates[] = "role = :role";
            $params['role'] = $data['role'];
        }

        if (isset($data['parent_id'])) {
            $updates[] = "parent_id = :parent_id";
            $params['parent_id'] = $data['parent_id'] ?: null;
        }

        // Dla zwykłych userów - aktualizuj device_limit i playlist_limit
        if (isset($data['device_limit']) && ($user['role'] === self::ROLE_USER || !isset($data['role']) || $data['role'] === self::ROLE_USER)) {
            $updates[] = "device_limit = :device_limit";
            $params['device_limit'] = max(1, (int)$data['device_limit']);
        }

        if (isset($data['playlist_limit']) && ($user['role'] === self::ROLE_USER || !isset($data['role']) || $data['role'] === self::ROLE_USER)) {
            $updates[] = "playlist_limit = :playlist_limit";
            $params['playlist_limit'] = max(1, (int)$data['playlist_limit']);
        }

        // Pola tylko dla resellera
        if (isset($data['max_users'])) {
            $updates[] = "max_users = :max_users";
            $params['max_users'] = max(0, (int)$data['max_users']);
        }

        if (isset($data['max_devices'])) {
            $maxDevices = max(1, (int)$data['max_devices']);
            $updates[] = "max_devices = :max_devices";
            $params['max_devices'] = $maxDevices;
            // Dla resellera - synchronizuj device_limit z max_devices
            $targetRole = $data['role'] ?? $user['role'];
            if ($targetRole === self::ROLE_RESELLER) {
                $updates[] = "device_limit = :device_limit";
                $params['device_limit'] = $maxDevices;
            }
        }

        if (isset($data['max_playlists'])) {
            $maxPlaylists = max(1, (int)$data['max_playlists']);
            $updates[] = "max_playlists = :max_playlists";
            $params['max_playlists'] = $maxPlaylists;
            // Dla resellera - synchronizuj playlist_limit z max_playlists
            $targetRole = $data['role'] ?? $user['role'];
            if ($targetRole === self::ROLE_RESELLER) {
                $updates[] = "playlist_limit = :playlist_limit";
                $params['playlist_limit'] = $maxPlaylists;
            }
        }

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

        // Zmiana hasła
        if (!empty($data['password'])) {
            if (strlen($data['password']) < 6) {
                return ['success' => false, 'error' => 'Hasło musi mieć minimum 6 znaków.'];
            }
            $updates[] = "password_hash = :password_hash";
            $params['password_hash'] = password_hash($data['password'], PASSWORD_BCRYPT, ['cost' => BCRYPT_COST]);
        }

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

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

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

        $this->logger->log('user_updated', "Zaktualizowano użytkownika ID: {$id}", $loggedUserId);

        return ['success' => true];
    }

    /**
     * Usuń użytkownika
     */
    public function delete(int $id, ?int $loggedUserId = null): array
    {
        $user = $this->getById($id);
        
        if (!$user) {
            return ['success' => false, 'error' => 'Użytkownik nie istnieje.'];
        }

        if ($user['role'] === self::ROLE_ADMIN) {
            // Sprawdź czy to nie ostatni admin
            $adminCount = $this->db->fetchOne(
                "SELECT COUNT(*) as count FROM users WHERE role = 'admin' AND is_active = 1"
            );
            
            if ($adminCount['count'] <= 1) {
                return ['success' => false, 'error' => 'Nie można usunąć ostatniego administratora.'];
            }
        }

        // Jeśli to reseller, usuń też jego userów
        if ($user['role'] === self::ROLE_RESELLER) {
            $this->db->execute("DELETE FROM users WHERE parent_id = :id", ['id' => $id]);
        }

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

        $roleLabel = $this->getRoleLabel($user['role']);
        $this->logger->log('user_deleted', "Usunięto {$roleLabel}: {$user['username']} (ID: {$id})", $loggedUserId);

        return ['success' => true];
    }

    /**
     * Zlicz wszystkich użytkowników
     */
    public function count(?string $search = null, ?int $parentId = null): int
    {
        $sql = "SELECT COUNT(*) as count FROM users";
        $params = [];
        $where = [];
        
        if ($search) {
            $where[] = "username LIKE :search";
            $params['search'] = "%{$search}%";
        }
        
        if ($parentId !== null) {
            $where[] = "parent_id = :parent_id";
            $params['parent_id'] = $parentId;
        }
        
        if (!empty($where)) {
            $sql .= " WHERE " . implode(' AND ', $where);
        }
        
        $result = $this->db->fetchOne($sql, $params);
        return $result['count'] ?? 0;
    }

    /**
     * Zlicz użytkowników po parent_id
     */
    public function countByParentId(int $parentId, ?string $search = null): int
    {
        return $this->count($search, $parentId);
    }

    /**
     * Pobierz statystyki użytkowników
     */
    public function getStats(): array
    {
        $total = $this->db->fetchOne("SELECT COUNT(*) as count FROM users");
        $active = $this->db->fetchOne("SELECT COUNT(*) as count FROM users WHERE is_active = 1");
        $admins = $this->db->fetchOne("SELECT COUNT(*) as count FROM users WHERE role = 'admin'");
        $resellers = $this->db->fetchOne("SELECT COUNT(*) as count FROM users WHERE role = 'reseller'");
        $users = $this->db->fetchOne("SELECT COUNT(*) as count FROM users WHERE role = 'user'");
        $todayLogins = $this->db->fetchOne(
            "SELECT COUNT(*) as count FROM users WHERE date(last_login) = date('now')"
        );
        
        return [
            'total' => $total['count'] ?? 0,
            'active' => $active['count'] ?? 0,
            'admins' => $admins['count'] ?? 0,
            'resellers' => $resellers['count'] ?? 0,
            'users' => $users['count'] ?? 0,
            'today_logins' => $todayLogins['count'] ?? 0
        ];
    }

    /**
     * Pobierz wykorzystanie limitów resellera
     * Uwzględnia: własne urządzenia/playlisty resellera + przydzielone limity użytkownikom
     */
    public function getResellerUsage(int $resellerId): array
    {
        $reseller = $this->getById($resellerId);
        
        if (!$reseller || $reseller['role'] !== self::ROLE_RESELLER) {
            return [
                'users' => ['used' => 0, 'max' => 0],
                'devices' => ['used' => 0, 'max' => 0, 'own' => 0, 'allocated' => 0],
                'playlists' => ['used' => 0, 'max' => 0, 'own' => 0, 'allocated' => 0]
            ];
        }

        // Zlicz userów resellera
        $usersCount = $this->db->fetchOne(
            "SELECT COUNT(*) as count FROM users WHERE parent_id = :id",
            ['id' => $resellerId]
        );

        // Zlicz sumę device_limit userów resellera (przydzielone)
        $devicesAllocated = $this->db->fetchOne(
            "SELECT COALESCE(SUM(device_limit), 0) as total FROM users WHERE parent_id = :id",
            ['id' => $resellerId]
        );

        // Zlicz sumę playlist_limit userów resellera (przydzielone)
        $playlistsAllocated = $this->db->fetchOne(
            "SELECT COALESCE(SUM(playlist_limit), 0) as total FROM users WHERE parent_id = :id",
            ['id' => $resellerId]
        );

        // Zlicz własne urządzenia resellera (faktycznie używane)
        $ownDevices = $this->db->fetchOne(
            "SELECT COUNT(*) as count FROM devices WHERE user_id = :id",
            ['id' => $resellerId]
        );

        // Zlicz własne playlisty resellera (faktycznie używane)
        $ownPlaylists = $this->db->fetchOne(
            "SELECT COUNT(*) as count FROM playlists p 
             JOIN devices d ON p.device_id = d.id 
             WHERE d.user_id = :id",
            ['id' => $resellerId]
        );

        // Łączne wykorzystanie = własne + przydzielone użytkownikom
        $devicesUsed = (int)$ownDevices['count'] + (int)$devicesAllocated['total'];
        $playlistsUsed = (int)$ownPlaylists['count'] + (int)$playlistsAllocated['total'];

        // Dostępne dla własnego użytku resellera = max - przydzielone userom - już używane własne
        $devicesAvailableForSelf = (int)$reseller['max_devices'] - (int)$devicesAllocated['total'] - (int)$ownDevices['count'];
        $playlistsAvailableForSelf = (int)$reseller['max_playlists'] - (int)$playlistsAllocated['total'] - (int)$ownPlaylists['count'];

        return [
            'users' => [
                'used' => (int)$usersCount['count'],
                'max' => (int)$reseller['max_users']
            ],
            'devices' => [
                'used' => $devicesUsed,
                'max' => (int)$reseller['max_devices'],
                'own' => (int)$ownDevices['count'],
                'allocated' => (int)$devicesAllocated['total'],
                'available_for_self' => max(0, $devicesAvailableForSelf),
                'available_for_users' => max(0, (int)$reseller['max_devices'] - $devicesUsed)
            ],
            'playlists' => [
                'used' => $playlistsUsed,
                'max' => (int)$reseller['max_playlists'],
                'own' => (int)$ownPlaylists['count'],
                'allocated' => (int)$playlistsAllocated['total'],
                'available_for_self' => max(0, $playlistsAvailableForSelf),
                'available_for_users' => max(0, (int)$reseller['max_playlists'] - $playlistsUsed)
            ]
        ];
    }

    /**
     * Sprawdź czy reseller może dodać użytkownika z podanymi limitami
     */
    public function checkResellerLimits(int $resellerId, int $deviceLimit, int $playlistLimit, ?int $excludeUserId = null): array
    {
        $usage = $this->getResellerUsage($resellerId);
        
        // Odejmij limity edytowanego użytkownika jeśli to edycja
        if ($excludeUserId) {
            $existingUser = $this->getById($excludeUserId);
            if ($existingUser && $existingUser['parent_id'] == $resellerId) {
                $usage['devices']['used'] -= $existingUser['device_limit'];
                $usage['playlists']['used'] -= $existingUser['playlist_limit'];
            }
        }

        // Sprawdź limit użytkowników (tylko przy tworzeniu nowego)
        if (!$excludeUserId && $usage['users']['used'] >= $usage['users']['max']) {
            return [
                'success' => false, 
                'error' => "Przekroczono limit użytkowników ({$usage['users']['max']})."
            ];
        }

        // Sprawdź limit urządzeń
        if ($usage['devices']['used'] + $deviceLimit > $usage['devices']['max']) {
            $available = $usage['devices']['max'] - $usage['devices']['used'];
            return [
                'success' => false, 
                'error' => "Przekroczono limit urządzeń. Dostępne: {$available}, żądane: {$deviceLimit}."
            ];
        }

        // Sprawdź limit playlist
        if ($usage['playlists']['used'] + $playlistLimit > $usage['playlists']['max']) {
            $available = $usage['playlists']['max'] - $usage['playlists']['used'];
            return [
                'success' => false, 
                'error' => "Przekroczono limit playlist. Dostępne: {$available}, żądane: {$playlistLimit}."
            ];
        }

        return ['success' => true];
    }

    /**
     * Pobierz dostępne limity dla resellera
     */
    public function getResellerAvailableLimits(int $resellerId): array
    {
        $usage = $this->getResellerUsage($resellerId);
        
        return [
            'users' => max(0, $usage['users']['max'] - $usage['users']['used']),
            'devices' => max(0, $usage['devices']['max'] - $usage['devices']['used']),
            'playlists' => max(0, $usage['playlists']['max'] - $usage['playlists']['used'])
        ];
    }

    /**
     * Pobierz wszystkich resellerów (dla admina)
     */
    public function getResellers(): array
    {
        return $this->db->fetchAll(
            "SELECT u.*, 
                    (SELECT COUNT(*) FROM users u2 WHERE u2.parent_id = u.id) as users_count
             FROM users u 
             WHERE u.role = 'reseller' 
             ORDER BY u.username"
        );
    }

    /**
     * Pobierz etykietę roli
     */
    public function getRoleLabel(string $role): string
    {
        $labels = [
            self::ROLE_ADMIN => 'administratora',
            self::ROLE_RESELLER => 'resellera',
            self::ROLE_USER => 'użytkownika'
        ];
        return $labels[$role] ?? 'użytkownika';
    }

    /**
     * Pobierz ID wszystkich użytkowników widocznych dla danego managera
     */
    public function getVisibleUserIds(array $currentUser): array
    {
        if ($currentUser['role'] === self::ROLE_ADMIN) {
            $result = $this->db->fetchAll("SELECT id FROM users");
        } elseif ($currentUser['role'] === self::ROLE_RESELLER) {
            $result = $this->db->fetchAll(
                "SELECT id FROM users WHERE parent_id = :parent_id",
                ['parent_id' => $currentUser['id']]
            );
            // Dodaj też siebie
            $result[] = ['id' => $currentUser['id']];
        } else {
            $result = [['id' => $currentUser['id']]];
        }
        
        return array_column($result, 'id');
    }

    /**
     * Sprawdź czy użytkownik może zarządzać innym użytkownikiem
     */
    public function canManageUser(array $currentUser, int $targetUserId): bool
    {
        if ($currentUser['role'] === self::ROLE_ADMIN) {
            return true;
        }
        
        if ($currentUser['role'] === self::ROLE_RESELLER) {
            $targetUser = $this->getById($targetUserId);
            return $targetUser && $targetUser['parent_id'] == $currentUser['id'];
        }
        
        return false;
    }
}
