from typing import Optional,Any
from pathlib import Path
import time
from datetime import datetime

from .store import Store

class Node:
    def __init__(self,
                name: str,
                cls: type,
                init_input: Optional[dict[str, Any]] = None,
                run_input: Optional[dict[str, Any]] = None,
                run_output: Optional[dict[str, Any]] = None,
                stores_path: Optional[str | Path] = None,
                database_type: str = "cache",
                context: Optional[dict[str, Any]] = None,
                ):
        self.name = name
        self.cls = cls
        self.init_input = init_input if init_input is not None else {}
        self.run_input = run_input if run_input is not None else {}
        self.run_output = run_output if run_output is not None else {}

        self.store = Store(self.name, stores_path / self.name, database_type)

        self.meta = {
            "version":0,
        }

        meta = self.store.get_meta()
        if meta is not None:
            self.meta.update(meta)
        else:
            self.store.set_meta(self.meta)

        self._is_loaded = False

        self.context = {
            "node":{
                "name":self.name,
                "store":self.store
            }
        }
        self.context.update(context if context is not None else {})

        

    def run(self, run_context: dict[str, Any], run_input: dict[str,Any]):
        final_run_input = {
            key: run_input.get(key, default)
            for key, default in self.run_input.items()
        }

        start_time = time.time()
        output: dict = self.instance.run(run_context=run_context,**final_run_input)
        cost_time = time.time() - start_time

        final_output = {
            key: output.get(key, default)
            for key, default in self.run_output.items()
        }

        temp={
            "meta":{
                "name":self.name,
                "version":self.meta["version"],
                "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "cost_time":cost_time
            },
            "output":final_output
        }
        task = run_context["task"]
        self.store.set(task,temp)

    def generate_task(self):
        self.load()
        return self.instance.generate_task()

    def load(self):
        if self._is_loaded:
            return
        self.instance = self.cls(context=self.context,**self.init_input)
        self._is_loaded = True

    def unload(self):
        self.instance = None
        self._is_loaded = False

    def bump_version(self):
        self.meta["version"] += 1
        if self.meta["version"] > 1000:
            self.meta["version"] = 0
        self.store.set_meta(self.meta)

    def requires_run(self,task: int | str) -> bool:
        if not self.store.exists(task):
            return True
        if self.meta["version"] == self.store.get(task)["meta"]["version"]:
            return False
        return True

    def find_run_completed_tasks(self) -> list[int | str]:
        version = self.meta["version"]
        completed_tasks = []
        for task in self.store.get_all_keys():
            if version == self.store.get(task)["meta"]["version"]:
                completed_tasks.append(task)
        return completed_tasks

    def find_run_output(self,task: int | str) -> dict[str,Any]:
        return self.store.get(task)["output"]
