import QtQuick
import QtQuick.Controls
import QtQuick.Templates as T
import qs.Commons

Item {
  id: root

  // Signal for key press events when keyNavigationEnabled is true
  signal keyPressed(var event)

  property color handleColor: Qt.alpha(Color.mHover, 0.8)
  property color handleHoverColor: handleColor
  property color handlePressedColor: handleColor
  property color trackColor: "transparent"
  property real handleWidth: 6
  property real handleRadius: Style.iRadiusM
  property int verticalPolicy: ScrollBar.AsNeeded
  property int horizontalPolicy: ScrollBar.AlwaysOff
  readonly property bool verticalScrollBarActive: {
    if (gridView.ScrollBar.vertical.policy === ScrollBar.AlwaysOff)
      return false;
    return gridView.contentHeight > gridView.height;
  }
  readonly property bool contentOverflows: gridView.contentHeight > gridView.height

  // Gradient properties
  property bool showGradientMasks: true
  property color gradientColor: Color.mSurfaceVariant
  property int gradientHeight: 16
  property bool reserveScrollbarSpace: true

  // Keep scrollbars visible whenever overflow exists (without forcing visibility when not scrollable)
  property bool showScrollbarWhenScrollable: Settings.data.ui.scrollbarAlwaysVisible

  // Available width for content (excludes scrollbar space when reserveScrollbarSpace is true)
  // Note: Always reserves space when enabled to avoid binding loops with cellWidth calculations
  readonly property real availableWidth: width - (reserveScrollbarSpace ? handleWidth + Style.marginXS : 0)

  // Expose activeFocus from internal gridView
  readonly property bool hasActiveFocus: gridView.activeFocus

  // Forward GridView properties
  property alias model: gridView.model
  property alias delegate: gridView.delegate
  property alias cellWidth: gridView.cellWidth
  property alias cellHeight: gridView.cellHeight
  property alias leftMargin: gridView.leftMargin
  property alias rightMargin: gridView.rightMargin
  property alias topMargin: gridView.topMargin
  property alias bottomMargin: gridView.bottomMargin
  property alias currentIndex: gridView.currentIndex
  property alias count: gridView.count
  property alias contentHeight: gridView.contentHeight
  property alias contentWidth: gridView.contentWidth
  property alias contentY: gridView.contentY
  property alias contentX: gridView.contentX
  property alias currentItem: gridView.currentItem
  property alias highlightItem: gridView.highlightItem
  property alias highlightFollowsCurrentItem: gridView.highlightFollowsCurrentItem
  property alias preferredHighlightBegin: gridView.preferredHighlightBegin
  property alias preferredHighlightEnd: gridView.preferredHighlightEnd
  property alias highlightRangeMode: gridView.highlightRangeMode
  property alias snapMode: gridView.snapMode
  property alias keyNavigationEnabled: gridView.keyNavigationEnabled
  property alias keyNavigationWraps: gridView.keyNavigationWraps
  property alias cacheBuffer: gridView.cacheBuffer
  property alias displayMarginBeginning: gridView.displayMarginBeginning
  property alias displayMarginEnd: gridView.displayMarginEnd
  property alias layoutDirection: gridView.layoutDirection
  property alias effectiveLayoutDirection: gridView.effectiveLayoutDirection
  property alias flow: gridView.flow
  property alias boundsBehavior: gridView.boundsBehavior
  property alias flickableDirection: gridView.flickableDirection
  property alias interactive: gridView.interactive
  property alias moving: gridView.moving
  property alias flicking: gridView.flicking
  property alias dragging: gridView.dragging
  property alias horizontalVelocity: gridView.horizontalVelocity
  property alias verticalVelocity: gridView.verticalVelocity
  property alias reuseItems: gridView.reuseItems

  // Animate items when the model is reordered (e.g. ListModel.move())
  property bool animateMovement: false

  // Scroll speed multiplier for mouse wheel (1.0 = default, higher = faster)
  property real wheelScrollMultiplier: 2.0
  property int smoothWheelAnimationDuration: Style.animationNormal
  property real _wheelTargetY: 0

  function clampScrollY(value) {
    return Math.max(0, Math.min(value, gridView.contentHeight - gridView.height));
  }

  function applyWheelScroll(delta) {
    if (!root.contentOverflows)
      return;

    const step = delta * root.wheelScrollMultiplier;

    if (!Settings.data.general.smoothScrollEnabled || Settings.data.general.animationDisabled) {
      gridView.contentY = root.clampScrollY(gridView.contentY - step);
      root._wheelTargetY = gridView.contentY;
      return;
    }

    if (!wheelScrollAnimation.running)
      root._wheelTargetY = gridView.contentY;

    root._wheelTargetY = root.clampScrollY(root._wheelTargetY - step);
    wheelScrollAnimation.to = root._wheelTargetY;
    wheelScrollAnimation.restart();
  }

  function animateToContentY(targetY) {
    const clampedY = root.clampScrollY(targetY);

    if (!Settings.data.general.smoothScrollEnabled || Settings.data.general.animationDisabled || gridView.dragging || gridView.flicking) {
      gridView.contentY = clampedY;
      root._wheelTargetY = clampedY;
      return;
    }

    root._wheelTargetY = clampedY;
    wheelScrollAnimation.to = clampedY;
    wheelScrollAnimation.restart();
  }

  // Track selection index for gradient visibility (set externally)
  property int trackedSelectionIndex: -1

  // Check if selection is on first visible row
  readonly property bool selectionOnFirstVisibleRow: {
    if (trackedSelectionIndex < 0 || cellHeight <= 0 || cellWidth <= 0)
      return false;

    // Calculate columns per row
    var cols = Math.floor(gridView.width / cellWidth);
    if (cols <= 0)
      cols = 1;

    // Calculate the row of the selection
    var selectionRow = Math.round(trackedSelectionIndex / cols);

    // Calculate the first visible row
    var firstVisibleRow = Math.round(gridView.contentY / cellHeight);

    return selectionRow === firstVisibleRow;
  }

  // Check if selection is on last visible row
  readonly property bool selectionOnLastVisibleRow: {
    if (trackedSelectionIndex < 0 || cellHeight <= 0 || cellWidth <= 0)
      return false;

    // Calculate columns per row
    var cols = Math.floor(gridView.width / cellWidth);
    if (cols <= 0)
      cols = 1;

    // Calculate the row of the selection
    var selectionRow = Math.round(trackedSelectionIndex / cols);

    // Calculate the last visible row (might be partially visible)
    var lastVisibleRow = Math.round((gridView.contentY + gridView.height - 1) / cellHeight);

    return selectionRow === lastVisibleRow;
  }

  // Forward GridView methods
  function positionViewAtIndex(index, mode) {
    const shouldAnimate = mode === GridView.Contain;
    if (!shouldAnimate) {
      gridView.positionViewAtIndex(index, mode);
      root._wheelTargetY = gridView.contentY;
      return;
    }

    const previousY = gridView.contentY;
    gridView.positionViewAtIndex(index, mode);
    const targetY = root.clampScrollY(gridView.contentY);

    if (Math.abs(targetY - previousY) < 0.5) {
      root._wheelTargetY = targetY;
      return;
    }

    gridView.contentY = previousY;
    root._wheelTargetY = previousY;
    root.animateToContentY(targetY);
  }

  function positionViewAtBeginning() {
    gridView.positionViewAtBeginning();
  }

  function positionViewAtEnd() {
    gridView.positionViewAtEnd();
  }

  function forceLayout() {
    gridView.forceLayout();
  }

  function forceActiveFocus() {
    gridView.forceActiveFocus();
  }

  function cancelFlick() {
    gridView.cancelFlick();
  }

  function flick(xVelocity, yVelocity) {
    gridView.flick(xVelocity, yVelocity);
  }

  function incrementCurrentIndex() {
    gridView.incrementCurrentIndex();
  }

  function decrementCurrentIndex() {
    gridView.decrementCurrentIndex();
  }

  function indexAt(x, y) {
    return gridView.indexAt(x, y);
  }

  function itemAt(x, y) {
    return gridView.itemAt(x, y);
  }

  function itemAtIndex(index) {
    return gridView.itemAtIndex(index);
  }

  function moveCurrentIndexUp() {
    gridView.moveCurrentIndexUp();
  }

  function moveCurrentIndexDown() {
    gridView.moveCurrentIndexDown();
  }

  function moveCurrentIndexLeft() {
    gridView.moveCurrentIndexLeft();
  }

  function moveCurrentIndexRight() {
    gridView.moveCurrentIndexRight();
  }

  // Set reasonable implicit sizes for Layout usage
  implicitWidth: 200
  implicitHeight: 200

  Component.onCompleted: {
    _wheelTargetY = gridView.contentY;
    createGradients();
  }

  // Dynamically create gradient overlays
  function createGradients() {
    if (!showGradientMasks)
      return;

    Qt.createQmlObject(`
      import QtQuick
      import qs.Commons
      Rectangle {
        x: 0
        y: 0
        width: root.availableWidth
        height: root.gradientHeight
        z: 1
        visible: root.showGradientMasks && root.contentOverflows
        opacity: (gridView.contentY <= 1 || root.selectionOnFirstVisibleRow) ? 0 : 1
        Behavior on opacity {
          NumberAnimation { duration: Style.animationFast; easing.type: Easing.InOutQuad }
        }
        gradient: Gradient {
          GradientStop { position: 0.0; color: root.gradientColor }
          GradientStop { position: 1.0; color: "transparent" }
        }
      }
    `, root, "topGradient");

    Qt.createQmlObject(`
      import QtQuick
      import qs.Commons
      Rectangle {
        x: 0
        anchors.bottom: parent.bottom
        anchors.bottomMargin: -1
        width: root.availableWidth
        height: root.gradientHeight + 1
        z: 1
        visible: root.showGradientMasks && root.contentOverflows
        opacity: ((gridView.contentY + gridView.height >= gridView.contentHeight - 1) || root.selectionOnLastVisibleRow) ? 0 : 1
        Behavior on opacity {
          NumberAnimation { duration: Style.animationFast; easing.type: Easing.InOutQuad }
        }
        gradient: Gradient {
          GradientStop { position: 0.0; color: "transparent" }
          GradientStop { position: 1.0; color: root.gradientColor }
        }
      }
    `, root, "bottomGradient");
  }

  GridView {
    id: gridView
    anchors.fill: parent
    anchors.rightMargin: root.reserveScrollbarSpace ? root.handleWidth + Style.marginXS : 0

    move: root.animateMovement ? moveTransitionImpl : null
    displaced: root.animateMovement ? displacedTransitionImpl : null

    Transition {
      id: moveTransitionImpl
      NumberAnimation {
        properties: "x,y"
        duration: Style.animationNormal
        easing.type: Easing.InOutQuad
      }
    }
    Transition {
      id: displacedTransitionImpl
      NumberAnimation {
        properties: "x,y"
        duration: Style.animationNormal
        easing.type: Easing.InOutQuad
      }
    }

    // Enable clipping to keep content within bounds
    clip: true

    // Enable flickable for smooth scrolling
    boundsBehavior: Flickable.StopAtBounds

    NumberAnimation {
      id: wheelScrollAnimation
      target: gridView
      property: "contentY"
      duration: root.smoothWheelAnimationDuration
      easing.type: Easing.OutCubic
    }

    onDraggingChanged: {
      if (dragging) {
        wheelScrollAnimation.stop();
        root._wheelTargetY = contentY;
      }
    }

    onFlickingChanged: {
      if (flicking) {
        wheelScrollAnimation.stop();
        root._wheelTargetY = contentY;
      }
    }

    onContentHeightChanged: root._wheelTargetY = root.clampScrollY(root._wheelTargetY)
    onHeightChanged: root._wheelTargetY = root.clampScrollY(root._wheelTargetY)

    // Focus handling depends on keyNavigationEnabled
    focus: keyNavigationEnabled
    activeFocusOnTab: keyNavigationEnabled

    // Emit keyPressed signal for custom key handling
    Keys.onPressed: event => {
                      if (keyNavigationEnabled) {
                        root.keyPressed(event);
                      }
                    }

    WheelHandler {
      enabled: root.wheelScrollMultiplier !== 1.0
      acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
      onWheel: event => {
                 const delta = event.pixelDelta.y !== 0 ? event.pixelDelta.y : event.angleDelta.y / 2;
                 root.applyWheelScroll(delta);
                 event.accepted = true;
               }
    }

    ScrollBar.vertical: ScrollBar {
      parent: root
      x: root.mirrored ? 0 : root.width - width
      y: 0
      height: root.height
      policy: root.verticalPolicy

      contentItem: Rectangle {
        implicitWidth: root.handleWidth
        implicitHeight: 100
        radius: root.handleRadius
        color: parent.pressed ? root.handlePressedColor : parent.hovered ? root.handleHoverColor : root.handleColor
        opacity: parent.policy === ScrollBar.AlwaysOn ? 1.0 : root.verticalScrollBarActive ? ((root.showScrollbarWhenScrollable || parent.active) ? 1.0 : 0.0) : 0.0

        Behavior on opacity {
          NumberAnimation {
            duration: Style.animationFast
          }
        }

        Behavior on color {
          ColorAnimation {
            duration: Style.animationFast
          }
        }
      }

      background: Rectangle {
        implicitWidth: root.handleWidth
        implicitHeight: 100
        color: root.trackColor
        opacity: parent.policy === ScrollBar.AlwaysOn ? 0.3 : root.verticalScrollBarActive ? ((root.showScrollbarWhenScrollable || parent.active) ? 0.3 : 0.0) : 0.0
        radius: root.handleRadius / 2

        Behavior on opacity {
          NumberAnimation {
            duration: Style.animationFast
          }
        }
      }
    }
  }
}
