#pragma once
#include "ProjectionGenerator.h"
#include "ClassDeclaration.h"
#include "GenericType.h"
#include "AsyncNodes.h"
#include "../SemannoAnnotationImpl.h"

class ElispGenerator : public ProjectionGenerator, public SemannoAnnotationImpl<ElispGenerator> {
public:
    std::string commentPrefix() const { return ";; "; }

    std::string generate(const ASTNode* node) override {
        return dispatchGenerate(this, node, "; Unknown concept: ");
    }

    std::string visitModule(const Module* module) override {
        std::ostringstream oss;

        oss << "; Module: " << module->name << "\n\n";

        // Process variables first
        auto variables = module->getChildren("variables");
        for (const auto* var : variables) {
            oss << visitVariable(static_cast<const Variable*>(var)) << "\n";
        }

        if (!variables.empty()) {
            oss << "\n";
        }

        // Process functions
        auto functions = module->getChildren("functions");
        for (size_t i = 0; i < functions.size(); ++i) {
            if (i > 0) oss << "\n";  // Add blank line between functions
            oss << visitFunction(static_cast<const Function*>(functions[i]));
        }

        return oss.str();
    }

    std::string visitFunction(const Function* function) override {
        std::ostringstream oss;

        // Process annotations first
        auto annotations = function->getChildren("annotations");
        for (const auto* annotation : annotations) {
            std::string annotationCode = generate(annotation);
            if (!annotationCode.empty()) {
                oss << annotationCode << "\n";
            }
        }

        // Generate function definition in Elisp
        oss << "(defun " << function->name << " (";

        auto parameters = function->getChildren("parameters");
        for (size_t i = 0; i < parameters.size(); ++i) {
            if (i > 0) oss << " ";
            oss << visitParameter(static_cast<const Parameter*>(parameters[i]));
        }

        oss << ")\n";

        // Process function body
        auto body = function->getChildren("body");
        if (body.empty()) {
            oss << "  nil)\n";  // Return nil if no body
        } else {
            for (const auto* stmt : body) {
                std::string stmtCode = generate(stmt);
                // Indent each line of the statement
                size_t pos = 0;
                while (pos < stmtCode.length()) {
                    size_t newlinePos = stmtCode.find('\n', pos);
                    if (newlinePos == std::string::npos) {
                        oss << "  " << stmtCode.substr(pos) << "\n";
                        break;
                    } else {
                        oss << "  " << stmtCode.substr(pos, newlinePos - pos) << "\n";
                        pos = newlinePos + 1;
                        if (pos >= stmtCode.length()) break;
                    }
                }
            }
            oss << ")\n";  // Close the function
        }

        return oss.str();
    }

    std::string visitVariable(const Variable* variable) override {
        std::ostringstream oss;
        oss << "(defvar " << variable->name << " ";

        auto initializer = variable->getChild("initializer");
        if (initializer) {
            oss << generate(initializer);
        } else {
            oss << "nil";
        }

        oss << ")  ; Variable declaration\n";
        return oss.str();
    }

    std::string visitParameter(const Parameter* parameter) override {
        // In Elisp, parameters are just symbols
        return parameter->name;
    }

    std::string visitAssignment(const Assignment* assignment) override {
        std::ostringstream oss;

        auto target = assignment->getChild("target");
        auto value = assignment->getChild("value");

        if (target && value) {
            oss << "(setq " << generate(target) << " " << generate(value) << ")";
        } else if (target) {
            oss << "(setq " << generate(target) << " nil)";
        } else {
            oss << "nil";
        }

        return oss.str();
    }

    std::string visitReturn(const Return* ret) override {
        // In Elisp, the last expression in a function is the return value
        // So we just return the value expression
        auto value = ret->getChild("value");
        if (value) {
            return generate(value);
        } else {
            return "nil";
        }
    }

    std::string visitBinaryOperation(const BinaryOperation* binOp) override {
        std::ostringstream oss;

        auto left = binOp->getChild("left");
        auto right = binOp->getChild("right");

        // Map operators to Elisp equivalents
        std::string op = binOp->op;
        if (op == "+") op = "+";
        else if (op == "-") op = "-";
        else if (op == "*") op = "*";
        else if (op == "/") op = "/";
        else if (op == "==") op = "=";
        else if (op == "!=") op = "/=";
        else if (op == "<") op = "<";
        else if (op == ">") op = ">";
        else if (op == "<=") op = "<=";
        else if (op == ">=") op = ">=";
        else if (op == "and") op = "and";
        else if (op == "or") op = "or";

        oss << "(" << op << " ";
        if (left) {
            oss << generate(left);
        } else {
            oss << "nil";
        }
        oss << " ";
        if (right) {
            oss << generate(right);
        } else {
            oss << "nil";
        }
        oss << ")";

        return oss.str();
    }

