// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once

#include "Luau/Constraint.h"
#include "Luau/NotNull.h"
#include "Luau/Scope.h"
#include "Luau/Module.h"
#include "Luau/ToString.h"
#include "Luau/Error.h"
#include "Luau/Variant.h"

#include <optional>
#include <string>
#include <vector>

namespace Luau
{

struct ErrorSnapshot
{
    std::string message;
    Location location;
};

struct BindingSnapshot
{
    std::string typeId;
    std::string typeString;
    Location location;
};

struct TypeBindingSnapshot
{
    std::string typeId;
    std::string typeString;
};

struct ExprTypesAtLocation
{
    Location location;
    TypeId ty;
    std::optional<TypeId> expectedTy;
};

struct AnnotationTypesAtLocation
{
    Location location;
    TypeId resolvedTy;
};

struct ConstraintGenerationLog
{
    std::string source;
    std::vector<ErrorSnapshot> errors;

    std::vector<ExprTypesAtLocation> exprTypeLocations;
    std::vector<AnnotationTypesAtLocation> annotationTypeLocations;
};

struct ScopeSnapshot
{
    std::unordered_map<Name, BindingSnapshot> bindings;
    std::unordered_map<Name, TypeBindingSnapshot> typeBindings;
    std::unordered_map<Name, TypeBindingSnapshot> typePackBindings;
    std::vector<ScopeSnapshot> children;
};

using ConstraintBlockTarget = Variant<TypeId, TypePackId, NotNull<const Constraint>>;

struct ConstraintBlock
{
    ConstraintBlockTarget target;
    std::string stringification;
};

struct ConstraintSnapshot
{
    std::string stringification;
    Location location;
    std::vector<ConstraintBlock> blocks;
};

struct BoundarySnapshot
{
    DenseHashMap<const Constraint*, ConstraintSnapshot> unsolvedConstraints{nullptr};
    ScopeSnapshot rootScope;
    DenseHashMap<const void*, std::string> typeStrings{nullptr};
};

struct StepSnapshot
{
    const Constraint* currentConstraint;
    bool forced;
    DenseHashMap<const Constraint*, ConstraintSnapshot> unsolvedConstraints{nullptr};
    ScopeSnapshot rootScope;
    DenseHashMap<const void*, std::string> typeStrings{nullptr};
};

struct TypeSolveLog
{
    BoundarySnapshot initialState;
    std::vector<StepSnapshot> stepStates;
    BoundarySnapshot finalState;
};

struct TypeCheckLog
{
    std::vector<ErrorSnapshot> errors;
};

struct DcrLogger
{
    std::string compileOutput();

    void captureSource(std::string source);
    void captureGenerationError(const TypeError& error);
    void captureConstraintLocation(NotNull<const Constraint> constraint, Location location);
    void captureGenerationModule(const ModulePtr& module);

    void pushBlock(NotNull<const Constraint> constraint, TypeId block);
    void pushBlock(NotNull<const Constraint> constraint, TypePackId block);
    void pushBlock(NotNull<const Constraint> constraint, NotNull<const Constraint> block);
    void popBlock(TypeId block);
    void popBlock(TypePackId block);
    void popBlock(NotNull<const Constraint> block);

    void captureInitialSolverState(const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints);
    StepSnapshot prepareStepSnapshot(
        const Scope* rootScope,
        NotNull<const Constraint> current,
        bool force,
        const std::vector<NotNull<const Constraint>>& unsolvedConstraints
    );
    void commitStepSnapshot(StepSnapshot snapshot);
    void captureFinalSolverState(const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints);

    void captureTypeCheckError(const TypeError& error);

private:
    ConstraintGenerationLog generationLog;
    std::unordered_map<NotNull<const Constraint>, std::vector<ConstraintBlockTarget>> constraintBlocks;
    TypeSolveLog solveLog;
    TypeCheckLog checkLog;

    ToStringOptions opts{true};

    std::vector<ConstraintBlock> snapshotBlocks(NotNull<const Constraint> constraint);
    void captureBoundaryState(BoundarySnapshot& target, const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints);
};

} // namespace Luau