#include "networkstreammodel.h"
#include <QBrush>
#include <QColor>

NetworkStreamModel::NetworkStreamModel(Store *store, QObject *parent) : QAbstractItemModel(parent), m_store(store)
{
  connect(m_store, &Store::networksChanged, this, [this]() {
    beginResetModel();
    endResetModel();
  });
  connect(m_store, &Store::streamsChanged, this, [this]() {
    beginResetModel();
    endResetModel();
  });

  // Granular update: repaint a single stream row when its properties change (e.g. unseen count)
  connect(m_store, &Store::streamUpdated, this, [this](const QString &streamId) {
    const auto &networks = m_store->networks();
    for (int ni = 0; ni < networks.size(); ++ni)
    {
      const auto &streams = networks.at(ni).streams;
      for (int si = 0; si < static_cast<int>(streams.size()); ++si)
      {
        if (streams[si].id == streamId)
        {
          // Build indices for both columns of this stream row
          QModelIndex topLeft = index(si, 0, index(ni, 0));
          QModelIndex bottomRight = index(si, 1, index(ni, 0));
          emit dataChanged(topLeft, bottomRight, {Qt::DisplayRole, Qt::ForegroundRole});
          return;
        }
      }
    }
  });
}

QModelIndex NetworkStreamModel::index(int row, int column, const QModelIndex &parent) const
{
  if (!hasIndex(row, column, parent))
  {
    return QModelIndex();
  }

  if (!parent.isValid())
  {
    // Top-level item (Network)
    // Encode: networkIndex in lower 32 bits, -1 in upper 32 bits
    quintptr id = static_cast<quintptr>(row) | (static_cast<quintptr>(0xFFFFFFFF) << 32);
    return createIndex(row, column, id);
  }
  else
  {
    // Child item (Stream)
    // Decode parent to get network index
    quintptr parentId = parent.internalId();
    int networkIndex = static_cast<int>(parentId & 0xFFFFFFFF);

    // Encode: streamIndex in lower 32 bits, networkIndex in upper 32 bits
    quintptr id = static_cast<quintptr>(row) | (static_cast<quintptr>(networkIndex) << 32);
    return createIndex(row, column, id);
  }
}

QModelIndex NetworkStreamModel::parent(const QModelIndex &child) const
{
  if (!child.isValid())
  {
    return QModelIndex();
  }

  quintptr id = child.internalId();
  int networkIndex = static_cast<int>((id >> 32) & 0xFFFFFFFF);

  // If networkIndex is 0xFFFFFFFF, this is a network item (no parent)
  if (networkIndex == 0xFFFFFFFF)
  {
    return QModelIndex();
  }

  // This is a stream item, return its parent network
  quintptr parentId = static_cast<quintptr>(networkIndex) | (static_cast<quintptr>(0xFFFFFFFF) << 32);
  return createIndex(networkIndex, 0, parentId);
}

int NetworkStreamModel::rowCount(const QModelIndex &parent) const
{
  if (!parent.isValid())
  {
    // Root level - return number of networks
    return m_store->networkCount();
  }

  quintptr id = parent.internalId();
  int networkIndex = static_cast<int>((id >> 32) & 0xFFFFFFFF);

  // If this is a network item, return number of streams
  if (networkIndex == 0xFFFFFFFF)
  {
    int actualNetworkIndex = static_cast<int>(id & 0xFFFFFFFF);
    if (actualNetworkIndex >= 0 && actualNetworkIndex < m_store->networks().size())
    {
      return static_cast<int>(m_store->networks().at(actualNetworkIndex).streams.size());
    }
  }

  // Stream items have no children
  return 0;
}

int NetworkStreamModel::columnCount(const QModelIndex &parent) const
{
  Q_UNUSED(parent);
  return 2; // Column 0: name, Column 1: unseen count
}