    std::string visitVariableReference(const VariableReference* varRef) override {
        return varRef->variableName;
    }

    std::string visitIntegerLiteral(const IntegerLiteral* lit) override {
        return std::to_string(lit->value);
    }

    std::string visitFloatLiteral(const FloatLiteral* lit) override {
        return lit->value;
    }

    std::string visitStringLiteral(const StringLiteral* lit) override {
        // In Elisp, strings are enclosed in double quotes
        return "\"" + lit->value + "\"";
    }

    std::string visitBooleanLiteral(const BooleanLiteral* lit) override {
        return lit->value ? "t" : "nil";
    }

    std::string visitNullLiteral(const NullLiteral* lit) override {
        return "nil";
    }

    std::string visitIfStatement(const IfStatement* stmt) override {
        std::ostringstream oss;

        auto condition = stmt->getChild("condition");
        auto thenBranch = stmt->getChildren("thenBranch");
        auto elseBranch = stmt->getChildren("elseBranch");

        oss << "(if ";
        if (condition) {
            oss << generate(condition);
        } else {
            oss << "nil";  // fallback
        }
        oss << "\n";

        // Then branch
        if (!thenBranch.empty()) {
            for (size_t i = 0; i < thenBranch.size(); ++i) {
                if (i > 0) oss << "\n";
                oss << "    " << generate(thenBranch[i]);
            }
        } else {
            oss << "    nil";
        }

        // Else branch
        if (!elseBranch.empty()) {
            oss << "\n";
            for (size_t i = 0; i < elseBranch.size(); ++i) {
                if (i > 0) oss << "\n";
                oss << "    " << generate(elseBranch[i]);
            }
        } else {
            oss << "\n    nil";
        }

        oss << ")";
        return oss.str();
    }

    std::string visitWhileLoop(const WhileLoop* loop) override {
        std::ostringstream oss;

        auto condition = loop->getChild("condition");
        auto body = loop->getChildren("body");

        oss << "(while ";
        if (condition) {
            oss << generate(condition);
        } else {
            oss << "t";  // fallback to infinite loop
        }
        oss << "\n";

        for (size_t i = 0; i < body.size(); ++i) {
            if (i > 0) oss << "\n";
            oss << "  " << generate(body[i]);
        }

        oss << ")";
        return oss.str();
    }

    std::string visitForLoop(const ForLoop* loop) override {
        std::ostringstream oss;

        auto iterable = loop->getChild("iterable");
        auto body = loop->getChildren("body");

        std::string iterator = loop->iteratorName.empty() ? "item" : loop->iteratorName;

        oss << "(dolist (" << iterator << " ";
        if (iterable) {
            oss << generate(iterable);
        } else {
            oss << "nil";  // fallback
        }
        oss << ")\n";

        for (size_t i = 0; i < body.size(); ++i) {
            if (i > 0) oss << "\n";
            oss << "  " << generate(body[i]);
        }

        oss << ")";
        return oss.str();
    }

    std::string visitExpressionStatement(const ExpressionStatement* stmt) override {
        std::ostringstream oss;

        auto expr = stmt->getChild("expression");
        if (expr) {
            oss << generate(expr);
        } else {
            oss << "nil";
        }

        return oss.str();
    }

    std::string visitUnaryOperation(const UnaryOperation* unOp) override {
        std::ostringstream oss;

        auto operand = unOp->getChild("operand");
        std::string op = unOp->op;

        // Map unary operators to Elisp equivalents
        if (op == "!") op = "not";
        else if (op == "-") op = "-";
        else if (op == "+") op = "+";

        oss << "(" << op << " ";
        if (operand) {
            oss << generate(operand);
        } else {
            oss << "nil";
        }
        oss << ")";

        return oss.str();
    }

