/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2025 The FreeBSD Foundation
 *
 * Portions of this software were developed by
 * Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
 * the FreeBSD Foundation
 */

#include <atf-c.h>
#include <private/pkg_osvf.h>
#include <stdlib.h>


char *osvf_json_path = TESTING_TOP_DIR "/lib/FBSD-2025-05-28.json";

ATF_TC_WITHOUT_HEAD(osvfdetect);
ATF_TC_WITHOUT_HEAD(osvfopen);
ATF_TC_WITHOUT_HEAD(osvfparse);

#ifndef ATF_CHECK_INTEQ
#define ATF_CHECK_INTEQ ATF_CHECK_EQ
#endif

ATF_TC_BODY(osvfdetect, tc)
{
	struct pkg_audit_ecosystem test_rtn_ecosystem_struct[] =
	{
		{"AlmaLinux", "AlmaLinux", NULL},
		{"AlmaLinux:8", "AlmaLinux", NULL},
		{"Alpine", "Alpine", NULL},
		{"Alpine:v3.16", "Alpine", NULL},
		{"Android", "Android", NULL},
		{"Bioconductor", "Bioconductor", NULL},
		{"Bitnami", "Bitnami", NULL},
		{"Chainguard", "Chainguard", NULL},
		{"ConanCenter", "ConanCenter", NULL},
		{"CRAN", "CRAN", NULL},
		{"crates.io", "crates.io", NULL},
		{"Debian", "Debian", NULL},
		{"Debian:13", "Debian", NULL},
		{"FreeBSD", "FreeBSD", NULL},
		{"FreeBSD:ports", "FreeBSD", NULL},
		{"FreeBSD:src:14.3", "FreeBSD", NULL},
		{"FreeBSD:kernel:14.3", "FreeBSD", NULL},
		{"GHC", "GHC", NULL},
		{"GitHub Actions", "GitHub Actions", NULL},
		{"Go", "Go", NULL},
		{"Hackage", "Hackage", NULL},
		{"Hex", "Hex", NULL},
		{"Kubernetes", "Kubernetes", NULL},
		{"Linux", "Linux", NULL},
		{"Mageia", "Mageia", NULL},
		{"Mageia:9", "Mageia", NULL},
		{"Maven", "Maven", NULL},
		{"Maven:https://repo1.maven.org/maven2/", "Maven", NULL},
		{"MinimOS", "MinimOS", NULL},
		{"npm", "npm", NULL},
		{"NuGet", "NuGet", NULL},
		{"openSUSE", "openSUSE", NULL},
		{"OSS-Fuzz", "OSS-Fuzz", NULL},
		{"Packagist", "Packagist", NULL},
		{"Photon OS", "Photon OS", NULL},
		{"Photon OS:3.0", "Photon OS", NULL},
		{"Pub", "Pub", NULL},
		{"PyPI", "PyPI", NULL},
		{"Red Hat", "Red Hat", NULL},
		{"Red Hat:rhel_aus:8.4::appstream", "Red Hat", NULL},
		{"Rocky Linux", "Rocky Linux", NULL},
		{"RubyGems", "RubyGems", NULL},
		{"SUSE", "SUSE", NULL},
		{"SwiftURL", "SwiftURL", NULL},
		{"Ubuntu", "Ubuntu", NULL},
		{"Ubuntu:22.04:LTS", "Ubuntu", NULL},
		{"Ubuntu:Pro:18.04:LTS", "Ubuntu", NULL},
		{"Wolfi", "Wolfi", NULL},
	};

	unsigned int test_rtn_reference[] =
	{
		OSVF_REFERENCE_UNKNOWN,
		OSVF_REFERENCE_ADVISORY,
		OSVF_REFERENCE_ARTICLE,
		OSVF_REFERENCE_DETECTION,
		OSVF_REFERENCE_DISCUSSION,
		OSVF_REFERENCE_REPORT,
		OSVF_REFERENCE_FIX,
		OSVF_REFERENCE_INTRODUCED,
		OSVF_REFERENCE_PACKAGE,
		OSVF_REFERENCE_EVIDENCE,
		OSVF_REFERENCE_WEB
	};

	char *test_input_reference[] =
	{
		"NOTAVAIL",
		"ADVISORY",
		"ARTICLE",
		"DETECTION",
		"DISCUSSION",
		"REPORT",
		"FIX",
		"INTRODUCED",
		"PACKAGE",
		"EVIDENCE",
		"WEB"
	};

	unsigned int test_rtn_event[] =
	{
		OSVF_EVENT_VERSION_UNKNOWN,
		OSVF_EVENT_VERSION_SEMVER,
		OSVF_EVENT_VERSION_ECOSYSTEM,
		OSVF_EVENT_VERSION_GIT
	};

	char *test_input_event[] =
	{
		"SOMETHING",
		"SEMVER",
		"ECOSYSTEM",
		"GIT"
	};

	int i = 0;


	for(i = 0; i < 47; i ++)
	{
		struct pkg_audit_ecosystem *ecosystem = pkg_osvf_get_ecosystem(test_rtn_ecosystem_struct[i].original);

		ATF_REQUIRE_STREQ(ecosystem->name, test_rtn_ecosystem_struct[i].name);
		ATF_REQUIRE_STREQ(ecosystem->original, test_rtn_ecosystem_struct[i].original);

		pkg_osvf_free_ecosystem(ecosystem);
	}

	for(i = 0; i < 10; i ++)
	{
		ATF_REQUIRE(pkg_osvf_get_reference(test_input_reference[i]) == test_rtn_reference[i]);
	}

	for(i = 0; i < 4; i ++)
	{
		ATF_REQUIRE(pkg_osvf_get_event(test_input_event[i]) == test_rtn_event[i]);
	}

}

