#pragma once
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>
#include <cctype>

struct ImportIssue {
    int line = 0;
    std::string symbol;
    std::string message;
};

struct ImportEditResult {
    std::string text;
    bool changed = false;
};

static inline std::vector<std::string> importSplitLines(const std::string& text) {
    std::vector<std::string> lines;
    std::stringstream ss(text);
    std::string line;
    while (std::getline(ss, line)) {
        lines.push_back(line);
    }
    if (!text.empty() && text.back() == '\n') lines.push_back("");
    return lines;
}

static inline std::string joinLines(const std::vector<std::string>& lines) {
    std::string out;
    for (size_t i = 0; i < lines.size(); ++i) {
        out += lines[i];
        if (i + 1 < lines.size()) out += "\n";
    }
    return out;
}

static inline std::string trimStr(const std::string& s) {
    size_t start = s.find_first_not_of(" \t\r\n");
    size_t end = s.find_last_not_of(" \t\r\n");
    if (start == std::string::npos || end == std::string::npos) return "";
    return s.substr(start, end - start + 1);
}

static inline bool startsWith(const std::string& s, const std::string& prefix) {
    return s.rfind(prefix, 0) == 0;
}

static inline bool isIdentChar(char c) {
    return std::isalnum((unsigned char)c) || c == '_' || c == '-';
}

static inline bool containsWord(const std::string& text, const std::string& word) {
    if (word.empty()) return false;
    size_t pos = 0;
    while ((pos = text.find(word, pos)) != std::string::npos) {
        bool leftOk = (pos == 0) || !isIdentChar(text[pos - 1]);
        bool rightOk = (pos + word.size() >= text.size()) ||
                       !isIdentChar(text[pos + word.size()]);
        if (leftOk && rightOk) return true;
        pos += word.size();
    }
    return false;
}

static inline std::string lastPathSegment(const std::string& path) {
    auto pos = path.find_last_of("/.");
    if (pos == std::string::npos) return path;
    return path.substr(pos + 1);
}

static inline ImportEditResult ensureImportPython(const std::string& text,
                                                  const std::string& library,
                                                  const std::string& symbol) {
    ImportEditResult out{ text, false };
    std::string line;
    if (startsWith(symbol, library + ".")) {
        line = "import " + library;
    } else {
        line = "from " + library + " import " + symbol;
    }

    auto lines = importSplitLines(text);
    std::vector<std::string> imports;
    std::vector<std::string> rest;
    for (const auto& l : lines) {
        std::string t = trimStr(l);
        if (startsWith(t, "import ") || startsWith(t, "from ")) {
            if (t != line) imports.push_back(l);
        } else {
            rest.push_back(l);
        }
    }
    imports.push_back(line);
    std::sort(imports.begin(), imports.end());
    out.text = joinLines(imports) + "\n" + joinLines(rest);
    out.changed = true;
    return out;
}

static inline ImportEditResult ensureImportJs(const std::string& text,
                                              const std::string& library,
                                              const std::string& symbol) {
    ImportEditResult out{ text, false };
    std::string name = symbol;
    auto dot = name.find_last_of('.');
    if (dot != std::string::npos) name = name.substr(dot + 1);
    std::string line = "import { " + name + " } from '" + library + "';";

    auto lines = importSplitLines(text);
    std::vector<std::string> imports;
    std::vector<std::string> rest;
    for (const auto& l : lines) {
        std::string t = trimStr(l);
        if (startsWith(t, "import ")) {
            if (t != line) imports.push_back(l);
        } else {
            rest.push_back(l);
        }
    }
    imports.push_back(line);
    std::sort(imports.begin(), imports.end());
    out.text = joinLines(imports) + "\n" + joinLines(rest);
    out.changed = true;
    return out;
}

static inline ImportEditResult ensureImportRust(const std::string& text,
                                                const std::string& library,
                                                const std::string& symbol) {
    ImportEditResult out{ text, false };
    std::string line;
    if (symbol.find("::") != std::string::npos) line = "use " + symbol + ";";
    else line = "use " + library + "::" + symbol + ";";

    auto lines = importSplitLines(text);
    std::vector<std::string> imports;
    std::vector<std::string> rest;
    for (const auto& l : lines) {
        std::string t = trimStr(l);
        if (startsWith(t, "use ")) {
            if (t != line) imports.push_back(l);
        } else {
            rest.push_back(l);
        }
    }
    imports.push_back(line);
    std::sort(imports.begin(), imports.end());
    out.text = joinLines(imports) + "\n" + joinLines(rest);
    out.changed = true;
    return out;
}

