<?php

declare(strict_types=1);

namespace ServerManager\Models;

use ServerManager\Core\Database;
use ServerManager\Core\App;
use ServerManager\Core\Encryption;
use ServerManager\Core\Session;

class Server
{
    private Database $db;
    private Encryption $encryption;
    private array $encryptedFields = ['ssh_password', 'ssh_key'];

    public function __construct()
    {
        $this->db = Database::getInstance();
        $this->encryption = App::getEncryption();
    }

    public function findById(int $id): ?array
    {
        $server = $this->db->fetch('SELECT * FROM servers WHERE id = ? AND status = \'active\'', [$id]);
        if ($server) {
            $server = $this->encryption->decryptArray($server, $this->encryptedFields);
        }
        return $server;
    }

    public function findAll(bool $activeOnly = false): array
    {
        $sql = 'SELECT * FROM servers WHERE status = \'active\'';
        $params = [];

        if ($activeOnly) {
            $sql .= ' AND is_active = 1';
        }

        $sql .= ' ORDER BY name ASC';
        $servers = $this->db->fetchAll($sql, $params);

        return array_map(function ($server) {
            return $this->encryption->decryptArray($server, $this->encryptedFields);
        }, $servers);
    }

    public function getAll(int $page = 1, int $perPage = 20, array $filters = []): array
    {
        $where = ["s.status = 'active'"];
        $params = [];

        if (!empty($filters['search'])) {
            $where[] = '(s.name LIKE ? OR s.ip_address LIKE ? OR s.description LIKE ?)';
            $search = '%' . $filters['search'] . '%';
            $params[] = $search;
            $params[] = $search;
            $params[] = $search;
        }

        if (isset($filters['is_active']) && $filters['is_active'] !== '') {
            $where[] = 's.is_active = ?';
            $params[] = (int) $filters['is_active'];
        }

        if (!empty($filters['group_name'])) {
            $where[] = 's.group_name = ?';
            $params[] = $filters['group_name'];
        }

        if (!empty($filters['status'])) {
            $where[] = 's.current_status = ?';
            $params[] = $filters['status'];
        }

        if (!empty($filters['accessible_ids'])) {
            $ids = array_map('intval', $filters['accessible_ids']);
            $placeholders = implode(',', array_fill(0, count($ids), '?'));
            $where[] = "s.id IN ({$placeholders})";
            $params = array_merge($params, $ids);
        }

        $whereClause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
        $offset = ($page - 1) * $perPage;

        $total = $this->db->fetch(
            "SELECT COUNT(*) as total FROM servers s {$whereClause}",
            $params
        );

        $servers = $this->db->fetchAll(
            "SELECT s.* FROM servers s {$whereClause} ORDER BY s.name ASC LIMIT ? OFFSET ?",
            array_merge($params, [$perPage, $offset])
        );

        $servers = array_map(function ($server) {
            return $this->encryption->decryptArray($server, $this->encryptedFields);
        }, $servers);

        return [
            'data' => $servers,
            'total' => (int) ($total['total'] ?? 0),
            'page' => $page,
            'per_page' => $perPage,
            'total_pages' => ceil((int) ($total['total'] ?? 0) / $perPage),
        ];
    }

    public function create(array $data): int
    {
        if (!empty($data['ssh_password'])) {
            $data['ssh_password'] = $this->encryption->encrypt($data['ssh_password']);
        } else {
            $data['ssh_password'] = null;
        }

        if (!empty($data['ssh_key'])) {
            $data['ssh_key'] = $this->encryption->encrypt($data['ssh_key']);
        } else {
            $data['ssh_key'] = null;
        }

        if (empty($data['agent_key'])) {
            $data['agent_key'] = $this->generateAgentKey();
        }

        $data['created_at'] = date('Y-m-d H:i:s');
        $data['updated_at'] = date('Y-m-d H:i:s');

        if (empty($data['current_status'])) {
            $data['current_status'] = 'unknown';
        }

        $userId = Session::get('user_id');
        if ($userId && empty($data['created_by'])) {
            $data['created_by'] = $userId;
        }

        return $this->db->insert('servers', $data);
    }

