from pip_audit_extra.constant import CACHE_FOLDER
from pip_audit_extra.vulnerability.cache.object_hook import JSONObjectHook
from pip_audit_extra.vulnerability.cache.type import VulnerabilityData, Record
from pip_audit_extra.vulnerability.cache.encoder import JSONEncoder

from os.path import exists, join
from os import makedirs
from typing import Final, Optional, Dict
from json import load, dump
from datetime import datetime, timedelta, timezone
from warnings import warn


"""
Raw cache structure:
{
	"GHSA-0000-0000-0000": {
		"updated_at": "2024-01-01T00:00:00",
		"data": {
			"id": "GHSA-0000-0000-0000",
			"fix_versions": ["0.0.0"],
			"severity": "HIGH"
		}
	},
	...
}
"""


class Cache:
	CACHE_FILE_PATH: Final[str] = join(CACHE_FOLDER, "vulnerabilities.json")

	def __init__(self, lifetime: Optional[timedelta] = None) -> None:
		self._data: Dict[str, Record] = {}
		self._object_hook = JSONObjectHook()
		self.lifetime = lifetime or timedelta.min

		self.load()
		self.clean()

	def load(self) -> None:
		try:
			if exists(self.CACHE_FILE_PATH):
				with open(self.CACHE_FILE_PATH, mode="r") as file:
					self._data = load(file, object_hook=self._object_hook)

		except Exception as err:
			warn(f"Cannot load cache from file: {err}")

	def save(self) -> None:
		try:
			if not exists(CACHE_FOLDER):
				makedirs(CACHE_FOLDER, exist_ok=True)

			with open(self.CACHE_FILE_PATH, mode="w") as file:
				dump(self._data, file, cls=JSONEncoder)

		except Exception as err:
			warn(f"Cannot save cache on disk: {err}")

	def add(self, vuln_data: VulnerabilityData) -> None:
		self._data[vuln_data.id] = Record(updated_at=datetime.now(timezone.utc), data=vuln_data)

	def get(self, vuln_id: str) -> Optional[VulnerabilityData]:
		if record := self._data.get(vuln_id):
			if datetime.now(timezone.utc) - record["updated_at"] <= self.lifetime:
				return record["data"]
			else:
				del self._data[vuln_id]

		return None

	def clean(self) -> None:
		for vuln_id in list(self._data):
			if datetime.now(timezone.utc) - self._data[vuln_id]["updated_at"] > self.lifetime:
				del self._data[vuln_id]