static inline ImportEditResult ensureImportGo(const std::string& text,
                                              const std::string& library) {
    ImportEditResult out{ text, false };
    std::string path = "\"" + library + "\"";
    std::string line = "import " + path;
    auto lines = importSplitLines(text);
    int pkgLine = -1;
    int importStart = -1;
    int importEnd = -1;
    for (int i = 0; i < (int)lines.size(); ++i) {
        std::string t = trimStr(lines[i]);
        if (startsWith(t, "package ")) pkgLine = i;
        if (startsWith(t, "import (")) { importStart = i; }
        if (importStart >= 0 && t == ")") { importEnd = i; break; }
        if (importStart < 0 && startsWith(t, "import \"")) {
            importStart = i;
            importEnd = i;
            break;
        }
    }

    if (importStart >= 0 && importEnd >= importStart && importStart != importEnd) {
        std::vector<std::string> imports;
        for (int i = importStart + 1; i < importEnd; ++i) {
            std::string t = trimStr(lines[i]);
            if (!t.empty()) imports.push_back(t);
        }
        if (std::find(imports.begin(), imports.end(), path) == imports.end()) {
            imports.push_back(path);
        }
        std::sort(imports.begin(), imports.end());
        std::vector<std::string> outLines;
        for (int i = 0; i <= importStart; ++i) outLines.push_back(lines[i]);
        for (const auto& imp : imports) outLines.push_back("    " + imp);
        for (int i = importEnd; i < (int)lines.size(); ++i) outLines.push_back(lines[i]);
        out.text = joinLines(outLines);
        out.changed = true;
        return out;
    }

    if (importStart >= 0 && importStart == importEnd) {
        std::string existing = trimStr(lines[importStart]);
        std::vector<std::string> imports = { existing };
        if (existing != line) imports.push_back(line);
        std::sort(imports.begin(), imports.end());
        std::vector<std::string> outLines;
        for (int i = 0; i < importStart; ++i) outLines.push_back(lines[i]);
        outLines.push_back("import (");
        for (const auto& imp : imports) {
            std::string t = trimStr(imp);
            if (startsWith(t, "import ")) t = trimStr(t.substr(6));
            outLines.push_back("    " + t);
        }
        outLines.push_back(")");
        for (int i = importStart + 1; i < (int)lines.size(); ++i) outLines.push_back(lines[i]);
        out.text = joinLines(outLines);
        out.changed = true;
        return out;
    }

    int insertAt = (pkgLine >= 0) ? pkgLine + 1 : 0;
    std::vector<std::string> outLines;
    for (int i = 0; i < insertAt; ++i) outLines.push_back(lines[i]);
    outLines.push_back("import (");
    outLines.push_back("    " + path);
    outLines.push_back(")");
    for (int i = insertAt; i < (int)lines.size(); ++i) outLines.push_back(lines[i]);
    out.text = joinLines(outLines);
    out.changed = true;
    return out;
}

static inline ImportEditResult ensureImportElisp(const std::string& text,
                                                 const std::string& library) {
    ImportEditResult out{ text, false };
    std::string line = "(require '" + library + ")";
    auto lines = importSplitLines(text);
    for (const auto& l : lines) {
        if (trimStr(l) == line) return out;
    }
    std::vector<std::string> outLines;
    outLines.push_back(line);
    outLines.insert(outLines.end(), lines.begin(), lines.end());
    out.text = joinLines(outLines);
    out.changed = true;
    return out;
}

static inline ImportEditResult ensureImportCpp(const std::string& text,
                                               const std::string& library) {
    ImportEditResult out{ text, false };
    std::string line = "#include <" + library + ">";
    auto lines = importSplitLines(text);
    std::vector<std::string> imports;
    std::vector<std::string> rest;
    for (const auto& l : lines) {
        std::string t = trimStr(l);
        if (startsWith(t, "#include")) {
            if (t != line) imports.push_back(l);
        } else {
            rest.push_back(l);
        }
    }
    imports.push_back(line);
    std::sort(imports.begin(), imports.end());
    out.text = joinLines(imports) + "\n" + joinLines(rest);
    out.changed = true;
    return out;
}

static inline ImportEditResult ensureImport(const std::string& text,
                                            const std::string& language,
                                            const std::string& library,
                                            const std::string& symbol) {
    if (library.empty()) return {text, false};
    ImportEditResult res;
    if (language == "python") res = ensureImportPython(text, library, symbol);
    else if (language == "javascript" || language == "typescript")
        res = ensureImportJs(text, library, symbol);
    else if (language == "rust") res = ensureImportRust(text, library, symbol);
    else if (language == "go") res = ensureImportGo(text, library);
    else if (language == "elisp") res = ensureImportElisp(text, library);
    else res = ensureImportCpp(text, library);
    if (res.text == text) res.changed = false;
    return res;
}

