<?php
require_once 'config.php';
require_once 'Database.php';
include '../helper_functions/data_request.php';
class Doors {
    private $db;
    private function getUserPropertyId($userId, $userType) {
        $conn = $this->db->getConnection();
    
        switch ($userType) {
            case 'property_user':
                $stmt = $conn->prepare("SELECT property_id FROM property_users WHERE id = ? LIMIT 1");
                break;
            case 'multi_prop':
                $stmt = $conn->prepare("SELECT property_id FROM multi_prop_users WHERE user_assoc = ? LIMIT 1");
                break;
            case 'dealer_admin':
                $stmt = $conn->prepare("SELECT default_property_id AS property_id FROM dealer_admins WHERE id = ? LIMIT 1");
                break;
            default:
                return null;
        }
    
        $stmt->execute([$userId]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        return $result['property_id'] ?? null;
    }       
    
    public function __construct() {
        $this->db = new Database();
    }
    
    private function getUserFromToken() {
        $headers = getallheaders();
        $auth = isset($headers['Authorization']) ? $headers['Authorization'] : '';
        
        if (!preg_match('/Bearer\s+(.+)/', $auth, $matches)) {
            return null;
        }
    
        $sessionId = $matches[1];
    
        $stmt = $this->db->getConnection()->prepare(
            "SELECT user_id, user_type 
             FROM mobile_sessions 
             WHERE session_id = ? AND expires_at > NOW()"
        );
        $stmt->execute([$sessionId]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }    
    private function hasCameraViewPermission($user, $hardwareSettingId, $propertyId) {
        $cameraSql = "
            SELECT lcd.id AS camera_id, lcd.liveImageURL
            FROM hardwares_settings hs
            LEFT JOIN gate_doors gd ON hs.assigned_door = gd.id
            LEFT JOIN live_camera_streaming_data lcd ON gd.camera_id = lcd.id AND lcd.deleted_at IS NULL
            WHERE hs.id = :hardware_id
            LIMIT 1
        ";
        $camera = fetchSingleDataWithJoins($cameraSql, [':hardware_id' => $hardwareSettingId]);
        if (!$camera || empty($camera['camera_id'])) return false;
    
        $email = $this->getUserEmail($user['user_id'], $user['user_type']);
        if (!$email) return false;
    
        $permSql = "
            SELECT id FROM camera_permissions
            WHERE property_user_email = :email
            AND camera_id = :camera_id
            AND property_id = :property_id
            AND have_view_permission = 1
            LIMIT 1
        ";
    
        $perm = fetchSingleDataWithJoins($permSql, [
            ':email' => $email,
            ':camera_id' => $camera['camera_id'],
            ':property_id' => $propertyId
        ]);
    
        return !empty($perm);
    }       
    private function getUserEmail($userId, $userType) {
        switch ($userType) {
            case 'user':
                $row = fetchSingleDataWithJoins("SELECT email FROM users WHERE id = :id", [':id' => $userId]);
                return $row['email'] ?? null;
            case 'property_user':
            case 'multi_prop':
                $row = fetchSingleDataWithJoins("SELECT email FROM property_users WHERE id = :id", [':id' => $userId]);
                return $row['email'] ?? null;
            case 'dealer_admin':
                $row = fetchSingleDataWithJoins("SELECT email FROM dealer_admins WHERE id = :id", [':id' => $userId]);
                return $row['email'] ?? null;
            default:
                return null;
        }
    }
 
    public function list() {
        $user = $this->getUserFromToken();
        if (!$user || empty($user['user_id']) || empty($user['user_type'])) {
            http_response_code(401);
            echo json_encode(['error' => ['message' => 'Unauthorized']]);
            return;
        }
    
        $userId = $user['user_id'];
        $userType = $user['user_type'];
        $propertyId = $this->getUserPropertyId($userId, $userType);
        if (!$propertyId) {
            http_response_code(400);
            echo json_encode(['error' => ['message' => 'No property associated with user']]);
            return;
        }
    
        $sql = "
            SELECT 
                hs.id AS hardware_setting_id,
                hs.assigned_door AS door_id,
                gd.door_name,
                gd.door_image AS videoThumbnail,
                gd.status,
                pp.actions_to_perform,
                lcd.liveImageURL,
                hs.property_id
            FROM hardwares_settings hs
            LEFT JOIN gate_doors gd ON hs.assigned_door = gd.id
            LEFT JOIN live_camera_streaming_data lcd ON gd.camera_id = lcd.id AND lcd.deleted_at IS NULL
            LEFT JOIN property_permissions pp ON 
                pp.door_id = hs.id 
                AND pp.user_id = :user_id 
                AND pp.user_type = :user_type
                AND pp.property_id = :property_id
            WHERE hs.property_id = :property_id
        ";
    
        $results = fetchDataWithJoins($sql, [
            ':user_id' => $userId,
            ':user_type' => $userType,
            ':property_id' => $propertyId
        ]);
    
        if (!$results) {
            echo json_encode([]);
            return;
        }
    
        $final = [];
    
        foreach ($results as $row) {
            $actionsJson = $row['actions_to_perform'] ?? '{}';
            $actionsDecoded = json_decode($actionsJson, true);
    
            $canOpen = !empty($actionsDecoded['Open']);
            $canRelease = !empty($actionsDecoded['Release']);
            $canHold = !empty($actionsDecoded['Holdopen']);
            $eventsSql = "
                SELECT id, created_at AS timestamp, eventName AS action, created_by AS user
                FROM hardwares_settings_events
                WHERE eventOf = :eventOf
                ORDER BY created_at DESC
                LIMIT 5
            ";
            $events = fetchDataWithJoins($eventsSql, [':eventOf' => $row['hardware_setting_id']]);
    
            $final[] = [
                'id' => (string) $row['hardware_setting_id'],
                'name' => $row['door_name'] ?? 'Unknown Door',
                'status' => $row['status'] ?? 'closed',
                'videoThumbnail' => $row['liveImageURL'] ?? '',
                'events' => $events ?: [],
                'permissions' => [
                    'canOpen' => $canOpen,
                    'canRelease' => $canRelease,
                    'canHold' => $canHold,
                    'canViewEvents' => true,
                    'canViewVideo' => $this->hasCameraViewPermission($user, $row['hardware_setting_id'], $row['property_id']),
                ]
            ];
        }
    
        echo json_encode($final);
    }    
         
    public function get($id) {
        $session = $this->getUserFromToken();
        if (!$session || empty($session['user_id']) || empty($session['user_type'])) {
            http_response_code(401);
            echo json_encode(['error' => ['message' => 'Unauthorized']]);
            return;
        }
    
        $userId = $session['user_id'];
        $userType = $session['user_type'];
    
        $propertyId = $this->getUserPropertyId($userId, $userType);
        if (!$propertyId) {
            http_response_code(400);
            echo json_encode(['error' => ['message' => 'No property associated with user']]);
            return;
        }
    
        $sql = "
            SELECT 
                gd.id AS id,
                gd.door_name AS name,
                gd.status,
                gd.door_image,
                lcd.liveImageURL AS videoThumbnail,
                lcd.cameraName,
                lcd.streamingUrl,
                pp.actions_to_perform,
                hs.id AS hardware_setting_id
            FROM gate_doors gd
            LEFT JOIN hardwares_settings hs ON hs.assigned_door = gd.id
            LEFT JOIN live_camera_streaming_data lcd ON gd.camera_id = lcd.id  AND lcd.deleted_at IS NULL
            LEFT JOIN property_permissions pp 
                ON pp.door_id = hs.id 
                AND pp.user_id = :user_id 
                AND pp.user_type = :user_type 
                AND pp.property_id = :property_id
            WHERE hs.id = :door_id
            LIMIT 1
        ";
    
        $params = [
            ':user_id' => $userId,
            ':user_type' => $userType,
            ':property_id' => $propertyId,
            ':door_id' => $id
        ];
    
        $door = fetchSingleDataWithJoins($sql, $params);
    
        if (!$door) {
            http_response_code(404);
            echo json_encode(['error' => ['message' => 'Door not found']]);
            return;
        }
    
        $permissions = [
            'canOpen' => false,
            'canRelease' => false,
            'canHold' => false,
            'canViewEvents' => true,
            'canViewVideo' => false
        ];
    
        if (!empty($door['actions_to_perform'])) {
            $actions = json_decode($door['actions_to_perform'], true);
            if (is_array($actions)) {
                $permissions['canOpen'] = !empty($actions['Open']);
                $permissions['canRelease'] = !empty($actions['Release']);
                $permissions['canHold'] = !empty($actions['Holdopen']);
            }
        }
        $permissions['canViewVideo'] = $this->hasCameraViewPermission($session, $door['hardware_setting_id'], $propertyId);
    
        $eventsSql = "
            SELECT id, created_at AS timestamp, eventName AS action, created_by AS user
            FROM hardwares_settings_events
            WHERE eventOf = :door_id
            ORDER BY created_at DESC
            LIMIT 5
        ";
        $events = fetchDataWithJoins($eventsSql, [':door_id' => $door['hardware_setting_id']]);
    
        echo json_encode([
            'id' => (string) $door['id'],
            'name' => $door['name'],
            'status' => $door['status'] ?? 'closed',
            'streamingUrl' => $door['videoThumbnail'] ?? '',
            'videoThumbnail' => $door['videoThumbnail'] ?? '',
            'events' => $events ?: [],
            'permissions' => $permissions
        ]);
    }              
    public function control($id) {
        try {
            $session = $this->getUserFromToken();
            if (!$session || empty($session['user_id']) || empty($session['user_type'])) {
                http_response_code(401);
                echo json_encode(['error' => ['message' => 'Unauthorized']]);
                return;
            }
            $userId = $session['user_id'];
            $userType = $session['user_type'];
            $propertyId = $this->getUserPropertyId($userId, $userType);
            if (!$propertyId) {
                http_response_code(400);
                echo json_encode(['error' => ['message' => 'No property associated with user']]);
                return;
            }
            $data = json_decode(file_get_contents('php://input'), true);
            $action = $data['action'] ?? null;
            $duration = $data['duration'] ?? null;

            switch($action) {

                case "hold":
                    $action = "holdopen";
                    break;
                default:
                    $action = $action;
                    break;
            }

            $validActions = ['open' => 2, 'hold' => 1, 'release' => 0];
            if (!isset($validActions[$action])) {
                http_response_code(400);
                echo json_encode(['error' => ['message' => 'Invalid action']]);
                return;
            }
            $perm = fetchSingleDataWithJoins("
                SELECT pp.actions_to_perform
                FROM property_permissions pp
                JOIN hardwares_settings hs ON pp.door_id = hs.id
                WHERE hs.id = :door_id 
                AND pp.user_id = :user_id
                AND pp.user_type = :user_type
                AND pp.property_id = :property_id
                LIMIT 1
            ", [
                ':door_id' => $id,
                ':user_id' => $userId,
                ':user_type' => $userType,
                ':property_id' => $propertyId
            ]);
            if (!$perm || empty($perm['actions_to_perform'])) {
                http_response_code(403);
                echo json_encode(['error' => ['message' => 'Permission denied']]);
                return;
            }
            $actions = json_decode($perm['actions_to_perform'], true);
            $requiredAction = ucfirst($action);
            if (empty($actions[$requiredAction])) {
                http_response_code(403);
                echo json_encode(['error' => ['message' => 'Action not allowed']]);
                return;
            }

            $hardware = fetchSingleDataWithJoins("
                SELECT completeUrl, output, device_type
                FROM hardwares_settings 
                WHERE id = :id 
                SELECT 
                    hd.complete_url as completeUrl, 
                    hs.output, 
                    hd.device_type
                FROM hardwares_settings hs
                JOIN hardware_devices hd ON hs.device_id = hd.id
                WHERE hs.id = :id
                LIMIT 1, 
                [':id' => $id]);
            ", [':id' => $id]);       

            if (!$hardware) {
                http_response_code(500);
                echo json_encode(['error' => ['message' => 'Hardware not found']]);
                return;
            }
            $deviceType = $hardware['device_type'] ?? 'relay';
            switch ($deviceType) {
                case 'SMSIO':
                    // Placeholder - you can implement SMS logic here
                    http_response_code(501);
                    echo json_encode(['error' => ['message' => 'SMSIO control not yet implemented']]);
                    return;

                case 'relay':
                default:
                case 'web_Relay':
                    if (empty($hardware['completeUrl']) || empty($hardware['output'])) {
                        http_response_code(500);
                        echo json_encode(['error' => ['message' => 'Incomplete relay configuration']]);
                        return;
                    }
                    $commandValue = $validActions[$action];
                    $controlUrl = "{$hardware['completeUrl']}?{$hardware['output']}={$commandValue}";

                    if (is_numeric($duration) && $action === "holdopen") {
                        $pulseTime = (int)$duration * 60;

                        preg_match('/\d+$/', $hardware['output'], $matches);
                        $relayNum = $matches[0] ?? null;

                        if ($relayNum) {
                            $pulseTimeKey = "pulseTime{$relayNum}";
                            $commandValue = 2;
                            $controlUrl = "{$hardware['completeUrl']}?{$pulseTimeKey}={$pulseTime}&{$hardware['output']}={$commandValue}";
                        } else {
                            $controlUrl = "{$hardware['completeUrl']}?{$hardware['output']}={$commandValue}";
                        }
                    } else {
                        $controlUrl = "{$hardware['completeUrl']}?{$hardware['output']}={$commandValue}";
                    }

                    $curl = curl_init();
                    curl_setopt_array($curl, [
                        CURLOPT_URL => $controlUrl,
                        CURLOPT_RETURNTRANSFER => true,
                        CURLOPT_TIMEOUT => 10,
                        CURLOPT_FOLLOWLOCATION => true,
                        CURLOPT_HTTPHEADER => [
                            'Authorization: Basic YWRtaW46d2VicmVsYXk='
                        ]
                    ]);
                    $response = curl_exec($curl);
                    $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
                    curl_close($curl);
                    if ($httpCode !== 200 || strpos($response, '404 Error') !== false) {
                        http_response_code(500);
                        echo json_encode(['error' => ['message' => 'Failed to contact door hardware']]);
                        return;
                    }

                    break;
                default:
                    break;
            }

            $status = ($action === 'hold' || $action === 'open') ? 'unlocked' : 'locked';
            updateTable('gate_doors', ['status' => $status], 'id = :id', ['id' => $id]);
            insertData('hardwares_settings_events', [
                'eventName' => ucfirst($action),
                'eventType' => $deviceType === 'SMSIO' ? 'SMS IO' : 'door_control',
                'eventOf' => $id,
                'created_by' => $userId,
                'created_at' => date('Y-m-d H:i:s')
            ]);
            $doorData = $this->getDoorDataById($id, $propertyId);
            if (!$doorData) {
                http_response_code(404);
                echo json_encode(['error' => ['message' => 'Door not found']]);
                return;
            }
            echo json_encode([
                'success' => true,
                'door' => $doorData,
                'duration' => $duration,
                'status' => $status
            ]);
        } catch (Throwable $e) {
            error_log("Server error: " . $e->getMessage());
            error_log("Trace: " . $e->getTraceAsString());
            http_response_code(500);
            echo json_encode([
                'error' => [
                    'message' => 'An internal server error occurred. Please try again later.'
                ]
            ]);
        }
    }
    
    private function getDoorDataById($doorId, $propertyId) {
        $sql = "
            SELECT 
                gd.id AS id,
                gd.door_name AS name,
                gd.status,
                gd.door_image AS videoThumbnail,
                lcd.liveImageURL,
                lcd.cameraName,
                lcd.streamingUrl
            FROM gate_doors gd
            LEFT JOIN hardwares_settings hs ON hs.assigned_door = gd.id
            LEFT JOIN live_camera_streaming_data lcd ON gd.camera_id = lcd.id  AND lcd.deleted_at IS NULL
            WHERE hs.id = :door_id AND gd.property_id = :property_id
            LIMIT 1
        ";
    
        return fetchSingleDataWithJoins($sql, [
            ':door_id' => $doorId,
            ':property_id' => $propertyId
        ]);
    }
         
    public function getEvents($id) {
        $session = $this->getUserFromToken();
        if (!$session || empty($session['user_id']) || empty($session['user_type'])) {
            http_response_code(401);
            echo json_encode(['error' => ['message' => 'Unauthorized']]);
            return;
        }
    
        $userId = $session['user_id'];
        $userType = $session['user_type'];
        $propertyId = $this->getUserPropertyId($userId, $userType);
        if (!$propertyId) {
            http_response_code(403);
            echo json_encode(['error' => ['message' => 'User has no assigned property']]);
            return;
        }
        $permStmt = $this->db->getConnection()->prepare("
            SELECT COUNT(*) FROM property_permissions pp
            JOIN hardwares_settings hs ON pp.door_id = hs.id
            WHERE hs.assigned_door = :door_id 
              AND pp.user_id = :user_id 
              AND pp.user_type = :user_type
              AND pp.property_id = :property_id
        ");
        $permStmt->execute([
            ':door_id' => $id,
            ':user_id' => $userId,
            ':user_type' => $userType,
            ':property_id' => $propertyId
        ]);
        $hasAccess = $permStmt->fetchColumn();
    
        if (!$hasAccess) {
            http_response_code(403);
            echo json_encode(['error' => ['message' => 'Access denied to door events']]);
            return;
        }
        $from = isset($_GET['from']) ? date('Y-m-d H:i:s', strtotime($_GET['from'])) : null;
        $to = isset($_GET['to']) ? date('Y-m-d H:i:s', strtotime($_GET['to'])) : null;
        $limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
        $offset = isset($_GET['offset']) ? (int)$_GET['offset'] : 0;
    
        $sql = "SELECT id, created_at AS timestamp, eventName AS action, created_by AS user 
                FROM hardwares_settings_events 
                WHERE eventOf = ?";
        $params = [$id];
    
        if ($from) {
            $sql .= " AND created_at >= ?";
            $params[] = $from;
        }
    
        if ($to) {
            $sql .= " AND created_at <= ?";
            $params[] = $to;
        }
        $countSql = str_replace(
            "SELECT id, created_at AS timestamp, eventName AS action, created_by AS user",
            "SELECT COUNT(*)",
            $sql
        );
        $stmt = $this->db->getConnection()->prepare($countSql);
        $stmt->execute($params);
        $total = $stmt->fetchColumn();
        $sql .= " ORDER BY created_at DESC LIMIT ? OFFSET ?";
        $params[] = $limit;
        $params[] = $offset;
    
        $stmt = $this->db->getConnection()->prepare($sql);
        $stmt->execute($params);
        $events = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
        echo json_encode([
            'events' => $events,
            'total' => $total
        ]);
    }
    
    public function proxyImageFetch() {
        $session = $this->getUserFromToken();
        if (!$session || empty($session['user_id']) || empty($session['user_type'])) {
            http_response_code(401);
            echo json_encode(['error' => 'Unauthorized']);
            return;
        }
    
        $data = $_POST;
        if (!isset($data['imageUrl'])) {
            http_response_code(400);
            echo json_encode(['error' => 'Missing imageUrl']);
            return;
        }
    
        $imageUrl = $data['imageUrl'];
        if (!filter_var($imageUrl, FILTER_VALIDATE_URL) || !preg_match('/^https?:\/\//', $imageUrl)) {
            http_response_code(400);
            echo json_encode(['error' => 'Invalid or unsafe URL']);
            return;
        }

        $parsedUrl = parse_url($imageUrl);
        $host = $parsedUrl['host'] ?? '';
        $ip = gethostbyname($host);

        if (
            !$host ||
            filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false
        ) {
            error_log("SSRF attempt blocked: $host ($ip)");
            http_response_code(400);
            echo json_encode(['error' => 'Disallowed target']);
            return;
        }
        // $host = $parsedUrl['host'] ?? '';
        // $ip = gethostbyname($host);

        // if (
        //     !$host ||
        //     filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false
        // ) {
        //     error_log("SSRF attempt blocked: $host ($ip)");
        //     http_response_code(400);
        //     echo json_encode(['error' => 'Disallowed target']);
        //     return;
        // }

        $user = $parsedUrl['user'] ?? null;
        $pass = $parsedUrl['pass'] ?? null;
        $cleanUrl = $parsedUrl['scheme'] . '://' . $parsedUrl['host'];
        if (isset($parsedUrl['port'])) $cleanUrl .= ':' . $parsedUrl['port'];
        if (isset($parsedUrl['path'])) $cleanUrl .= $parsedUrl['path'];
        if (isset($parsedUrl['query'])) $cleanUrl .= '?' . $parsedUrl['query'];
    
        $finalIp = gethostbyname(parse_url($cleanUrl, PHP_URL_HOST));
        $blocked = ['127.', '10.', '192.168.', '169.254.'];
        foreach ($blocked as $range) {
            if (strpos($finalIp, $range) === 0) {
                error_log("Blocked final IP before curl: $finalIp");
                http_response_code(400);
                echo json_encode(['error' => 'Blocked IP']);
                return;
            }
        }
        if ($finalIp === '::1') {
            error_log("Blocked IPv6 loopback before curl");
            http_response_code(400);
            echo json_encode(['error' => 'Blocked IP']);
            return;
        }
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $cleanUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        if ($user && $pass) {
            curl_setopt($ch, CURLOPT_USERPWD, "$user:$pass");
            curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        }
    
        $response = curl_exec($ch);
    
        if ($response === false) {
            http_response_code(500);
            echo json_encode(['error' => curl_error($ch)]);
            curl_close($ch);
            return;
        }
    
        $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
        curl_close($ch);
    
        if (strpos($contentType, 'image/') !== 0) {
            http_response_code(400);
            echo json_encode(['error' => 'URL does not point to a valid image']);
            return;
        }
    
        echo json_encode([
            'image' => base64_encode($response),
            'mime' => $contentType
        ]);
    }   
      
}
$doors = new Doors();
$method = $_SERVER['REQUEST_METHOD'];
$segments = $GLOBALS['api_path_segments'] ?? [];
if (count($segments) === 2 && $segments[1] === 'image' && $method === 'POST') {
    $doors->proxyImageFetch();
    return;
}
switch (count($segments)) {
    case 1:
        if ($method === 'GET') $doors->list();
        break;
    case 2:
        if ($method === 'GET') $doors->get($segments[1]);
        break;
    case 3:
        if ($segments[2] === 'control' && $method === 'POST') {
            $doors->control($segments[1]);
        } elseif ($segments[2] === 'events' && $method === 'GET') {
            $doors->getEvents($segments[1]);
        }
        break;
    default:
        http_response_code(405);
        echo json_encode(['error' => ['message' => 'Unsupported method or endpoint']]);
}