#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <limits>
#include <cctype>
#include <algorithm>
#include <iomanip>

void clear_screen(){
        std::cout << "\033[H\033[2J\033[3J";
}

// =========================
// Lowercase Helper
// =========================
std::string toLower(const std::string& s) {
    std::string out;
    out.reserve(s.size());
    for (char c : s) out.push_back(std::tolower((unsigned char)c));
    return out;
}

// =========================
// Car Class
// =========================
class Car {
private:
    std::string year, make, model, engine, transmission, vin;

public:
    Car(const std::string& y,
        const std::string& mk,
        const std::string& mdl,
        const std::string& eng,
        const std::string& trans,
        const std::string& v)
        : year(y), make(mk), model(mdl),
          engine(eng), transmission(trans), vin(v) {}

    const std::string& getYear() const { return year; }
    const std::string& getMake() const { return make; }
    const std::string& getModel() const { return model; }
    const std::string& getEngine() const { return engine; }
    const std::string& getTransmission() const { return transmission; }
    const std::string& getVin() const { return vin; }
};

// =========================
// Load CSV
// =========================
std::vector<Car> loadCarsFromCSV(const std::string& filename) {
    std::vector<Car> cars;
    std::ifstream file(filename);

    if (!file.is_open()) {
        std::cerr << "Failed to open file\n";
        return cars;
    }

    std::string line;
    std::getline(file, line); // skip header

    while (std::getline(file, line)) {
        std::stringstream ss(line);
        std::string year, make, model, engine, transmission, vin;

        std::getline(ss, year, ',');
        std::getline(ss, make, ',');
        std::getline(ss, model, ',');
        std::getline(ss, engine, ',');
        std::getline(ss, transmission, ',');
        std::getline(ss, vin, ',');

        cars.emplace_back(year, make, model, engine, transmission, vin);
    }

    return cars;
}

// =========================
// Search Functions
// =========================
std::vector<Car> findByVinContains(const std::vector<Car>& cars, const std::string& vin) {
    std::vector<Car> results;
    std::string target = toLower(vin);

    for (const auto& car : cars) {
        if (toLower(car.getVin()).find(target) != std::string::npos)
            results.push_back(car);
    }
    return results;
}

std::vector<Car> findByYear(const std::vector<Car>& cars, const std::string& year) {
    std::vector<Car> results;
    std::string target = toLower(year);

    for (const auto& car : cars) {
        if (toLower(car.getYear()) == target)
            results.push_back(car);
    }
    return results;
}

std::vector<Car> findByMake(const std::vector<Car>& cars, const std::string& make) {
    std::vector<Car> results;
    std::string target = toLower(make);

    for (const auto& car : cars) {
        if (toLower(car.getMake()) == target)
            results.push_back(car);
    }
    return results;
}

std::vector<Car> findByModelContains(const std::vector<Car>& cars, const std::string& text) {
    std::vector<Car> results;
    std::string target = toLower(text);

    for (const auto& car : cars) {
        if (toLower(car.getModel()).find(target) != std::string::npos)
            results.push_back(car);
    }
    return results;
}

// =========================
// Add Car
// =========================
void addCarToCSV(const std::string& filename, const Car& car) {
    std::ofstream file(filename, std::ios::app);
    if (!file.is_open()) {
        std::cerr << "Failed to open file for writing\n";
        return;
    }

    file << car.getYear() << ","
         << car.getMake() << ","
         << car.getModel() << ","
         << car.getEngine() << ","
         << car.getTransmission() << ","
         << car.getVin() << "\n";
}

// =========================
// Rewrite CSV (for delete)
// =========================
void rewriteCSV(const std::string& filename, const std::vector<Car>& cars) {
    std::ofstream file(filename, std::ios::trunc);
    if (!file.is_open()) {
        std::cerr << "Failed to rewrite CSV\n";
        return;
    }

    file << "year,make,model,engine,transmission,vin\n";

    for (const auto& c : cars) {
        file << c.getYear() << ","
             << c.getMake() << ","
             << c.getModel() << ","
             << c.getEngine() << ","
             << c.getTransmission() << ","
             << c.getVin() << "\n";
    }
}

// =========================
// Export CSV
// =========================
void exportResults(const std::string& filename, const std::vector<Car>& results) {
    std::ofstream file(filename, std::ios::trunc);
    if (!file.is_open()) {
        std::cerr << "Failed to export\n";
        return;
    }

    file << "year,make,model,engine,transmission,vin\n";

    for (const auto& c : results) {
        file << c.getYear() << ","
             << c.getMake() << ","
             << c.getModel() << ","
             << c.getEngine() << ","
             << c.getTransmission() << ","
             << c.getVin() << "\n";
    }

    std::cout << "Exported " << results.size() << " cars to " << filename << "\n";
}