QVariant NetworkStreamModel::data(const QModelIndex &index, int role) const
{
  if (!index.isValid())
  {
    return QVariant();
  }

  quintptr id = index.internalId();
  int networkIndex = static_cast<int>((id >> 32) & 0xFFFFFFFF);

  if (networkIndex == 0xFFFFFFFF)
  {
    int actualNetworkIndex = static_cast<int>(id & 0xFFFFFFFF);
    // This is a network item
    if (role == Qt::DisplayRole && index.column() == 0)
    {
      if (actualNetworkIndex >= 0 && actualNetworkIndex < m_store->networks().size())
      {
        return m_store->networks().at(actualNetworkIndex).name.toUpper();
      }
    } else if (role == Qt::DisplayRole && index.column() == 1)
    {
      if (actualNetworkIndex >= 0 && actualNetworkIndex < m_store->networks().size())
      {
        auto net = m_store->networks().at(actualNetworkIndex);
        return QString("%1/%2").arg(net.openStreamCount).arg(net.openStreamCapacity);
      }
    }
    // Networks don't show unseen count in column 1
    return QVariant();
  }
  else
  {
    // This is a stream item
    int streamIndex = static_cast<int>(id & 0xFFFFFFFF);
    if (networkIndex >= 0 && networkIndex < m_store->networks().size())
    {
      const auto &streams = m_store->networks().at(networkIndex).streams;

      if (streamIndex >= 0 && streamIndex < static_cast<int>(streams.size()))
      {
        const Stream &stream = streams.at(streamIndex);

        if (role == Qt::DisplayRole)
        {
          if (index.column() == 0)
          {
            // Column 0: stream name
            return stream.name;
          }
          else if (index.column() == 1)
          {
            // Column 1: unseen count (only show if > 0)
            if (stream.unseenCount > 0)
            {
              return stream.unseenCount;
            }
          }
        }
        else if (role == Qt::TextAlignmentRole && index.column() == 1)
        {
          // Right-align the unseen count
          return static_cast<int>(Qt::AlignRight | Qt::AlignVCenter);
        }
        else if (role == Qt::ForegroundRole)
        {
          // Make streams with no unseen items grey (fully caught up)
          if (stream.unseenCount == 0)
          {
            return QBrush(QColor(128, 128, 128));
          }
          // Streams with unseen items use default color
        }
      }
    }
  }

  return QVariant();
}

QString NetworkStreamModel::streamIdFromIndex(const QModelIndex &index) const
{
  if (!index.isValid())
  {
    return QString();
  }

  quintptr id = index.internalId();
  int networkIndex = static_cast<int>((id >> 32) & 0xFFFFFFFF);

  // Check if this is a stream item (not a network)
  if (networkIndex != 0xFFFFFFFF)
  {
    int streamIndex = static_cast<int>(id & 0xFFFFFFFF);
    if (networkIndex >= 0 && networkIndex < m_store->networks().size())
    {
      const auto &streams = m_store->networks().at(networkIndex).streams;

      if (streamIndex >= 0 && streamIndex < static_cast<int>(streams.size()))
      {
        return streams.at(streamIndex).id;
      }
    }
  }

  return QString();
}

QString NetworkStreamModel::networkIdFromIndex(const QModelIndex &index) const
{
  if (!index.isValid())
  {
    return QString();
  }

  quintptr id = index.internalId();
  int networkIndex = static_cast<int>((id >> 32) & 0xFFFFFFFF);

  // Check if this is a stream item (not a network)
  if (networkIndex == 0xFFFFFFFF)
  {
    int actualNetworkIndex = static_cast<int>(id & 0xFFFFFFFF);
    if (actualNetworkIndex >= 0 && actualNetworkIndex < m_store->networks().size())
    {
      return m_store->networks().at(actualNetworkIndex).id;
    }
  }

  return QString();
}

bool NetworkStreamModel::isStreamItem(const QModelIndex &index) const
{
  if (!index.isValid())
  {
    return false;
  }

  quintptr id = index.internalId();
  int networkIndex = static_cast<int>((id >> 32) & 0xFFFFFFFF);

  // If networkIndex is not 0xFFFFFFFF, this is a stream item
  return networkIndex != 0xFFFFFFFF;
}
