/*
 * The MIT License (MIT)

 * Copyright (c) 2025 libsig 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 "ecc.h"
#include "args_parser.h"

#include <vector>
#include <string>
#include <iostream>

int TestECC(int* failed_cases, std::vector<std::string>* failed_cases_name) {
  int total_cases = 3;

  libsig::ECCrypto ecc;
  // case 0
  auto key = ecc.generateKeyPair();
  if (!key) {
    (*failed_cases)++;
    failed_cases_name->push_back("ECC-case-0");
  }

  std::string pubKeyPEM = ecc.publicKeyToPEM(key);
  std::string privKeyPEM = ecc.privateKeyToPEM(key);
    
  ecc.saveKeyTofile("public_key.pem", pubKeyPEM);
  ecc.saveKeyTofile("private_key.pem", privKeyPEM);

  auto privkey = ecc.LoadPrivateKey("private_key.pem");
  auto pubkey = ecc.LoadPublicKey("public_key.pem");

  std::string message = "This is a test message for ECC signing";
  std::string signature;

  // case 1
  if (ecc.signMessage(privkey, message, signature)) {
  	// case 2
    if (!ecc.verifySignature(pubkey, message, signature)) {
      (*failed_cases)++;
      failed_cases_name->push_back("ECC-case-2");
    }
  } else {
    (*failed_cases)++;
    failed_cases_name->push_back("ECC-case-1");
  }

  return total_cases;
}

int TestArgsParser(int* failed_cases, std::vector<std::string>* failed_cases_name) {
  int total_cases = 9;

  size_t size = 0;
  char** prtArray = nullptr;

  // case 0
  const char* strings_0[] = {"./libsig", "keys", "key"};
  size = std::size(strings_0);
  prtArray = const_cast<char**>(strings_0);
  libsig::ArgsParser parser_0(size, prtArray);

  if (parser_0.ArgsCheck() == false) {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-0-0");
  }

  if (parser_0.command() != "keys") {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-0-1");
  }

  if (parser_0.file() != "key") {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-0-2");
  }

  // case 1
  const char* strings_1[] = {"./libsig", "sign", "image.jpg", "private_key.pem"};
  size = std::size(strings_1);
  prtArray = const_cast<char**>(strings_1);
  libsig::ArgsParser parser_1(size, prtArray);

  if (parser_1.ArgsCheck() == false) {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-1-0");
  }

  if (parser_1.command() != "sign") {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-1-1");
  }

  if (parser_1.file() != "image.jpg") {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-1-2");
  }

  if (parser_1.private_key() != "private_key.pem") {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-1-3");
  }

  // case 2
  const char* strings_2[] = {"./libsig", "verify", "image.jpg", "public_key.pem"};
  size = std::size(strings_2);
  prtArray = const_cast<char**>(strings_2);
  libsig::ArgsParser parser_2(size, prtArray);
  if (parser_2.ArgsCheck() == false) {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-2-0");
  }

  if (parser_2.command() != "verify") {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-2-1");
  }

  if (parser_2.file() != "image.jpg") {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-2-2");
  }

  if (parser_2.public_key() != "public_key.pem") {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-2-3");
  }

  // case 3
  const char* strings_3[] = {"./libsig", "verify", "image.jpg"};
  size = std::size(strings_3);
  prtArray = const_cast<char**>(strings_3);
  libsig::ArgsParser parser_3(size, prtArray);
  if (parser_3.ArgsCheck() == true) {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-3-0");
  }

  // case 4
  const char* strings_4[] = {"./libsig", "keys", "key", "image.jpg"};
  size = std::size(strings_4);
  prtArray = const_cast<char**>(strings_4);
  libsig::ArgsParser parser_4(size, prtArray);
  if (parser_4.ArgsCheck() == true) {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-4-0");
  }

  // case 5
  const char* strings_5[] = {"./libsig", "sign", "image.jpg"};
  size = std::size(strings_5);
  prtArray = const_cast<char**>(strings_5);
  libsig::ArgsParser parser_5(size, prtArray);
  if (parser_5.ArgsCheck() == true) {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-5-0");
  }

  // case 7
  const char* strings_6[] = {"./libsig", "sign", "image.jpg", "private_key.pem"};
  size = std::size(strings_6);
  prtArray = const_cast<char**>(strings_6);
  libsig::ArgsParser parser_6(size, prtArray);
  if (parser_6.ArgsCheck() == false) {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-6-0");
  }

  // case 8
  const char* strings_8[] = {"./libsig", "keys"};
  size = std::size(strings_8);
  prtArray = const_cast<char**>(strings_8);
  libsig::ArgsParser parser_8(size, prtArray);

  if (parser_8.ArgsCheck() == true) {
    (*failed_cases)++;
    failed_cases_name->push_back("parser-case-8-0");
  }

  return total_cases;
}

int main(int argc, char** argv) {
  int total_cases = 0;
  int failed_cases = 0;
  std::vector<std::string> failed_cases_name;

  total_cases += TestECC(&failed_cases, &failed_cases_name);
  total_cases += TestArgsParser(&failed_cases, &failed_cases_name);
  
  std::cout << total_cases - failed_cases << " cases passed. " 
            << failed_cases << " cases failed." << std::endl;

  if (failed_cases_name.size() != 0) {
    std::cout << "Failed cases: " << std::endl;
    for (int i = 0; i < failed_cases_name.size(); ++i) {
      std::cout << failed_cases_name[i] << std::endl;
    }
  }

  return 0;
}