    public function update(int $id, array $data): bool
    {
        if (array_key_exists('ssh_password', $data)) {
            if (!empty($data['ssh_password'])) {
                $data['ssh_password'] = $this->encryption->encrypt($data['ssh_password']);
            } else {
                unset($data['ssh_password']);
            }
        }

        if (array_key_exists('ssh_key', $data)) {
            if (!empty($data['ssh_key'])) {
                $data['ssh_key'] = $this->encryption->encrypt($data['ssh_key']);
            } else {
                unset($data['ssh_key']);
            }
        }

        $data['updated_at'] = date('Y-m-d H:i:s');

        return $this->db->update('servers', $data, 'id = ?', [$id]) > 0;
    }

    public function delete(int $id): bool
    {
        return $this->db->update('servers', [
            'status' => 'deleted',
            'is_active' => 0,
            'updated_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]) > 0;
    }

    public function toggleActive(int $id): ?bool
    {
        $server = $this->db->fetch('SELECT is_active, status FROM servers WHERE id = ?', [$id]);
        if (!$server) {
            return null;
        }

        $newState = $server['is_active'] ? 0 : 1;
        $newStatus = $newState ? 'active' : 'disabled';
        $this->db->update('servers', [
            'is_active' => $newState,
            'status' => $newStatus,
            'updated_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);

        return (bool) $newState;
    }

    public function getGroups(): array
    {
        $groups = $this->db->fetchAll(
            'SELECT DISTINCT group_name FROM servers WHERE status = \'active\' AND group_name IS NOT NULL AND group_name != "" ORDER BY group_name'
        );
        return array_column($groups, 'group_name');
    }

    public function countAll(): int
    {
        $result = $this->db->fetch("SELECT COUNT(*) as total FROM servers WHERE status = 'active'");
        return (int) ($result['total'] ?? 0);
    }

    public function countOnline(): int
    {
        $result = $this->db->fetch(
            "SELECT COUNT(*) as total FROM servers WHERE current_status = 'online' AND status = 'active' AND is_active = 1"
        );
        return (int) ($result['total'] ?? 0);
    }

    public function getUserRole(int $serverId, int $userId): ?string
    {
        $server = $this->db->fetch('SELECT created_by FROM servers WHERE id = ? AND status = \'active\'', [$serverId]);
        if (!$server) {
            return null;
        }

        if ((int) $server['created_by'] === $userId) {
            return 'owner';
        }

        $direct = $this->db->fetch(
            'SELECT role FROM server_user WHERE server_id = ? AND user_id = ? AND status = \'active\'',
            [$serverId, $userId]
        );

        $teamRole = $this->db->fetch(
            'SELECT ts.role FROM team_user tu
             JOIN team_server ts ON tu.team_id = ts.team_id
             WHERE tu.user_id = ? AND ts.server_id = ? AND tu.status = \'active\' AND ts.status = \'active\'
             ORDER BY FIELD(ts.role, \'manager\', \'viewer\')
             LIMIT 1',
            [$userId, $serverId]
        );

        $directRole = $direct ? $direct['role'] : null;
        $teamRoleValue = $teamRole ? $teamRole['role'] : null;

        if ($directRole === 'manager' || $teamRoleValue === 'manager') {
            return 'manager';
        }
        if ($directRole === 'viewer' || $teamRoleValue === 'viewer') {
            return 'viewer';
        }

        return null;
    }

    public function getAccessibleServerIds(int $userId): array
    {
        $user = $this->db->fetch(
            'SELECT role FROM users WHERE id = ? AND status = \'active\'',
            [$userId]
        );

        if (!$user) {
            return [];
        }

        if ($user['role'] === 'super_admin') {
            $all = $this->db->fetchAll(
                'SELECT id FROM servers WHERE status = \'active\''
            );
            return array_map(fn($r) => (int) $r['id'], $all);
        }

        $owned = $this->db->fetchAll(
            'SELECT id FROM servers WHERE created_by = ? AND status = \'active\'',
            [$userId]
        );

        $assigned = $this->db->fetchAll(
            'SELECT server_id FROM server_user WHERE user_id = ? AND status = \'active\'',
            [$userId]
        );

        $teamServerIds = $this->db->fetchAll(
            'SELECT DISTINCT ts.server_id FROM team_user tu
             JOIN team_server ts ON tu.team_id = ts.team_id
             WHERE tu.user_id = ? AND tu.status = \'active\' AND ts.status = \'active\'',
            [$userId]
        );

        $ids = [];
        foreach ($owned as $row) {
            $ids[] = (int) $row['id'];
        }
        foreach ($assigned as $row) {
            $ids[] = (int) $row['server_id'];
        }
        foreach ($teamServerIds as $row) {
            $ids[] = (int) $row['server_id'];
        }

        return array_unique($ids);
    }

    public function isOwner(int $serverId, int $userId): bool
    {
        $server = $this->db->fetch('SELECT created_by FROM servers WHERE id = ? AND status = \'active\'', [$serverId]);
        return $server && (int) $server['created_by'] === $userId;
    }

    public function canManage(int $serverId, int $userId): bool
    {
        $role = $this->getUserRole($serverId, $userId);
        return $role === 'owner' || $role === 'manager';
    }

    public function canView(int $serverId, int $userId): bool
    {
        return $this->getUserRole($serverId, $userId) !== null;
    }

    public function getTeam(int $serverId): array
    {
        $members = $this->db->fetchAll(
            'SELECT su.*, u.username, u.email
             FROM server_user su
             JOIN users u ON su.user_id = u.id
             WHERE su.server_id = ? AND su.status = \'active\' AND u.status = \'active\'
             ORDER BY su.created_at ASC',
            [$serverId]
        );

        $owner = $this->db->fetch(
            'SELECT u.id, u.username, u.email
             FROM servers s
             JOIN users u ON s.created_by = u.id
             WHERE s.id = ? AND s.status = \'active\' AND u.status = \'active\'',
            [$serverId]
        );

        $result = [];
        if ($owner) {
            $result[] = [
                'user_id' => (int) $owner['id'],
                'username' => $owner['username'],
                'email' => $owner['email'],
                'role' => 'owner',
            ];
        }
        foreach ($members as $m) {
            $result[] = [
                'user_id' => (int) $m['user_id'],
                'username' => $m['username'],
                'email' => $m['email'],
                'role' => $m['role'],
            ];
        }
        return $result;
    }

    public function addUser(int $serverId, int $userId, string $role): bool
    {
        if (!in_array($role, ['viewer', 'manager'], true)) {
            return false;
        }

        $existing = $this->db->fetch(
            'SELECT id FROM server_user WHERE server_id = ? AND user_id = ? AND status = \'active\'',
            [$serverId, $userId]
        );
        if ($existing) {
            return false;
        }

        $this->db->insert('server_user', [
            'server_id' => $serverId,
            'user_id' => $userId,
            'role' => $role,
        ]);
        return true;
    }

    public function removeUser(int $serverId, int $userId): bool
    {
        return $this->db->update(
            'server_user',
            ['status' => 'deleted'],
            'server_id = ? AND user_id = ?',
            [$serverId, $userId]
        ) > 0;
    }

    public function updateUserRole(int $serverId, int $userId, string $role): bool
    {
        if (!in_array($role, ['viewer', 'manager'], true)) {
            return false;
        }

        return $this->db->update(
            'server_user',
            ['role' => $role],
            'server_id = ? AND user_id = ? AND status = \'active\'',
            [$serverId, $userId]
        ) > 0;
    }

    public function getOwnedServers(int $userId): array
    {
        return $this->db->fetchAll(
            'SELECT * FROM servers WHERE created_by = ? AND status = \'active\' ORDER BY name ASC',
            [$userId]
        );
    }

    public function findByAgentKey(string $key): ?array
    {
        $server = $this->db->fetch(
            'SELECT * FROM servers WHERE agent_key = ? AND status = \'active\'',
            [$key]
        );
        if ($server) {
            $server = $this->encryption->decryptArray($server, $this->encryptedFields);
        }
        return $server;
    }

    public function generateAgentKey(): string
    {
        return bin2hex(random_bytes(32));
    }

    public function regenerateAgentKey(int $id): string
    {
        $key = $this->generateAgentKey();
        $this->db->update('servers', [
            'agent_key' => $key,
            'updated_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]);
        return $key;
    }

    public function updateAgentLastSeen(int $id): bool
    {
        return $this->db->update('servers', [
            'agent_last_seen_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]) > 0;
    }

    public function setAgentInstalled(int $id, bool $installed, ?string $path = null): bool
    {
        return $this->db->update('servers', [
            'agent_installed' => $installed ? 1 : 0,
            'agent_install_path' => $path,
            'updated_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$id]) > 0;
    }
}