ATF_TC_BODY(osvfopen, tc)
{
	static ucl_object_t *obj = NULL;

	obj = pkg_osvf_open(osvf_json_path);

	ATF_REQUIRE(obj != NULL);
	ucl_object_unref(obj);

	ATF_REQUIRE(pkg_osvf_create_entry(NULL) == NULL);
}


ATF_TC_BODY(osvfparse, tc)
{
	static ucl_object_t *obj = NULL;
	char buf[1024];
	char *version_strs[] =
	{
		"1.0.0",
		"0.0.1",
		"1.1.0_1",
		"1.0.9_1",
		"c14e07db4",
		"ae637a3ad"
	};
	unsigned int version_types[] =
	{
		OSVF_EVENT_VERSION_SEMVER,
		OSVF_EVENT_VERSION_ECOSYSTEM,
		OSVF_EVENT_VERSION_GIT,
	};
	char *name_strs[] =
	{
		"osvf-test-package10",
		"osvf-test-package11",
		"osvf-test-package12"
	};
	int reference_types[] =
	{
		OSVF_REFERENCE_ADVISORY,
		OSVF_REFERENCE_ARTICLE,
		OSVF_REFERENCE_DETECTION,
		OSVF_REFERENCE_DISCUSSION,
		OSVF_REFERENCE_REPORT,
		OSVF_REFERENCE_FIX,
		OSVF_REFERENCE_INTRODUCED,
		OSVF_REFERENCE_PACKAGE,
		OSVF_REFERENCE_EVIDENCE,
		OSVF_REFERENCE_WEB
	};
	struct pkg_osvf_reference *references = NULL;
	int pos = 0;
	int subpos = 0;
	int otherpos = 0;

	obj = pkg_osvf_open(osvf_json_path);
	ATF_REQUIRE(obj != NULL);

	struct pkg_osvf_entry *oentry = pkg_osvf_create_entry(obj);
	ucl_object_unref(obj);

	ATF_REQUIRE(oentry != NULL);
	struct pkg_audit_entry *entry = &oentry->audit;

	/* First package's first name is the primary name */
	ATF_CHECK_STREQ(entry->packages.d[0].names.d[0].pkgname, "osvf-test-package10");
	ATF_CHECK_STREQ(entry->desc, "OSVF test");
	ATF_CHECK_STREQ(entry->url, "https://www.freebsd.org/");
	ATF_CHECK_STREQ(entry->id, "FreeBSD-2025-05-28");

	references = oentry->references;

	while(references)
	{
		ATF_CHECK_STREQ(references->url, "https://www.freebsd.org/");
		ATF_CHECK_INTEQ(references->type, reference_types[pos++]);
		references = references->next;
	}

	/* Check all versions across all packages (flat iteration) */
	pos = 0;
	otherpos = 0;

	vec_foreach(entry->packages, pi) {
		struct pkg_audit_package *p = &entry->packages.d[pi];
		vec_foreach(p->versions, vi) {
			struct pkg_audit_versions_range *vers = &p->versions.d[vi];
			ATF_CHECK_INTEQ(vers->type, version_types[otherpos++]);
			ATF_CHECK_STREQ(vers->v2.version, version_strs[pos++]);
			ATF_CHECK_INTEQ(vers->v2.type, LTE);
			ATF_CHECK_INTEQ(vers->v2.osv_type, OSVF_EVENT_FIXED);
			ATF_CHECK_STREQ(vers->v1.version, version_strs[pos++]);
			ATF_CHECK_INTEQ(vers->v1.type, GTE);
			ATF_CHECK_INTEQ(vers->v1.osv_type, OSVF_EVENT_INTRODUCED);
		}
	}

	/* Check names across all packages */
	pos = 0;

	vec_foreach(entry->packages, pi) {
		struct pkg_audit_package *p = &entry->packages.d[pi];
		vec_foreach(p->names, ni) {
			ATF_CHECK_STREQ(p->names.d[ni].pkgname, name_strs[pos++]);
		}
	}

	/* Check packages individually */
	pos = 0;
	subpos = 0;
	otherpos = 0;

	vec_foreach(entry->packages, pi) {
		struct pkg_audit_package *p = &entry->packages.d[pi];
		ATF_CHECK_STREQ(p->ecosystem->name, "FreeBSD");
		ATF_CHECK_STREQ(p->names.d[0].pkgname, name_strs[pos++]);

		vec_foreach(p->versions, vi) {
			struct pkg_audit_versions_range *vers = &p->versions.d[vi];
			ATF_CHECK_INTEQ(vers->type, version_types[otherpos++]);
			ATF_CHECK_STREQ(vers->v2.version, version_strs[subpos++]);
			ATF_CHECK_INTEQ(vers->v2.type, LTE);
			ATF_CHECK_INTEQ(vers->v2.osv_type, OSVF_EVENT_FIXED);
			ATF_CHECK_STREQ(vers->v1.version, version_strs[subpos++]);
			ATF_CHECK_INTEQ(vers->v1.type, GTE);
			ATF_CHECK_INTEQ(vers->v1.osv_type, OSVF_EVENT_INTRODUCED);
		}
	}

	strftime(buf, 1024, "%Y-%m-%dT%H:%M:%SZ", &entry->modified);
	ATF_CHECK_STREQ(buf, "2025-05-26T12:30:00Z");

	strftime(buf, 1024, "%Y-%m-%dT%H:%M:%SZ", &entry->published);
	ATF_CHECK_STREQ(buf, "2025-09-28T16:00:00Z");

	strftime(buf, 1024, "%Y-%m-%dT%H:%M:%SZ", &entry->discovery);
	ATF_CHECK_STREQ(buf, "2025-05-20T09:10:00Z");

	pkg_osvf_free_entry(oentry);

}

ATF_TP_ADD_TCS(tp)
{
	ATF_TP_ADD_TC(tp, osvfdetect);
	ATF_TP_ADD_TC(tp, osvfopen);
	ATF_TP_ADD_TC(tp, osvfparse);

	return (atf_no_error());
}
