// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#pragma once

#include "motion.h"
#include "colors.h"
#include "imgui.h"
#include "imgui_internal.h"
#include <functional>
#include <string>

namespace dragonx {
namespace ui {
namespace material {

// ============================================================================
// Material Design Transitions
// ============================================================================
// Based on https://m2.material.io/design/motion/the-motion-system.html
//
// Transition patterns for navigating between views and states.

// ============================================================================
// Transition Types
// ============================================================================

enum class TransitionType {
    None,           // Instant switch
    Fade,           // Cross-fade
    SlideLeft,      // Slide from right to left (forward navigation)
    SlideRight,     // Slide from left to right (back navigation)
    SlideUp,        // Slide from bottom (modal entry)
    SlideDown,      // Slide to bottom (modal exit)
    Scale,          // Scale up/down
    SharedAxis,     // Material shared axis transition
    ContainerTransform  // Card to full-screen
};

// ============================================================================
// View Transition Manager
// ============================================================================

/**
 * @brief Manages transitions between views/screens
 */
class TransitionManager {
public:
    static TransitionManager& instance();
    
    /**
     * @brief Start a transition to a new view
     * 
     * @param newViewId Identifier for the new view
     * @param type Transition type
     * @param duration Transition duration (default from motion.h)
     */
    void transitionTo(const std::string& newViewId, 
                      TransitionType type = TransitionType::Fade,
                      float duration = duration::ScreenChange);
    
    /**
     * @brief Go back with reverse transition
     */
    void goBack(TransitionType type = TransitionType::SlideRight,
                float duration = duration::ExitScreen);
    
    /**
     * @brief Update transition state (call each frame)
     */
    void update(float deltaTime);
    
    /**
     * @brief Get current view ID
     */
    const std::string& getCurrentView() const { return m_currentView; }
    
    /**
     * @brief Get previous view ID (during transition)
     */
    const std::string& getPreviousView() const { return m_previousView; }
    
    /**
     * @brief Check if transition is in progress
     */
    bool isTransitioning() const { return m_transitioning; }
    
    /**
     * @brief Get transition progress (0-1)
     */
    float getProgress() const { return m_progress; }
    
    /**
     * @brief Get current transition type
     */
    TransitionType getType() const { return m_type; }
    
    /**
     * @brief Apply transition effect to outgoing content
     * 
     * Call before rendering the previous view content.
     * Returns false if outgoing content should be skipped.
     */
    bool beginOutgoingTransition();
    
    /**
     * @brief End outgoing content transition
     */
    void endOutgoingTransition();
    
    /**
     * @brief Apply transition effect to incoming content
     * 
     * Call before rendering the new view content.
     * Returns false if incoming content should be skipped.
     */
    bool beginIncomingTransition();
    
    /**
     * @brief End incoming content transition
     */
    void endIncomingTransition();
    
    /**
     * @brief Get alpha for fading effects
     */
    float getOutgoingAlpha() const;
    float getIncomingAlpha() const;
    
    /**
     * @brief Get offset for sliding effects
     */
    ImVec2 getOutgoingOffset() const;
    ImVec2 getIncomingOffset() const;
    
private:
    TransitionManager() = default;
    
    std::string m_currentView;
    std::string m_previousView;
    TransitionType m_type = TransitionType::None;
    float m_duration = 0;
    float m_elapsed = 0;
    float m_progress = 0;
    bool m_transitioning = false;
};

// ============================================================================
// Fade Transition Helper
// ============================================================================

/**
 * @brief Simple fade transition between two states
 */
class FadeTransition {
public:
    FadeTransition() : m_alpha(1.0f) {}
    
    /**
     * @brief Fade out
     */
    void fadeOut(float duration = duration::Fast) {
        m_alpha.animateTo(0.0f, duration, EaseAccelerate);
    }
    
    /**
     * @brief Fade in
     */
    void fadeIn(float duration = duration::Fast) {
        m_alpha.animateTo(1.0f, duration, EaseDecelerate);
    }
    
