/*
 * The MIT License (MIT)

 * Copyright (c) 2025 GenText-Checker Developers

 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "common.h"
#include "args_parser.h"
#include "ecc.h"

#include <string>
#include <vector>
#include <filesystem>

#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

void generateKeyPair(const std::string& file_path) {
  libsig::ECCrypto ecc;
  auto key = ecc.generateKeyPair();
  std::string pubKeyPEM = ecc.publicKeyToPEM(key);
  std::string privKeyPEM = ecc.privateKeyToPEM(key);
  std::cout << pubKeyPEM << std::endl;
  std::cout << privKeyPEM << std::endl;
  ecc.saveKeyTofile(file_path + "/public_key.pem", pubKeyPEM);
  ecc.saveKeyTofile(file_path + "/private_key.pem", privKeyPEM);
  std::cout << "public key has been written to: " << file_path  << "/public_key.pem" << std::endl;
  std::cout << "private key has been written to: " << file_path << "/private_key.pem" << std::endl;
}

void sign(const std::string& file_path, 
          const std::string& private_key) {
  // Read file to string buffer
  std::ifstream file(file_path, std::ios::binary);
  if (!file) {
    std::cout << "cannot open file: " << file_path << std::endl;
    exit(0);
  }
  auto size = std::filesystem::file_size(file_path);
  std::string buffer(size, '\0');
  file.read(buffer.data(), size);
  file.close();
  // load private key and sign file
  libsig::ECCrypto ecc;
  std::string signature;
  auto privkey = ecc.LoadPrivateKey(private_key);
  if (!ecc.signMessage(privkey, buffer, signature)) {
    std::cout << "cannot sign file: " << file_path << std::endl;
    exit(0);
  }
  std::string base64_signature = libsig::binary_to_base64(signature);
  buffer += base64_signature;

  std::cout << "The signature is: " << base64_signature << std::endl;
  // create a new file
  std::ofstream new_file("signed-"+file_path, std::ios::binary);
  if (!file) {
    std::cout << "cannot create file: " << "signed-"+file_path << std::endl;
    exit(0);
  }
  new_file.write(buffer.data(), buffer.size());
  new_file.close();

  std::cout << "New file is created: " << "signed-"+file_path << std::endl;
}

bool verify(const std::string& file_path, 
            const std::string& public_key) {
  // load public key
  libsig::ECCrypto ecc;
  auto pubKey = ecc.LoadPublicKey(public_key);
  // read last 96 bytes data
  std::ifstream file(file_path, std::ios::binary | std::ios::ate);
  if (!file.is_open()) {
    std::cout << "Failed to open file: " << file_path << std::endl;
    exit(0);
  }

  std::streampos file_size = file.tellg();
  if (file_size < 96) {
    std::cout << "File is smaller than 96 bytes" << std::endl;
    exit(0);
  }

  file.seekg(-96, std::ios::end);

  char buffer[96];
  file.read(buffer, 96);
  std::string result(buffer, 96);

  // verify signature
  std::string signature = libsig::base64_to_binary(result);
  uintmax_t size = fs::file_size(file_path);

  std::ifstream file_1(file_path, std::ios::binary);
  if (!file_1.is_open()) {
    std::cout << "Failed to open file: " + file_path << std::endl;
  }

  std::streamsize bytes_to_read = static_cast<std::streamsize>(size - 96);

  std::string message(bytes_to_read, '\0');
  file_1.read(&message[0], bytes_to_read);

  if (!ecc.verifySignature(pubKey, message, signature)) {
    return false;
  }

  return true;
}

int main(int argc, char** argv) {
  libsig::ArgsParser parser(argc, argv);
  if (parser.ArgsCheck() == false) {
    parser.HelpPage();
    return -1;
  } else {
    if (parser.command() == "keys") {
      generateKeyPair(parser.file());
    } else if (parser.command() == "sign") {
      sign(parser.file(), parser.private_key());
    } else if (parser.command() == "verify") {
      if (verify(parser.file(), parser.public_key())) {
        std::cout << "Verified success." << std::endl;
      } else {
        std::cout << "Verified failed." << std::endl;
      }
    } else {
      std::cout << "Unknow command." << std::endl;
      parser.HelpPage(); 
    }
  }
  return 0;
}