/*
 * Copyright (C) 2023-2026 Intel Corporation
 *
 * SPDX-License-Identifier: MIT
 *
 */

#pragma once

#include "shared/source/helpers/non_copyable_or_moveable.h"

#include "level_zero/sysman/source/api/events/sysman_os_events.h"
#include "level_zero/sysman/source/shared/linux/udev/udev_lib.h"

#include <map>
#include <mutex>
#include <poll.h>
#include <vector>

namespace L0 {
namespace Sysman {

class DrmNlApi;
class LinuxSysmanDriverImp;
class LinuxSysmanImp;
class Ras;
class UdevLib;
class FsAccessInterface;
struct DrmRasEvent;
struct OsSysman;
struct SysmanDeviceImp;

enum class PollSourceType { udev,
                            pipe,
                            netlink };

struct PollDescriptor {
    struct pollfd pfd;
    PollSourceType type;
};

class LinuxEventsImp : public OsEvents, NEO::NonCopyableAndNonMovableClass {
  public:
    bool eventListen(zes_event_type_flags_t &pEvent, uint64_t timeout) override;
    ze_result_t eventRegister(zes_event_type_flags_t events) override;
    LinuxEventsImp() = delete;
    LinuxEventsImp(OsSysman *pOsSysman);
    ~LinuxEventsImp() override = default;

  protected:
    LinuxSysmanImp *pLinuxSysmanImp = nullptr;
};

class LinuxEventsUtil {

  public:
    LinuxEventsUtil() = delete;
    LinuxEventsUtil(LinuxSysmanDriverImp *pOsSysmanDriverImp);
    ~LinuxEventsUtil() = default;

    ze_result_t eventsListen(uint64_t timeout, uint32_t count, zes_device_handle_t *phDevices, uint32_t *pNumDeviceEvents, zes_event_type_flags_t *pEvents);
    void eventRegister(zes_event_type_flags_t events, SysmanDeviceImp *pSysmanDevice);

  protected:
    UdevLib *pUdevLib = nullptr;
    DrmNlApi *pDrmNl = nullptr;
    LinuxSysmanDriverImp *pLinuxSysmanDriverImp = nullptr;
    int pipeFd[2] = {-1, -1};
    std::map<SysmanDeviceImp *, zes_event_type_flags_t> deviceEventsMap;
    bool checkRasEvent(zes_event_type_flags_t &pEvent, SysmanDeviceImp *pSysmanDeviceImp, zes_event_type_flags_t registeredEvents);
    bool isResetRequired(void *dev, zes_event_type_flags_t &pEvent);
    bool checkDeviceDetachEvent(zes_event_type_flags_t &pEvent);
    bool checkDeviceAttachEvent(zes_event_type_flags_t &pEvent);
    bool checkIfMemHealthChanged(void *dev, zes_event_type_flags_t &pEvent);
    bool checkIfFabricPortStatusChanged(void *dev, zes_event_type_flags_t &pEvent);
    bool checkDeviceWedgedEvent(void *dev, zes_event_type_flags_t &pEvent);
    bool listenSystemEvents(zes_event_type_flags_t *pEvents, uint32_t count, std::vector<zes_event_type_flags_t> &registeredEvents, zes_device_handle_t *phDevices, uint64_t timeout);
    bool checkDeviceEvents(std::vector<zes_event_type_flags_t> &registeredEvents, const std::map<uint32_t, std::string> &mapOfDevIndexToDevPath, FsAccessInterface *pFsAccess, zes_event_type_flags_t *pEvents, void *dev, zes_device_handle_t *phDevices);
    void getDevIndexToDevPathMap(std::vector<zes_event_type_flags_t> &registeredEvents, uint32_t count, zes_device_handle_t *phDevices, std::map<uint32_t, std::string> &mapOfDevIndexToDevPath, FsAccessInterface *&pFsAccess);
    bool handleNetlinkEvents(zes_event_type_flags_t *pEvents, uint32_t count, zes_device_handle_t *phDevices, const std::vector<zes_event_type_flags_t> &registeredEvents);
    bool processNetlinkRasEvent(const DrmRasEvent &netlinkEvent, zes_event_type_flags_t *pEvents, uint32_t count, zes_device_handle_t *phDevices, const std::vector<zes_event_type_flags_t> &registeredEvents);
    static bool isSurvivabilityModeAsExpected(FsAccessInterface *pFsAccess, const std::string &devPath, const std::string &mode);
    std::string action;

  private:
    static const std::string add;
    static const std::string remove;
    static const std::string change;
    static const std::string unbind;
    static const std::string bind;
    static bool checkRasEventOccured(Ras *rasHandle);
    std::once_flag initEventsOnce;
    std::once_flag initNetlinkOnce;
    std::mutex eventsMutex;

  protected:
    void init();
    void initNetlink();
};

} // namespace Sysman
} // namespace L0
