#include "utils/Utils.h"
#include <cstring>
#include <iterator>
#include <vector>
#include <iostream>
#include <algorithm>
#include <fstream>
#include <zip.h>
#include <cstdlib>
#include <filesystem>
#include <string>
#include <cctype>
#include <SDL.h>

#ifdef __ANDROID__
#include <android/log.h>
#endif

namespace ZL
{
    std::string readTextFile(const std::string &filename) {
#ifdef __ANDROID__
        SDL_RWops* file = SDL_RWFromFile(filename.c_str(), "rb");
        if (!file) {
#ifdef DEBUG
            __android_log_print(ANDROID_LOG_ERROR, "Utils",
                               "Failed to open: %s", filename.c_str());
#endif
            return "";
        }

        Sint64 size = SDL_RWsize(file);
        if (size <= 0) {
            SDL_RWclose(file);
            return "";
        }

        std::string content(size, '\0');
        if (SDL_RWread(file, &content[0], size, 1) != 1) {
            SDL_RWclose(file);
            return "";
        }

        SDL_RWclose(file);
        return content;
#else
        std::ifstream f(filename);
        if (!f.is_open()) {
            return "";
        }
        return std::string((std::istreambuf_iterator<char>(f)),
                           std::istreambuf_iterator<char>());
#endif
    }

    std::vector<char> readFile(const std::string &filename) {
#ifdef __ANDROID__
        SDL_RWops* file = SDL_RWFromFile(filename.c_str(), "rb");
        if (!file) {
            return {};
        }

        Sint64 size = SDL_RWsize(file);
        if (size <= 0) {
            SDL_RWclose(file);
            return {};
        }

        std::vector<char> data(size);
        if (SDL_RWread(file, data.data(), size, 1) != 1) {
            SDL_RWclose(file);
            return {};
        }

        SDL_RWclose(file);
        return data;
#else
        std::ifstream file(filename, std::ios::binary | std::ios::ate);
        if (!file) {
            return {};
        }

        std::streamsize size = file.tellg();
        if (size <= 0) {
            return {};
        }

        file.seekg(0, std::ios::beg);
        std::vector<char> buffer(size);

        if (!file.read(buffer.data(), size)) {
            return {};
        }

        return buffer;
#endif
    }

	std::vector<char> readFileFromZIP(const std::string& filename, const std::string& zipfilename) {
#ifdef EMSCRIPTEN
        int zipErr = 0;
        zip_t* archive = zip_open(zipfilename.c_str(), ZIP_RDONLY, &zipErr);
        if (!archive) {
			std::cout << "Failed to open ZIP: " << zipfilename << " (error code " << zipErr << ")" << std::endl;
            throw std::runtime_error("Failed to open ZIP: " + zipfilename);
        }

        std::string cleanFilename = filename;
        if (cleanFilename.rfind("./", 0) == 0) cleanFilename = cleanFilename.substr(2);

        zip_stat_t fileStat;
        zip_stat_init(&fileStat);
        if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0 || !(fileStat.valid & ZIP_STAT_SIZE)) {
            zip_close(archive);
			std::cout << "Failed to stat file in ZIP: " << cleanFilename << " in " << zipfilename << std::endl;
            throw std::runtime_error("Can't stat file in ZIP: " + cleanFilename);
        }

        zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
        if (!zipFile) {
            zip_close(archive);
			std::cout << "Failed to open file in ZIP: " << cleanFilename << std::endl;
            throw std::runtime_error("Can't open file in ZIP: " + cleanFilename);
        }

        std::vector<char> fileData(static_cast<size_t>(fileStat.size));
        zip_int64_t bytesRead = zip_fread(zipFile, fileData.data(), fileStat.size);

        // Обязательно закрываем всё перед проверкой результата или throw
        zip_fclose(zipFile);
        zip_close(archive);

        if (bytesRead < 0 || static_cast<zip_uint64_t>(bytesRead) != fileStat.size) {
			std::cout << "Failed to read file from ZIP: " << cleanFilename << " (bytes read: " << bytesRead << ", expected: " << fileStat.size << ")" << std::endl;
            throw std::runtime_error("Error reading data from ZIP: " + cleanFilename);
        }

        return fileData;
#else

		if (zipfilename.empty()) {
			std::cerr << "readFileFromZIP: zipfilename empty" << std::endl;
			return {};
		}

		int zipErr = 0;
		zip_t* archive = zip_open(zipfilename.c_str(), ZIP_RDONLY, &zipErr);
		if (!archive) {
			std::cerr << "readFileFromZIP: zip_open failed for: " << zipfilename
				<< " (error code " << zipErr << ")" << std::endl;
			return {};
		}

		std::string cleanFilename = filename;
		if (cleanFilename.rfind("./", 0) == 0) {
			cleanFilename = cleanFilename.substr(2);
		}

		zip_stat_t fileStat;
		if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0) {
			std::cerr << "readFileFromZIP: file not found in ZIP: " << cleanFilename
				<< " in " << zipfilename << std::endl;
			zip_close(archive);
			return {};
		}

		zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
		if (!zipFile) {
			std::cerr << "readFileFromZIP: zip_fopen failed for " << cleanFilename << std::endl;
			zip_close(archive);
			return {};
		}

		std::vector<char> fileData;
		fileData.resize(static_cast<size_t>(fileStat.size));

		zip_int64_t readBytes = zip_fread(zipFile, fileData.data(), static_cast<zip_uint64_t>(fileData.size()));
		if (readBytes < 0) {
			std::cerr << "readFileFromZIP: zip_fread failed for " << cleanFilename << std::endl;
			zip_fclose(zipFile);
			zip_close(archive);
			return {};
		}

		zip_fclose(zipFile);
		zip_close(archive);

		return fileData;
#endif
	}

	bool findString(const char* in, char* list)
	{
		size_t thisLength = strlen(in);
		while (*list != 0)
		{
			size_t length = strcspn(list, " ");

			if (thisLength == length)
				if (!strncmp(in, list, length))
					return true;

			list += length;
			list += 1;
		}
		return false;
	}
};