    std::string visitFunctionCall(const FunctionCall* call) override {
        std::ostringstream oss;
        oss << "(" << call->functionName;

        auto arguments = call->getChildren("arguments");
        for (const auto* arg : arguments) {
            oss << " " << generate(arg);
        }

        oss << ")";

        return oss.str();
    }

    std::string visitBlock(const Block* block) override {
        std::ostringstream oss;

        auto statements = block->getChildren("statements");
        if (statements.empty()) {
            oss << "nil";
        } else {
            oss << "(progn\n";
            for (size_t i = 0; i < statements.size(); ++i) {
                oss << "  " << generate(statements[i]);
                if (i < statements.size() - 1) oss << "\n";
            }
            oss << ")";
        }

        return oss.str();
    }

    std::string visitListLiteral(const ListLiteral* lit) override {
        std::ostringstream oss;
        oss << "(list";

        auto elements = lit->getChildren("elements");
        for (const auto* elem : elements) {
            oss << " " << generate(elem);
        }

        oss << ")";

        return oss.str();
    }

    std::string visitIndexAccess(const IndexAccess* access) override {
        std::ostringstream oss;

        auto target = access->getChild("target");
        auto index = access->getChild("index");

        if (target && index) {
            // In Elisp, accessing list/array elements
            oss << "(nth " << generate(index) << " " << generate(target) << ")";
        } else if (target) {
            oss << generate(target);
        } else {
            oss << "nil";
        }

        return oss.str();
    }

    std::string visitMemberAccess(const MemberAccess* access) override {
        std::ostringstream oss;

        auto target = access->getChild("target");
        if (target) {
            // In Elisp, member access depends on the data structure
            // For now, we'll represent it as a function call
            oss << "(." << access->memberName << " " << generate(target) << ")";
        } else {
            oss << access->memberName;
        }

        return oss.str();
    }

    std::string visitPrimitiveType(const PrimitiveType* type) override {
        // In Elisp, types are more dynamic, but we can represent them as symbols
        std::string kind = type->kind;
        if (kind == "int") return "integer";
        if (kind == "float") return "float";
        if (kind == "string") return "string";
        if (kind == "bool") return "boolean";
        if (kind == "char") return "character";
        if (kind == "double") return "float";
        if (kind == "long") return "integer";
        if (kind == "short") return "integer";
        if (kind == "byte") return "integer";
        if (kind == "void") return "null";

        return kind;  // Return as-is if not a common type
    }

    std::string visitListType(const ListType* type) override {
        std::ostringstream oss;
        oss << "(list ";

        auto elementType = type->getChild("elementType");
        if (elementType) {
            oss << generate(elementType);
        } else {
            oss << "t";  // t means any type in Elisp
        }

        oss << ")";
        return oss.str();
    }

    std::string visitSetType(const SetType* type) override {
        std::ostringstream oss;
        oss << "(hash-table :test 'equal)";
        return oss.str();  // Represent sets as hash tables with equal test
    }

    std::string visitMapType(const MapType* type) override {
        std::ostringstream oss;
        oss << "(hash-table :test 'equal)";
        return oss.str();  // Represent maps as hash tables
    }

    std::string visitTupleType(const TupleType* type) override {
        std::ostringstream oss;
        oss << "(vector";  // Represent tuples as vectors

        auto elementTypes = type->getChildren("elementTypes");
        for (const auto* elemType : elementTypes) {
            oss << " " << generate(elemType);
        }

        oss << ")";
        return oss.str();
    }

    std::string visitArrayType(const ArrayType* type) override {
        std::ostringstream oss;
        oss << "(vector ";

        auto elementType = type->getChild("elementType");
        if (elementType) {
            oss << generate(elementType);
        } else {
            oss << "t";
        }

        oss << ")";
        return oss.str();
    }

    std::string visitOptionalType(const OptionalType* type) override {
        std::ostringstream oss;
        oss << "(or null ";

        auto innerType = type->getChild("innerType");
        if (innerType) {
            oss << generate(innerType);
        } else {
            oss << "t";
        }

        oss << ")";
        return oss.str();
    }

    std::string visitCustomType(const CustomType* type) override {
        return type->typeName;
    }

    // --- New AST node visitors (Step 308) ---