    /**
     * @brief Update (call each frame)
     */
    void update(float deltaTime) {
        m_alpha.update(deltaTime);
    }
    
    /**
     * @brief Get current alpha
     */
    float getAlpha() const { return m_alpha.get(); }
    
    /**
     * @brief Check if visible (alpha > 0)
     */
    bool isVisible() const { return m_alpha.get() > 0.001f; }
    
    /**
     * @brief Check if animating
     */
    bool isAnimating() const { return m_alpha.isAnimating(); }
    
    /**
     * @brief Push alpha to ImGui
     */
    void pushAlpha() const {
        ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_alpha.get());
    }
    
    /**
     * @brief Pop alpha from ImGui
     */
    void popAlpha() const {
        ImGui::PopStyleVar();
    }
    
private:
    AnimatedValue<float> m_alpha;
};

// ============================================================================
// Expandable/Collapsible Section
// ============================================================================

/**
 * @brief Animated expandable section
 */
class ExpandableSection {
public:
    ExpandableSection(bool initiallyExpanded = false)
        : m_expanded(initiallyExpanded)
        , m_height(initiallyExpanded ? 1.0f : 0.0f)
    {}
    
    /**
     * @brief Toggle expansion state
     */
    void toggle() {
        setExpanded(!m_expanded);
    }
    
    /**
     * @brief Set expansion state
     */
    void setExpanded(bool expanded) {
        m_expanded = expanded;
        m_height.animateTo(expanded ? 1.0f : 0.0f,
                          expanded ? duration::Standard : duration::Medium,
                          EaseStandard);
    }
    
    /**
     * @brief Update animation
     */
    void update(float deltaTime) {
        m_height.update(deltaTime);
    }
    
    /**
     * @brief Check if expanded
     */
    bool isExpanded() const { return m_expanded; }
    
    /**
     * @brief Get height multiplier (0-1)
     */
    float getHeightMultiplier() const { return m_height.get(); }
    
    /**
     * @brief Check if animating
     */
    bool isAnimating() const { return m_height.isAnimating(); }
    
