#pragma once

#include "Parameters/DeepPluckParameters.h"
#include "Parameters/DeepPluckParameterState.h"
#include "Effects/DeepPluckEffectsRack.h"
#include "Metering/DeepPluckOutputMeter.h"
#include "Presets/DeepPluckPresetManager.h"
#include "Presets/DeepPluckFactoryLibrary.h"
#include "Randomization/DeepPluckRandomization.h"
#include "Synth/DeepPluckSynthesiser.h"

#include <juce_audio_basics/juce_audio_basics.h>
#include <juce_audio_processors/juce_audio_processors.h>

#include <array>

namespace deeppluck::benchmark { struct Access; }
namespace deeppluck::presets { struct IntegrationTestAccess; }

class DeepPluckAudioProcessor final : public juce::AudioProcessor
{
public:
    struct PresetDisplayState
    {
        juce::String stableId;
        juce::String name { "Custom" };
        juce::String author;
        juce::File file;
        bool custom = true;
        bool editable = false;
    };

    explicit DeepPluckAudioProcessor (juce::File injectedPresetRoot = {},
                                      bool enablePresetFileIo = false);
    ~DeepPluckAudioProcessor() override = default;

    void prepareToPlay (double sampleRate, int maximumExpectedSamplesPerBlock) override;
    void releaseResources() override;

    bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
    void processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) override;
    void processBlockBypassed (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) override;

    juce::AudioProcessorEditor* createEditor() override;
    bool hasEditor() const override;

    const juce::String getName() const override;
    bool acceptsMidi() const override;
    bool producesMidi() const override;
    bool isMidiEffect() const override;
    double getTailLengthSeconds() const override;

    int getNumPrograms() override;
    int getCurrentProgram() override;
    void setCurrentProgram (int index) override;
    const juce::String getProgramName (int index) override;
    void changeProgramName (int index, const juce::String& newName) override;

    void getStateInformation (juce::MemoryBlock& destinationData) override;
    void setStateInformation (const void* data, int sizeInBytes) override;

    juce::AudioProcessorValueTreeState& getValueTreeState() noexcept;
    const juce::AudioProcessorValueTreeState& getValueTreeState() const noexcept;

    deeppluck::DeepPluckRandomization& getRandomization() noexcept;
    const deeppluck::DeepPluckRandomization& getRandomization() const noexcept;
    deeppluck::DeepPluckPresetManager& getPresetManager() noexcept;
    const deeppluck::DeepPluckPresetManager& getPresetManager() const noexcept;

    [[nodiscard]] bool randomizeAll();
    [[nodiscard]] bool randomizeModule (deeppluck::RandomizationModule module);
    [[nodiscard]] bool undoRandomization();
    void toggleRandomizationLock (deeppluck::RandomizationModule module) noexcept;

    [[nodiscard]] PresetDisplayState getPresetDisplayState() const;
    [[nodiscard]] deeppluck::UserPresetCatalog getUserPresetCatalog() const;
    [[nodiscard]] const deeppluck::FactoryLibrary& getFactoryLibrary() const noexcept;
    [[nodiscard]] bool loadFactoryPreset (int presetIndex);
    [[nodiscard]] bool loadPresetByStableId (const juce::String& stableId,
                                             juce::String& error);
    [[nodiscard]] bool loadRelativePreset (int direction, juce::String& error);
    [[nodiscard]] deeppluck::PresetSaveResult saveUserPreset (
        const juce::String& name,
        const juce::String& author,
        bool allowOverwrite,
        bool overwriteCurrentUser,
        juce::String& error);
    [[nodiscard]] bool deleteCurrentUserPreset (juce::String& error);
    void ensureInitialPresetScan();
    void refreshUserPresets();
    [[nodiscard]] bool revealPresetRoot (juce::String& error);
    [[nodiscard]] bool isPresetFileIoEnabled() const noexcept;

    [[nodiscard]] std::array<float, 2> consumeOutputMeterPeaks() noexcept;
    [[nodiscard]] bool isOutputMeterLockFree() const noexcept;

    static juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();

private:
    // Offline diagnostics only: no public API, data member, or callback branch.
    friend struct deeppluck::benchmark::Access;
    friend struct deeppluck::presets::IntegrationTestAccess;
    static constexpr auto stateIdentifier = "DeepPluckState";
    static constexpr double smoothingSeconds = 0.020;

    struct PreferredPreset
    {
        PresetDisplayState display;
        deeppluck::DeepPluckParameterValues fingerprint {};
        bool valid = false;
    };

    [[nodiscard]] PreferredPreset currentPreferenceForUndo() const;
    void setPreferredFactory (int presetIndex);
    [[nodiscard]] PreferredPreset factoryContentPreference (
        const deeppluck::FactoryContentEntry& entry) const;
    void setPreferredEntry (const deeppluck::UserPresetEntry& entry);
    void clearLoadBoundaryState();

    juce::AudioProcessorValueTreeState valueTreeState;
    deeppluck::DeepPluckParameterCache parameterCache;
    deeppluck::DeepPluckParameterSnapshot parameterSnapshot;
    deeppluck::DeepPluckSynthesiser synthesiser;
    deeppluck::DeepPluckEffectsRack effectsRack;
    juce::SmoothedValue<float, juce::ValueSmoothingTypes::Linear> outputGain;
    deeppluck::DeepPluckOutputMeter outputMeter;
    deeppluck::DeepPluckRandomization randomization;
    deeppluck::DeepPluckPresetManager presetManager;
    deeppluck::FactoryLibrary factoryLibrary;
    PreferredPreset preferredPreset;
    PreferredPreset undoPreferredPreset;
    double lastValidTempoBpm = 120.0;
    bool presetFileIoEnabled = false;
    bool initialPresetScanRequested = false;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DeepPluckAudioProcessor)
};