    std::string visitClassDeclaration(const ASTNode* node) override {
        auto* cls = static_cast<const ClassDeclaration*>(node);
        std::ostringstream oss;
        auto annotations = cls->getChildren("annotations");
        for (const auto* a : annotations) oss << generate(a) << "\n";
        oss << "(cl-defstruct " << cls->name;
        if (!cls->superClass.empty()) oss << "\n  (:include " << cls->superClass << ")";
        auto fields = cls->getChildren("fields");
        for (const auto* f : fields) {
            auto* var = static_cast<const Variable*>(f);
            oss << "\n  (" << var->name << " nil)";
        }
        oss << ")\n";
        auto methods = cls->getChildren("methods");
        for (const auto* m : methods) oss << "\n" << generate(m);
        return oss.str();
    }

    std::string visitInterfaceDeclaration(const ASTNode* node) override {
        auto* iface = static_cast<const InterfaceDeclaration*>(node);
        std::ostringstream oss;
        oss << "; Interface: " << iface->name << "\n";
        auto methods = iface->getChildren("methods");
        for (const auto* m : methods) {
            auto* meth = static_cast<const MethodDeclaration*>(m);
            oss << "(cl-defgeneric " << meth->name << " (self))\n";
        }
        return oss.str();
    }

    std::string visitMethodDeclaration(const ASTNode* node) override {
        auto* meth = static_cast<const MethodDeclaration*>(node);
        std::ostringstream oss;
        auto annotations = meth->getChildren("annotations");
        for (const auto* a : annotations) oss << generate(a) << "\n";
        oss << "(cl-defmethod " << meth->name << " ((self";
        if (!meth->className.empty()) oss << " " << meth->className;
        oss << ")";
        auto params = meth->getChildren("parameters");
        for (const auto* p : params) oss << " " << visitParameter(static_cast<const Parameter*>(p));
        oss << ")\n";
        auto body = meth->getChildren("body");
        if (body.empty()) {
            oss << "  nil)\n";
        } else {
            for (const auto* s : body) oss << "  " << generate(s) << "\n";
            oss << ")\n";
        }
        return oss.str();
    }

    std::string visitGenericType(const ASTNode* node) override {
        auto* gen = static_cast<const GenericType*>(node);
        return gen->baseName;
    }

    std::string visitTypeParameter(const ASTNode* node) override {
        auto* tp = static_cast<const TypeParameter*>(node);
        return tp->name;
    }

    std::string visitAsyncFunction(const ASTNode* node) override {
        auto* af = static_cast<const AsyncFunction*>(node);
        std::ostringstream oss;
        auto annotations = af->getChildren("annotations");
        for (const auto* a : annotations) oss << generate(a) << "\n";
        oss << "(defun " << af->name << " (";
        auto params = af->getChildren("parameters");
        for (size_t i = 0; i < params.size(); ++i) {
            if (i > 0) oss << " ";
            oss << visitParameter(static_cast<const Parameter*>(params[i]));
        }
        oss << ")\n";
        oss << "  ;; async\n";
        auto body = af->getChildren("body");
        if (body.empty()) {
            oss << "  nil)\n";
        } else {
            for (const auto* s : body) oss << "  " << generate(s) << "\n";
            oss << ")\n";
        }
        return oss.str();
    }

    std::string visitAwaitExpression(const ASTNode* node) override {
        auto* aw = static_cast<const AwaitExpression*>(node);
        auto* expr = aw->getChild("expression");
        return "(async-get " + (expr ? generate(expr) : "nil") + ")";
    }

    std::string visitLambdaExpression(const ASTNode* node) override {
        auto* lam = static_cast<const LambdaExpression*>(node);
        std::ostringstream oss;
        oss << "(lambda (";
        auto params = lam->getChildren("parameters");
        for (size_t i = 0; i < params.size(); ++i) {
            if (i > 0) oss << " ";
            oss << static_cast<const Parameter*>(params[i])->name;
        }
        oss << ")";
        auto body = lam->getChildren("body");
        if (body.empty()) {
            oss << " nil";
        } else {
            for (const auto* s : body) oss << "\n  " << generate(s);
        }
        oss << ")";
        return oss.str();
    }

    std::string visitDecoratorAnnotation(const ASTNode* node) override {
        auto* dec = static_cast<const DecoratorAnnotation*>(node);
        return "; @" + dec->name;
    }