static inline std::vector<ImportIssue> findUnusedImportsPython(const std::string& text) {
    std::vector<ImportIssue> issues;
    auto lines = importSplitLines(text);
    std::string body;
    for (const auto& l : lines) {
        std::string t = trimStr(l);
        if (startsWith(t, "import ") || startsWith(t, "from ")) continue;
        body += l;
        body += "\n";
    }
    for (int i = 0; i < (int)lines.size(); ++i) {
        std::string t = trimStr(lines[i]);
        std::string symbol;
        if (startsWith(t, "import ")) {
            symbol = trimStr(t.substr(7));
        } else if (startsWith(t, "from ")) {
            auto pos = t.find("import");
            if (pos != std::string::npos) symbol = trimStr(t.substr(pos + 6));
        }
        if (!symbol.empty() && symbol != "*") {
            if (!containsWord(body, symbol)) {
                issues.push_back({i, symbol, "Unused import: " + symbol});
            }
        }
    }
    return issues;
}

static inline std::vector<ImportIssue> findUnusedImportsJs(const std::string& text) {
    std::vector<ImportIssue> issues;
    auto lines = importSplitLines(text);
    std::string body;
    for (const auto& l : lines) {
        std::string t = trimStr(l);
        if (startsWith(t, "import ")) continue;
        body += l;
        body += "\n";
    }
    for (int i = 0; i < (int)lines.size(); ++i) {
        std::string t = trimStr(lines[i]);
        if (!startsWith(t, "import ")) continue;
        auto brace = t.find('{');
        auto from = t.find(" from ");
        if (brace != std::string::npos && from != std::string::npos) {
            auto end = t.find('}', brace);
            if (end == std::string::npos) continue;
            std::string names = t.substr(brace + 1, end - brace - 1);
            std::stringstream ss(names);
            std::string item;
            while (std::getline(ss, item, ',')) {
                item = trimStr(item);
                if (!item.empty() && !containsWord(body, item)) {
                    issues.push_back({i, item, "Unused import: " + item});
                }
            }
        }
    }
    return issues;
}

static inline std::vector<ImportIssue> findUnusedImportsRust(const std::string& text) {
    std::vector<ImportIssue> issues;
    auto lines = importSplitLines(text);
    std::string body;
    for (const auto& l : lines) {
        std::string t = trimStr(l);
        if (startsWith(t, "use ")) continue;
        body += l;
        body += "\n";
    }
    for (int i = 0; i < (int)lines.size(); ++i) {
        std::string t = trimStr(lines[i]);
        if (!startsWith(t, "use ")) continue;
        auto pos = t.find("::");
        if (pos == std::string::npos) continue;
        std::string symbol = t.substr(t.find_last_of(':') + 1);
        if (!symbol.empty() && symbol.back() == ';') symbol.pop_back();
        if (!symbol.empty() && !containsWord(body, symbol)) {
            issues.push_back({i, symbol, "Unused import: " + symbol});
        }
    }
    return issues;
}

static inline std::vector<ImportIssue> findUnusedImportsGo(const std::string& text) {
    std::vector<ImportIssue> issues;
    auto lines = importSplitLines(text);
    std::string body;
    for (const auto& l : lines) {
        std::string t = trimStr(l);
        if (startsWith(t, "import ")) continue;
        body += l;
        body += "\n";
    }
    for (int i = 0; i < (int)lines.size(); ++i) {
        std::string t = trimStr(lines[i]);
        if (startsWith(t, "import \"")) {
            std::string path = t.substr(8);
            if (!path.empty() && path.back() == '"') path.pop_back();
            std::string symbol = lastPathSegment(path);
            if (!symbol.empty() && body.find(symbol + ".") == std::string::npos) {
                issues.push_back({i, symbol, "Unused import: " + symbol});
            }
        }
    }
    return issues;
}

static inline std::vector<ImportIssue> findUnusedImportsElisp(const std::string& text) {
    std::vector<ImportIssue> issues;
    auto lines = importSplitLines(text);
    std::string body;
    for (const auto& l : lines) {
        std::string t = trimStr(l);
        if (startsWith(t, "(require '")) continue;
        body += l;
        body += "\n";
    }
    for (int i = 0; i < (int)lines.size(); ++i) {
        std::string t = trimStr(lines[i]);
        if (startsWith(t, "(require '")) {
            auto start = t.find('\'');
            auto end = t.find(')', start);
            if (start == std::string::npos || end == std::string::npos) continue;
            std::string symbol = t.substr(start + 1, end - start - 1);
            if (!symbol.empty() && body.find(symbol) == std::string::npos) {
                issues.push_back({i, symbol, "Unused import: " + symbol});
            }
        }
    }
    return issues;
}

static inline std::vector<ImportIssue> findUnusedImports(const std::string& text,
                                                         const std::string& language) {
    if (language == "python") return findUnusedImportsPython(text);
    if (language == "javascript" || language == "typescript") return findUnusedImportsJs(text);
    if (language == "rust") return findUnusedImportsRust(text);
    if (language == "go") return findUnusedImportsGo(text);
    if (language == "elisp") return findUnusedImportsElisp(text);
    return {};
}