// =========================
// Export Pretty Table
// =========================
void exportScreen(const std::string& filename, const std::vector<Car>& cars) {
    std::ofstream file(filename, std::ios::trunc);
    if (!file.is_open()) {
        std::cerr << "Failed to export screen view\n";
        return;
    }

    const int W_YEAR = 6;
    const int W_MAKE = 12;
    const int W_MODEL = 20;
    const int W_ENGINE = 15;
    const int W_TRANS = 18;
    const int W_VIN = 18;

    file << std::left
         << std::setw(W_YEAR)  << "Year"
         << std::setw(W_MAKE)  << "Make"
         << std::setw(W_MODEL) << "Model"
         << std::setw(W_ENGINE) << "Engine"
         << std::setw(W_TRANS) << "Transmission"
         << std::setw(W_VIN)   << "VIN"
         << "\n";

    file << std::string(W_YEAR + W_MAKE + W_MODEL + W_ENGINE + W_TRANS + W_VIN, '-') << "\n";

    for (const auto& c : cars) {
        file << std::left
             << std::setw(W_YEAR)  << c.getYear()
             << std::setw(W_MAKE)  << c.getMake()
             << std::setw(W_MODEL) << c.getModel()
             << std::setw(W_ENGINE) << c.getEngine()
             << std::setw(W_TRANS) << c.getTransmission()
             << std::setw(W_VIN)   << c.getVin()
             << "\n";
    }

    std::cout << "Screen-style export saved to " << filename << "\n";
}

// =========================
// Pretty Table Print
// =========================
void printCars(const std::vector<Car>& cars) {
    const int W_YEAR = 6;
    const int W_MAKE = 12;
    const int W_MODEL = 20;
    const int W_ENGINE = 15;
    const int W_TRANS = 18;
    const int W_VIN = 18;

    std::cout << std::left
        << std::setw(W_YEAR)  << "Year"
        << std::setw(W_MAKE)  << "Make"
        << std::setw(W_MODEL) << "Model"
        << std::setw(W_ENGINE) << "Engine"
        << std::setw(W_TRANS) << "Transmission"
        << std::setw(W_VIN)   << "VIN"
        << "\n";

    std::cout << std::string(W_YEAR + W_MAKE + W_MODEL + W_ENGINE + W_TRANS + W_VIN, '-') << "\n";

    for (const auto& c : cars) {
        std::cout << std::left
            << std::setw(W_YEAR)  << c.getYear()
            << std::setw(W_MAKE)  << c.getMake()
            << std::setw(W_MODEL) << c.getModel()
            << std::setw(W_ENGINE) << c.getEngine()
            << std::setw(W_TRANS) << c.getTransmission()
            << std::setw(W_VIN)   << c.getVin()
            << "\n";
    }
}

// =========================
// Search Command Handler
// =========================
enum SearchCommand {
    CMD_CONTINUE,
    CMD_STOP,
    CMD_BACK
};

SearchCommand handleSearchCommand(const std::string& input,
                                  std::vector<Car>& lastResults,
                                  const std::vector<Car>& cars)
{
    std::string cmd = toLower(input);

    if (cmd == "export") {
        std::string filename;
        std::cout << "Enter filename: ";
        std::getline(std::cin, filename);
        exportResults(filename, lastResults);
        return CMD_STOP;
    }

    if (cmd == "exportscreen") {
        std::string filename;
        std::cout << "Enter filename: ";
        std::getline(std::cin, filename);
        exportScreen(filename, lastResults);
        return CMD_STOP;
    }

    if (cmd == "reset") {
        lastResults = cars;
        clear_screen();
        return CMD_STOP;
    }

    if (cmd == "exit") {
        std::cout << "Goodbye.\n";
        exit(0);
    }

    if (cmd == "back") {
        return CMD_BACK;
    }

    return CMD_CONTINUE;
}