    std::string visitDerefStrategy(const DerefStrategy* annotation) override {
        // Convert deref strategy to Elisp annotation/comment
        if (annotation->strategy == "batched") {
            return "; @deref(batched) - Process in batches for efficiency";
        } else if (annotation->strategy == "streamed") {
            return "; @deref(streamed) - Stream processing";
        } else if (annotation->strategy == "manual") {
            return "; @deref(manual) - Manual memory management";
        } else {
            return "; @deref(" + annotation->strategy + ") - Memory dereference strategy";
        }
    }

    std::string visitOptimizationLock(const OptimizationLock* annotation) override {
        return "; @lock(" + annotation->lockedBy + ") - Optimization locked by " + annotation->lockedBy;
    }

    std::string visitLangSpecific(const LangSpecific* annotation) override {
        return "; @lang_specific(" + annotation->language + ", " + annotation->idiomType + ")";
    }

    std::string visitDeallocateAnnotation(const DeallocateAnnotation* annotation) override {
        return "; @dealloc(" + annotation->strategy + ")";
    }

    std::string visitLifetimeAnnotation(const LifetimeAnnotation* annotation) override {
        return "; @lifetime(" + annotation->strategy + ")";
    }

    std::string visitReclaimAnnotation(const ReclaimAnnotation* annotation) override {
        return "; @reclaim(" + annotation->strategy + ")";
    }

    std::string visitOwnerAnnotation(const OwnerAnnotation* annotation) override {
        return "; @owner(" + annotation->strategy + ")";
    }

    std::string visitAllocateAnnotation(const AllocateAnnotation* annotation) override {
        return "; @allocate(" + annotation->strategy + ")";
    }

    std::string visitHotColdAnnotation(const HotColdAnnotation* annotation) override {
        return "; @" + annotation->hint + " - Optimization hint";
    }

    std::string visitInlineAnnotation(const InlineAnnotation* annotation) override {
        return "; @inline(" + annotation->mode + ")";
    }

    std::string visitPureAnnotation(const PureAnnotation*) override {
        return "; @pure - No side effects";
    }

    std::string visitConstExprAnnotation(const ConstExprAnnotation*) override {
        return "; @constexpr - Compile-time evaluable";
    }

    // --- Preprocessor/Enum/Namespace visitors (Step 340) ---

    std::string visitIncludeDirective(const ASTNode* node) override {
        auto* inc = static_cast<const IncludeDirective*>(node);
        std::string mod = inc->path;
        auto dot = mod.rfind('.');
        if (dot != std::string::npos) mod = mod.substr(0, dot);
        return "(require '" + mod + ")";
    }

    std::string visitPragmaDirective(const ASTNode* node) override {
        auto* prag = static_cast<const PragmaDirective*>(node);
        return ";; pragma " + prag->directive;
    }

    std::string visitMacroDefinition(const ASTNode* node) override {
        auto* mac = static_cast<const MacroDefinition*>(node);
        if (mac->isFunctionLike) {
            std::ostringstream oss;
            oss << "(defmacro " << mac->name << " (";
            for (size_t i = 0; i < mac->parameters.size(); ++i) {
                if (i > 0) oss << " ";
                oss << mac->parameters[i];
            }
            oss << ")\n  " << (mac->body.empty() ? "nil" : mac->body) << ")";
            return oss.str();
        }
        return "(defconst " + mac->name + " " + (mac->body.empty() ? "nil" : mac->body) + ")";
    }

    std::string visitEnumDeclaration(const ASTNode* node) override {
        auto* e = static_cast<const EnumDeclaration*>(node);
        std::ostringstream oss;
        auto members = e->getChildren("members");
        for (size_t i = 0; i < members.size(); ++i) {
            auto* m = static_cast<const EnumMember*>(members[i]);
            oss << "(defconst " << e->name << "-" << m->name << " "
                << (m->value.empty() ? std::to_string(i) : m->value) << ")";
            if (i + 1 < members.size()) oss << "\n";
        }
        return oss.str();
    }

    std::string visitNamespaceDeclaration(const ASTNode* node) override {
        auto* ns = static_cast<const NamespaceDeclaration*>(node);
        std::ostringstream oss;
        oss << ";; namespace " << ns->name << "\n";
        auto body = ns->getChildren("body");
        for (const auto* child : body)
            oss << generate(child) << "\n";
        return oss.str();
    }

    std::string visitTypeAlias(const ASTNode* node) override {
        auto* ta = static_cast<const TypeAlias*>(node);
        return "(defalias '" + ta->aliasName + " '" + ta->targetType + ")";
    }
};
