<?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 = ? AND deleted_at IS NULL LIMIT 1");
                break;
            case 'multi_prop':
                $stmt = $conn->prepare("SELECT property_id FROM multi_prop_users WHERE user_assoc = ? AND deleted_at IS NULL LIMIT 1");
                break;
            case 'dealer_admin':
                $stmt = $conn->prepare("SELECT default_property_id AS property_id FROM dealer_admins WHERE id = ? AND deleted_at IS NULL LIMIT 1");
                break;
            default:
                return null;
        }
    
        $stmt->execute([$userId]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        return $result['property_id'] ?? null;
    }
    
    private function getCurrentPropertyId(array $session): ?int {
        $conn = $this->db->getConnection();
        
        $stmt = $conn->prepare("
            SELECT current_property_id
            FROM mobile_sessions
            WHERE user_id = :uid AND user_type = :utype AND session_id = :sid
            LIMIT 1
        ");
        $stmt->execute([
            ':uid' => $session['user_id'],
            ':utype' => $session['user_type'],
            ':sid' => $session['session_id']
        ]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!empty($row['current_property_id'])) {
            return (int)$row['current_property_id'];
        }

        return $this->getUserPropertyId($session['user_id'], $session['user_type']);
    }
    
    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, session_id
             FROM mobile_sessions
             WHERE session_id = ? AND expires_at > NOW()"
        );
        $stmt->execute([$sessionId]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    
    private static function parseMaybeEscapedJson($value, bool $assoc = true) {
        if ($value === null || $value === '' || is_array($value) || is_object($value)) {
            return $value;
        }

        $decoded = json_decode($value, $assoc);
        if (json_last_error() === JSON_ERROR_NONE) {
            return $decoded;
        }

        $tmp = $value;
        for ($i = 0; $i < 2; $i++) {
            $tmp = html_entity_decode($tmp, ENT_QUOTES | ENT_HTML5, 'UTF-8');
            $decoded = json_decode($tmp, $assoc);
            if (json_last_error() === JSON_ERROR_NONE) {
                return $decoded;
            }
        }

        $fallback = strtr($value, ['&quot;' => '"', '&#034;' => '"', '&#039;' => "'"]);
        $decoded  = json_decode($fallback, $assoc);
        if (json_last_error() === JSON_ERROR_NONE) {
            return $decoded;
        }

        error_log('parseMaybeEscapedJson: failed to decode: ' . substr($value, 0, 200));
        return null;
    }


    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
            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() {
        $session = $this->getUserFromToken();
        if (!$session || empty($session['user_id']) || empty($session['user_type'])) {
            http_response_code(401);
            echo json_encode(['error' => ['message' => 'Unauthorized']]);
            return;
        }

        $userType   = $session['user_type'];
        $propertyId = $this->getCurrentPropertyId($session);

        if (!$propertyId) {
            http_response_code(400);
            echo json_encode(['error' => ['message' => 'No property associated with user']]);
            return;
        }

        $privilegedTypes = [
            'super_admin', 'admin', 'dealer', 'dealer_admin',
            'organization_admin', 'sub_dealer', 'property_admin'
        ];
        $isPrivileged = in_array($userType, $privilegedTypes, true);

        switch ($userType) {
            case "multi_prop":
                $idToUse = fetchSingleDataWithJoins("
                    SELECT id
                    FROM multi_prop_users
                    WHERE user_assoc = :id
                    AND property_id = :propertyId
                    AND deleted_at IS NULL
                    LIMIT 1
                ", [':id' => $session['user_id'], ':propertyId' => $propertyId]);

                if (!$idToUse) {
                    $idToUse = fetchSingleDataWithJoins("
                        SELECT id
                        FROM property_users
                        WHERE id = :id
                        AND property_id = :propertyId
                        AND deleted_at IS NULL
                        LIMIT 1
                    ", [':id' => $session['user_id'], ':propertyId' => $propertyId]);
                    $userType = 'property_user';
                }

                $userId = $idToUse['id'] ?? null;
                break;
            default:
                $userId = $session['user_id'];
                break;
        }

        if ($isPrivileged) {

            $sql = "
                SELECT 
                    hs.id AS hardware_setting_id,
                    hs.assigned_door AS door_id,
                    gd.door_name,
                    gd.status,
                    lcd.liveImageURL AS videoThumbnail,
                    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
                WHERE hs.property_id = :property_id
            ";
            $params = [':property_id' => $propertyId];
        } else {

            $sql = "
                SELECT 
                    hs.id AS hardware_setting_id,
                    hs.assigned_door AS door_id,
                    gd.door_name,
                    gd.status,
                    pp.actions_to_perform,
                    lcd.liveImageURL AS videoThumbnail,
                    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
                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
            ";
            $params = [
                ':user_id'    => $userId,
                ':user_type'  => $userType,
                ':property_id'=> $propertyId
            ];
        }

        $results = fetchDataWithJoins($sql, $params);

        if (!$results) {
            echo json_encode([]);
            return;
        }
    
        $final = [];

        $useDefaultForAllDoors = false;
        $defaultActionsDecoded = null;

        if (!$isPrivileged) {

            $anyPerm = fetchSingleDataWithJoins("
                SELECT id
                FROM property_permissions
                WHERE user_id = :uid
                AND user_type = :ut
                AND property_id = :pid
                LIMIT 1
            ", [':uid' => $userId, ':ut' => $userType, ':pid' => $propertyId]);

            if (!$anyPerm) {
                $def = fetchSingleDataWithJoins("
                    SELECT actions_to_perform
                    FROM property_default_permissions
                    WHERE property_id = :pid
                    ORDER BY id DESC
                    LIMIT 1
                ", [':pid' => $propertyId]);

                if ($def && !empty($def['actions_to_perform'])) {
                    $parsed = self::parseMaybeEscapedJson($def['actions_to_perform'], true) ?? [];
                    $defaultsShape = ['Release' => false, 'Open' => false, 'Holdopen' => false];
                    $defaultActionsDecoded = array_merge(
                        $defaultsShape,
                        array_intersect_key($parsed, $defaultsShape)
                    );
                    $useDefaultForAllDoors = true;
                } else {
                    $defaultActionsDecoded = ['Release' => false, 'Open' => false, 'Holdopen' => false];
                    $useDefaultForAllDoors = true;
                }
            }
        }

        foreach ($results as $row) {
            if ($isPrivileged) {
                $actionsDecoded = ['Open' => true, 'Release' => true, 'Holdopen' => true];
                $canOpen = $canRelease = $canHold = true;
                $canViewVideo = true;

            } else {
                if ($useDefaultForAllDoors) {

                    $actionsDecoded = $defaultActionsDecoded;

                } else {
                    $raw = $row['actions_to_perform'] ?? '{}';
                    $parsed = self::parseMaybeEscapedJson($raw, true) ?? [];
                    $defaultsShape = ['Release' => false, 'Open' => false, 'Holdopen' => false];

                    $actionsDecoded = array_merge(
                        $defaultsShape,
                        array_intersect_key($parsed, $defaultsShape)
                    );
                }

                $canOpen    = !empty($actionsDecoded['Open']);
                $canRelease = !empty($actionsDecoded['Release']);
                $canHold    = !empty($actionsDecoded['Holdopen']);

                $canViewVideo = $this->hasCameraViewPermission(
                    $session,
                    $row['hardware_setting_id'],
                    $row['property_id']
                );
            }

            $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['door_id'] ?? ''),
                'name'  => $row['door_name'] ?? 'Unknown Door',
                'status'=> 'locked',
                'videoThumbnail' => $row['liveImageURL'] ?? '',
                'events'=> $events ?: [],
                'actions_to_perform' => $actionsDecoded,
                'permissions' => [
                    'canOpen'       => $canOpen,
                    'canRelease'    => $canRelease,
                    'canHold'       => $canHold,
                    'canViewEvents' => true,
                    'canViewVideo'  => (bool)$canViewVideo,
                ]
            ];
        }

        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;
        }

        $userType   = $session['user_type'];
        $propertyId = $this->getCurrentPropertyId($session);

        if (!$propertyId) {
            http_response_code(400);
            echo json_encode(['error' => ['message' => 'No property associated with user']]);
            return;
        }

        $privilegedTypes = [
            'super_admin','admin','dealer','dealer_admin',
            'organization_admin','sub_dealer','property_admin'
        ];
        $isPrivileged = in_array($userType, $privilegedTypes, true);

        $effectiveUserType = $userType;
        $effectiveUserId   = $session['user_id'];

        if ($userType === 'multi_prop') {
            $idToUse = fetchSingleDataWithJoins("
                SELECT id
                FROM multi_prop_users
                WHERE user_assoc = :uid
                AND property_id = :pid
                AND deleted_at IS NULL
                LIMIT 1
            ", [':uid' => $session['user_id'], ':pid' => $propertyId]);

            if (!$idToUse) {
                $idToUse = fetchSingleDataWithJoins("
                    SELECT id
                    FROM property_users
                    WHERE id = :uid
                    AND property_id = :pid
                    AND deleted_at IS NULL
                    LIMIT 1
                ", [':uid' => $session['user_id'], ':pid' => $propertyId]);
                $effectiveUserType = 'property_user';
            }
            $effectiveUserId = $idToUse['id'] ?? $session['user_id'];
        }

        if ($isPrivileged) {

            $sql = "
                SELECT 
                    gd.id AS id,
                    gd.door_name AS name,
                    gd.status,
                    gd.door_image,
                    lcd.liveImageURL AS videoThumbnail,
                    lcd.cameraName,
                    lcd.streamingUrl,
                    hs.id AS hardware_setting_id,
                    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
                WHERE hs.id = :door_id
                AND hs.property_id = :property_id
                LIMIT 1
            ";
            $params = [
                ':door_id'     => $id,
                ':property_id' => $propertyId,
            ];
        } else {

            $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,
                    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
                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
                AND hs.property_id = :property_id
                LIMIT 1
            ";
            $params = [
                ':user_id'     => $effectiveUserId,
                ':user_type'   => $effectiveUserType,
                ':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;
        }

        $useDefault = false;
        $defaultActions = ['Release' => false, 'Open' => false, 'Holdopen' => false];

        if (!$isPrivileged) {
            $anyPerm = fetchSingleDataWithJoins("
                SELECT id
                FROM property_permissions
                WHERE user_id     = :uid
                AND user_type   = :ut
                AND property_id = :pid
                LIMIT 1
            ", [':uid' => $effectiveUserId, ':ut' => $effectiveUserType, ':pid' => $propertyId]);

            if (!$anyPerm) {
                $def = fetchSingleDataWithJoins("
                    SELECT actions_to_perform
                    FROM property_default_permissions
                    WHERE property_id = :pid
                    ORDER BY id DESC
                    LIMIT 1
                ", [':pid' => $propertyId]);

                if ($def && !empty($def['actions_to_perform'])) {
                    $parsed = self::parseMaybeEscapedJson($def['actions_to_perform'], true) ?? [];

                    $defaultActions = array_merge(
                        $defaultActions,
                        array_intersect_key($parsed, $defaultActions)
                    );
                }
                $useDefault = true;
            }
        }

        $permissions = [
            'canOpen'       => false,
            'canRelease'    => false,
            'canHold'       => false,
            'canViewEvents' => true,
            'canViewVideo'  => false
        ];

        if ($isPrivileged) {
            $permissions['canOpen']      = true;
            $permissions['canRelease']   = true;
            $permissions['canHold']      = true;
            $permissions['canViewVideo'] = true;

        } else {
            if ($useDefault) {
                $permissions['canOpen']    = !empty($defaultActions['Open']);
                $permissions['canRelease'] = !empty($defaultActions['Release']);
                $permissions['canHold']    = !empty($defaultActions['Holdopen']);
            } else {
                if (!empty($door['actions_to_perform'])) {
                    $actions = self::parseMaybeEscapedJson($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'],
                $door['property_id']
            );
        }

        $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'         => 'locked',
            'streamingUrl'   => $door['streamingUrl'] ?? '',
            '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->getCurrentPropertyId($session);

            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);
            $actionIn     = $data['action'] ?? null;
            $durationMin  = $data['duration'] ?? null;

            if (!$actionIn) {
                http_response_code(400);
                echo json_encode(['error' => ['message' => 'Missing action']]);
                return;
            }

            $action = strtolower((string)$actionIn);
            if ($action === 'hold') $action = 'holdopen';
            if (!in_array($action, ['holdopen','open','release'], true)) {
                http_response_code(400);
                echo json_encode(['error' => ['message' => 'Unsupported action']]);
                return;
            }

            $validActions = ['open' => 2, 'holdopen' => 1, 'release' => 0];

            $durationSecOverride = null;
            if ($durationMin !== null && $durationMin !== '') {
                if (!is_numeric($durationMin)) {
                    http_response_code(400);
                    echo json_encode(['error' => ['message' => 'Invalid duration; must be minutes (number)']]);
                    return;
                }
                $durationSecOverride = max(0, (int)$durationMin) * 60;
            }

            $privilegedTypes = [
                'super_admin', 'admin', 'dealer', 'dealer_admin',
                'organization_admin', 'sub_dealer', 'property_admin'
            ];
            $isPrivileged = in_array($userType, $privilegedTypes, true);

            $effectiveUserId   = $userId;
            $effectiveUserType = $userType;

            if (!$isPrivileged && $userType === 'multi_prop') {
                $idToUse = fetchSingleDataWithJoins("
                    SELECT id
                    FROM multi_prop_users
                    WHERE user_assoc = :uid
                    AND property_id = :pid
                    AND deleted_at IS NULL
                    LIMIT 1
                ", [':uid' => $userId, ':pid' => $propertyId]);

                if ($idToUse && !empty($idToUse['id'])) {
                    $effectiveUserId = $idToUse['id'];
                    $effectiveUserType = 'multi_prop';
                } else {
                    $idToUse = fetchSingleDataWithJoins("
                        SELECT id
                        FROM property_users
                        WHERE id = :uid
                        AND property_id = :pid
                        AND deleted_at IS NULL
                        LIMIT 1
                    ", [':uid' => $userId, ':pid' => $propertyId]);

                    if ($idToUse && !empty($idToUse['id'])) {
                        $effectiveUserId = $idToUse['id'];
                        $effectiveUserType = 'property_user';
                    } else {
                        http_response_code(400);
                        echo json_encode(['error' => ['message' => 'No property associated with user']]);
                        return;
                    }
                }
            }

            if (!$isPrivileged) {

                $permRow = fetchSingleDataWithJoins("
                    SELECT pp.actions_to_perform
                    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
                    LIMIT 1
                ", [
                    ':door_id'    => $id,
                    ':user_id'    => $effectiveUserId,
                    ':user_type'  => $effectiveUserType,
                    ':property_id'=> $propertyId
                ]);

                $actions = null;
                $usingDefaults = false;

                if ($permRow && !empty($permRow['actions_to_perform'])) {
                    $actions = self::parseMaybeEscapedJson($permRow['actions_to_perform'], true);
                } else {
                    $anyPerm = fetchSingleDataWithJoins("
                        SELECT id
                        FROM property_permissions
                        WHERE user_id     = :uid
                        AND user_type   = :ut
                        AND property_id = :pid
                        LIMIT 1
                    ", [':uid' => $effectiveUserId, ':ut' => $effectiveUserType, ':pid' => $propertyId]);

                    if ($anyPerm) {
                        http_response_code(403);
                        echo json_encode(['error' => ['message' => 'Permission denied']]);
                        return;
                    }

                    $def = fetchSingleDataWithJoins("
                        SELECT actions_to_perform
                        FROM property_default_permissions
                        WHERE property_id = :pid
                        ORDER BY id DESC
                        LIMIT 1
                    ", [':pid' => $propertyId]);

                    if ($def && !empty($def['actions_to_perform'])) {
                        $actions = self::parseMaybeEscapedJson($def['actions_to_perform'], true);
                        $usingDefaults = true;
                    } else {
                        http_response_code(403);
                        echo json_encode(['error' => ['message' => 'Permission denied']]);
                        return;
                    }
                }

                $requiredActionKey = ucfirst($action);
                $allowed = (is_array($actions) && !empty($actions[$requiredActionKey]));
                if (!$allowed) {
                    http_response_code(403);
                    echo json_encode(['error' => ['message' => 'Action not allowed']]);
                    return;
                }
            }

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

            if (!$hardware) {
                http_response_code(500);
                echo json_encode(['error' => ['message' => 'Hardware not found']]);
                return;
            }

            $deviceType   = $hardware['device_type'] ?? 'relay';
            $commandValue = $validActions[$action];
            $controlUrl   = null;

            switch ($deviceType) {
                case 'SMSIO': {
                    $stream_url = fetchDataSingle("stream_url");
                    if (empty($stream_url)) {
                        http_response_code(400);
                        echo json_encode(['error' => ['message' => 'SMSIO control not yet configured']]);
                        return;
                    }

                    $signalwire_settings = fetchData('signalwire_settings', ['is_property' => 1, 'user_id' => $propertyId]);
                    if (empty($signalwire_settings)) {
                        http_response_code(400);
                        echo json_encode(['error' => ['message' => 'Signalwire settings to send SMS not found']]);
                        return;
                    }
                    $signalwire_settings = $signalwire_settings[0];

                    $message_body = "";
                    if ($hardware['model'] === "UC300") {
                        switch ($action) {
                            case 'holdopen': {
                                $seconds = $durationSecOverride ?? 0;
                                $message_body = $hardware['password'] . ';' . $hardware['output'] . ',1,' . $seconds;
                                break;
                            }
                            case 'open': {
                                $seconds = $durationSecOverride ?? (int)$hardware['sms_duration'];
                                if ($seconds <= 0) $seconds = 1;
                                $message_body = $hardware['password'] . ';' . $hardware['output'] . ',1,' . $seconds;
                                break;
                            }
                            case 'release': {
                                $message_body = $hardware['password'] . ';' . $hardware['output'] . ',0,0';
                                break;
                            }
                        }
                    } elseif ($hardware['model'] === "UC1414") {
                        if ($action === "holdopen") {
                            $message_body = $hardware['output'] . ' hold open, Reply-STOP-to-opt-out';
                        } elseif ($action === "open") {
                            $message_body = $hardware['output'] . ' momentary, Reply-STOP-to-opt-out';
                        } elseif ($action === "release") {
                            $message_body = $hardware['output'] . ' release, Reply-STOP-to-opt-out';
                        }
                    }

                    $payload = [
                        "from_number" => $hardware['invisible_intercom_control_number'],
                        "to_number"   => $hardware['sms_io_number'],
                        "message_body"=> $message_body,
                        "project_id"  => $signalwire_settings['project_id'],
                        "api_token"   => $signalwire_settings['api_token'],
                        "space_url"   => $signalwire_settings['space_url']
                    ];
                    $baseUrl    = str_replace('\/', '/', $stream_url['stream_url'] ?? '');
                    $requestURL = $baseUrl . '/api/send_sms_to_open_door';
                    $controlUrl = $requestURL;

                    $curl = curl_init();
                    curl_setopt_array($curl, [
                        CURLOPT_URL            => $requestURL,
                        CURLOPT_RETURNTRANSFER => true,
                        CURLOPT_ENCODING       => '',
                        CURLOPT_MAXREDIRS      => 10,
                        CURLOPT_POST           => true,
                        CURLOPT_POSTFIELDS     => json_encode($payload),
                        CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
                        CURLOPT_SSL_VERIFYPEER => false,
                        CURLOPT_SSL_VERIFYHOST => false
                    ]);
                    $response = curl_exec($curl);
                    $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
                    $error    = curl_error($curl);
                    curl_close($curl);

                    if ($error || $httpCode >= 400) {
                        http_response_code(500);
                        echo json_encode(['error' => ['message' => 'Server Error. Command SMS could not be sent. Please try again']]);
                        return;
                    }

                    break;
                }

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

                    if ($action === "holdopen") {
                        $pulseTime = $durationSecOverride ?? (
                            ((int)$hardware['sms_duration'] > 0)
                                ? (int)$hardware['sms_duration'] * 60
                                : 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}";
                    }

                    $p    = parse_url($controlUrl);
                    $user = isset($p['user']) ? rawurldecode($p['user']) : null;
                    $pass = isset($p['pass']) ? rawurldecode($p['pass']) : null;

                    $clean = ($p['scheme'] ?? 'http') . '://' . $p['host']
                        . (isset($p['port'])  ? ':' . $p['port']  : '')
                        . (isset($p['path'])  ? $p['path']       : '')
                        . (isset($p['query']) ? '?' . $p['query'] : '');

                    $ch = curl_init();

                    $headers = [
                        'User-Agent: RelayControl/1.0',
                        'Accept: application/json,*/*;q=0.5',
                        'Connection: close',
                        'Expect:',
                    ];

                    curl_setopt_array($ch, [
                        CURLOPT_URL            => $clean,
                        CURLOPT_RETURNTRANSFER => true,
                        CURLOPT_CONNECTTIMEOUT => 6,
                        CURLOPT_TIMEOUT        => 10,
                        CURLOPT_FOLLOWLOCATION => true,
                        CURLOPT_UNRESTRICTED_AUTH => true,
                        CURLOPT_IPRESOLVE      => CURL_IPRESOLVE_V4,
                        CURLOPT_HTTPHEADER     => $headers,
                        CURLOPT_HTTPAUTH       => CURLAUTH_BASIC | CURLAUTH_DIGEST,
                    ]);

                    if ($user !== null && $pass !== null) {
                        curl_setopt($ch, CURLOPT_USERPWD, $user . ':' . $pass);
                    }

                    $response = curl_exec($ch);
                    $err      = curl_error($ch);
                    $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
                    curl_close($ch);

                    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;
            }

            $snapped   = storeSnapshotImage($id, $propertyId);
            $file_name = $snapped['filename'] ?? null;

            $status = ($action === 'holdopen' || $action === 'open') ? 'unlocked' : 'locked';

            updateTable('gate_doors', ['status' => $status], 'id = :id', ['id' => $id]);

            $hs = fetchSingleDataWithJoins("
                SELECT hs.id
                FROM hardwares_settings hs
                WHERE hs.assigned_door = :door_id
                ORDER BY hs.id DESC
                LIMIT 1
            ", [':door_id' => $id]);

            $eventOf = $hs ? (int)$hs['id'] : null;

            if ($eventOf !== null) {
                insertData('hardwares_settings_events', [
                    'eventName'   => ucfirst($action), // Open / Release / Holdopen
                    'eventType'   => ($deviceType === 'SMSIO' ? 'SMS IO' : 'door_control'),
                    'eventOf'     => $eventOf,
                    'property_id' => $propertyId,
                    'URL'         => $controlUrl,
                    'eventDetail' => "Mobile App Triggered Command",
                    'created_by'  => $userId,
                    'created_at'  => date('Y-m-d H:i:s'),
                    'timestamp'   => date('Y-m-d H:i:s'),
                    'file_name'   => $file_name,
                ]);
            } else {
                error_log("events: no hardwares_settings row for door_id={$id}");
            }

            $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' => $durationMin,
                '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,
                lcd.liveImageURL AS videoThumbnail,
                lcd.liveImageURL,
                lcd.cameraName
            FROM gate_doors gd
            LEFT JOIN live_camera_streaming_data lcd ON gd.camera_id = lcd.id
            WHERE gd.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->getCurrentPropertyId($session);
        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;
        }

        $imageUrl = $_POST['imageUrl'] ?? null;
        if (!$imageUrl || !filter_var($imageUrl, FILTER_VALIDATE_URL) || !preg_match('#^https?://#i', $imageUrl)) {
            http_response_code(400);
            echo json_encode(['error' => 'Invalid or missing imageUrl']);
            return;
        }

        $p = parse_url($imageUrl);
        $host = $p['host'] ?? '';
        if (!$host) {
            http_response_code(400);
            echo json_encode(['error' => 'Invalid URL host']);
            return;
        }

        $cleanUrl  = ($p['scheme'] ?? 'http') . '://' . $host;
        if (isset($p['port']))  $cleanUrl .= ':' . $p['port'];
        if (isset($p['path']))  $cleanUrl .= $p['path'];
        if (isset($p['query'])) $cleanUrl .= '?' . $p['query'];

        $ip = gethostbyname($host);
        if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
            http_response_code(400);
            echo json_encode(['error' => 'Disallowed target']);
            return;
        }

        $user = $p['user'] ?? null;
        $pass = $p['pass'] ?? null;

        $tries = [
            ['basic',  CURL_HTTP_VERSION_1_1],
            ['basic',  CURL_HTTP_VERSION_1_0],
            ['digest', CURL_HTTP_VERSION_1_1],
            ['digest', CURL_HTTP_VERSION_1_0],
        ];

        $data = null;
        $code = 0;
        $ctype = null;

        foreach ($tries as [$mode, $httpver]) {
            $ch = curl_init($cleanUrl);

            $headers = [
                'User-Agent: SnapshotFetcher/1.0',
                'Connection: close',
                'Expect:',
                'Accept: image/*,*/*;q=0.5',
            ];

            $opts = [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_CONNECTTIMEOUT => 8,
                CURLOPT_TIMEOUT        => 8,
                CURLOPT_FOLLOWLOCATION => false,
                CURLOPT_SSL_VERIFYPEER => false,
                CURLOPT_SSL_VERIFYHOST => 0,
                CURLOPT_IPRESOLVE      => CURL_IPRESOLVE_V4,
                CURLOPT_HTTP_VERSION   => $httpver,
                CURLOPT_HTTPHEADER     => $headers,
            ];

            if ($user !== null && $pass !== null) {
                if ($mode === 'basic') {
                    $headers[] = 'Authorization: Basic ' . base64_encode($user . ':' . $pass);
                    $opts[CURLOPT_HTTPHEADER] = $headers;
                    $opts[CURLOPT_USERPWD]    = $user . ':' . $pass;
                    $opts[CURLOPT_HTTPAUTH]   = CURLAUTH_BASIC;
                } else {
                    $opts[CURLOPT_USERPWD]  = $user . ':' . $pass;
                    $opts[CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
                }
            }

            curl_setopt_array($ch, $opts);
            $body  = curl_exec($ch);
            $code  = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $ctype = curl_getinfo($ch, CURLINFO_CONTENT_TYPE) ?: null;
            $err   = curl_error($ch);
            curl_close($ch);

            if ($code === 200 && $body !== false && strlen($body) > 20) {
                $data  = $body;
                break;
            }

        }

        if ($data === null) {
            if ($code === 401) {
                http_response_code(401);
                echo json_encode(['error' => 'Unauthorized (camera rejected credentials)']);
            } else {
                http_response_code(502);
                echo json_encode(['error' => 'Unable to fetch snapshot', 'status' => $code]);
            }
            return;
        }

        if (!$ctype || stripos($ctype, 'image/') !== 0) {
            if (@getimagesizefromstring($data) === false) {
                http_response_code(400);
                echo json_encode(['error' => 'URL did not return a valid image']);
                return;
            }
            $ctype = 'image/jpeg';
        }

        header('Content-Type: application/json; charset=utf-8');
        echo json_encode([
            'image' => base64_encode($data),
            'mime'  => $ctype,
        ]);
    }     
}

$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']]);
}