// =========================
// Main
// =========================
int main() {
	clear_screen();
    std::vector<Car> cars = loadCarsFromCSV("test.csv");
    std::vector<Car> lastResults = cars;

    while (true) {
        std::cout << "\nChoose: search, vin, add, delete, export, exportscreen, exit: ";
        std::string user_search;
        std::cin >> user_search;
	clear_screen();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

        // Guided Search Module
        if (user_search == "search") {
		 lastResults = cars;

            std::vector<Car> makeSnapshot;
            std::vector<Car> modelSnapshot;

            // =========================
            // MAKE STEP
            // =========================
make_step:
            {
                std::vector<Car> base = cars;
                std::string make;
                std::cout << "Enter Make (or export/reset/back/exit/exportscreen): ";
                std::getline(std::cin, make);

                switch (handleSearchCommand(make, lastResults, cars)) {
                    case CMD_STOP: continue;
                    case CMD_BACK:
                        goto make_step;
                    case CMD_CONTINUE: break;
                }

                makeSnapshot = findByMake(base, make);
                lastResults = makeSnapshot;

                printCars(lastResults);
            }

            // =========================
            // MODEL STEP
            // =========================
model_step:
            {
                std::vector<Car> base = makeSnapshot;
                std::string model;
                std::cout << "\nEnter Model (or export/reset/back/exit/exportscreen): ";
                std::getline(std::cin, model);

                switch (handleSearchCommand(model, lastResults, cars)) {
                    case CMD_STOP: continue;
                    case CMD_BACK:
                        lastResults = makeSnapshot;
                        printCars(lastResults);
                        goto make_step;
                    case CMD_CONTINUE: break;
                }

                modelSnapshot = findByModelContains(base, model);
                lastResults = modelSnapshot;

                printCars(lastResults);
            }

            // =========================
            // YEAR STEP
            // =========================
year_step:
            {
                std::vector<Car> base = modelSnapshot;
                std::string year;
                std::cout << "\nEnter Year (or export/reset/back/exit/exportscreen): ";
                std::getline(std::cin, year);

                switch (handleSearchCommand(year, lastResults, cars)) {
                    case CMD_STOP: continue;
                    case CMD_BACK:
                        lastResults = modelSnapshot;
                        printCars(lastResults);
                        goto model_step;
                    case CMD_CONTINUE: break;
                }

                lastResults = findByYear(base, year);
                printCars(lastResults);
            }
        }

        else if (user_search == "vin") {
            std::string vin;
            std::cout << "Enter VIN: ";
            std::getline(std::cin, vin);

            auto vinResults = findByVinContains(cars, vin);
            lastResults = vinResults;

            printCars(vinResults);
        }

        else if (user_search == "add") {
            std::string year, make, model, engine, transmission, vin;

            std::cout << "Enter year: ";
            std::getline(std::cin, year);

            std::cout << "Enter make: ";
            std::getline(std::cin, make);

            std::cout << "Enter model: ";
            std::getline(std::cin, model);

            std::cout << "Enter engine: ";
            std::getline(std::cin, engine);

            std::cout << "Enter transmission: ";
            std::getline(std::cin, transmission);

            std::cout << "Enter VIN: ";
            std::getline(std::cin, vin);

            Car newCar(year, make, model, engine, transmission, vin);
            addCarToCSV("test.csv", newCar);
            cars.push_back(newCar);
            lastResults = cars;

            std::cout << "\nCar added.\n";
        }

        else if (user_search == "delete") {
            std::string vin;
            std::cout << "Enter VIN (full or partial): ";
            std::getline(std::cin, vin);

            auto matches = findByVinContains(cars, vin);

            if (matches.empty()) {
                std::cout << "No matches.\n";
                continue;
            }

            std::cout << "\nMatches:\n";
            for (size_t i = 0; i < matches.size(); i++) {
                std::cout << i << ":\n";
                printCars({matches[i]});
            }

            std::cout << "Enter index to delete: ";
            size_t idx;
            std::cin >> idx;
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

            if (idx >= matches.size()) {
                std::cout << "Invalid index.\n";
                continue;
            }

            std::string vinToDelete = matches[idx].getVin();

            cars.erase(
                std::remove_if(cars.begin(), cars.end(),
                    [&](const Car& c) {
                        return toLower(c.getVin()) == toLower(vinToDelete);
                    }),
                cars.end()
            );

            rewriteCSV("test.csv", cars);
            lastResults = cars;

            std::cout << "Car deleted.\n";
        }

        else if (user_search == "export") {
            std::string filename;
            std::cout << "Enter filename: ";
            std::getline(std::cin, filename);

            exportResults(filename, lastResults);
        }

        else if (user_search == "exportscreen") {
            std::string filename;
            std::cout << "Enter filename: ";
            std::getline(std::cin, filename);

            exportScreen(filename, lastResults);
        }

        else if (user_search == "exit") {
            break;
        }
	else if (user_search == "clear"){
		clear_screen();
	}
        else {
            std::cout << "Unknown command.\n";
        }
    }

    return 0;
}