    /**
     * @brief Begin expandable content (applies clipping)
     * 
     * @param maxHeight Maximum content height when fully expanded
     * @return true if content should be rendered
     */
    bool beginContent(float maxHeight) {
        float currentHeight = maxHeight * m_height.get();
        
        if (currentHeight < 1.0f)
            return false;
        
        ImGui::BeginChild("##expandable", ImVec2(0, currentHeight), false,
                         ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
        return true;
    }
    
    /**
     * @brief End expandable content
     */
    void endContent() {
        ImGui::EndChild();
    }
    
private:
    bool m_expanded;
    AnimatedValue<float> m_height;
};

// ============================================================================
// Implementation
// ============================================================================

inline TransitionManager& TransitionManager::instance() {
    static TransitionManager s_instance;
    return s_instance;
}

inline void TransitionManager::transitionTo(const std::string& newViewId, 
                                            TransitionType type, float duration) {
    if (m_transitioning)
        return;  // Don't interrupt ongoing transition
    
    m_previousView = m_currentView;
    m_currentView = newViewId;
    m_type = type;
    m_duration = duration;
    m_elapsed = 0;
    m_progress = 0;
    m_transitioning = true;
}

inline void TransitionManager::goBack(TransitionType type, float duration) {
    if (m_transitioning || m_previousView.empty())
        return;
    
    std::string temp = m_currentView;
    m_currentView = m_previousView;
    m_previousView = temp;
    m_type = type;
    m_duration = duration;
    m_elapsed = 0;
    m_progress = 0;
    m_transitioning = true;
}

inline void TransitionManager::update(float deltaTime) {
    if (!m_transitioning)
        return;
    
    m_elapsed += deltaTime;
    m_progress = m_elapsed / m_duration;
    
    if (m_progress >= 1.0f) {
        m_progress = 1.0f;
        m_transitioning = false;
        m_previousView.clear();
    }
}

inline bool TransitionManager::beginOutgoingTransition() {
    if (!m_transitioning || m_type == TransitionType::None)
        return false;
    
    float alpha = getOutgoingAlpha();
    if (alpha < 0.001f)
        return false;
    
    ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
    
    ImVec2 offset = getOutgoingOffset();
    if (offset.x != 0 || offset.y != 0) {
        ImVec2 cursor = ImGui::GetCursorPos();
        ImGui::SetCursorPos(ImVec2(cursor.x + offset.x, cursor.y + offset.y));
    }
    
    return true;
}

inline void TransitionManager::endOutgoingTransition() {
    ImGui::PopStyleVar();
}

inline bool TransitionManager::beginIncomingTransition() {
    if (!m_transitioning && m_progress >= 1.0f)
        return true;  // No transition, just render normally
    
    if (m_type == TransitionType::None)
        return true;
    
    float alpha = getIncomingAlpha();
    ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
    
    ImVec2 offset = getIncomingOffset();
    if (offset.x != 0 || offset.y != 0) {
        ImVec2 cursor = ImGui::GetCursorPos();
        ImGui::SetCursorPos(ImVec2(cursor.x + offset.x, cursor.y + offset.y));
    }
    
    return true;
}

inline void TransitionManager::endIncomingTransition() {
    if (m_transitioning || m_type != TransitionType::None) {
        ImGui::PopStyleVar();
    }
}

inline float TransitionManager::getOutgoingAlpha() const {
    float eased = EaseAccelerate(m_progress);
    
    switch (m_type) {
        case TransitionType::Fade:
            return 1.0f - eased;
        case TransitionType::Scale:
            return 1.0f - eased;
        default:
            // For slides, fade quickly in first half
            return m_progress < 0.5f ? 1.0f - eased * 2.0f : 0.0f;
    }
}

inline float TransitionManager::getIncomingAlpha() const {
    float eased = EaseDecelerate(m_progress);
    
    switch (m_type) {
        case TransitionType::Fade:
            return eased;
        case TransitionType::Scale:
            return eased;
        default:
            // For slides, fade in during second half
            return m_progress > 0.5f ? (eased - 0.5f) * 2.0f : 0.0f;
    }
}

inline ImVec2 TransitionManager::getOutgoingOffset() const {
    ImGuiIO& io = ImGui::GetIO();
    float eased = EaseAccelerate(m_progress);
    float slideDistance = 100.0f;  // Pixels to slide
    
    switch (m_type) {
        case TransitionType::SlideLeft:
            return ImVec2(-slideDistance * eased, 0);
        case TransitionType::SlideRight:
            return ImVec2(slideDistance * eased, 0);
        case TransitionType::SlideUp:
            return ImVec2(0, -slideDistance * eased);
        case TransitionType::SlideDown:
            return ImVec2(0, slideDistance * eased);
        case TransitionType::SharedAxis:
            return ImVec2(-slideDistance * 0.3f * eased, 0);
        default:
            return ImVec2(0, 0);
    }
}

inline ImVec2 TransitionManager::getIncomingOffset() const {
    float eased = EaseDecelerate(m_progress);
    float slideDistance = 100.0f;
    float remaining = 1.0f - eased;
    
    switch (m_type) {
        case TransitionType::SlideLeft:
            return ImVec2(slideDistance * remaining, 0);
        case TransitionType::SlideRight:
            return ImVec2(-slideDistance * remaining, 0);
        case TransitionType::SlideUp:
            return ImVec2(0, slideDistance * remaining);
        case TransitionType::SlideDown:
            return ImVec2(0, -slideDistance * remaining);
        case TransitionType::SharedAxis:
            return ImVec2(slideDistance * 0.3f * remaining, 0);
        default:
            return ImVec2(0, 0);
    }
}

} // namespace material
} // namespace ui
} // namespace dragonx
