import fs from 'fs/promises';
import chalk from 'chalk';
import path from 'path';
import { nodeModels, edgeModels } from './model/import.js'
import Edge from "./model/edge.js"
import { fileURLToPath } from "url"
const __dirname = path.dirname(fileURLToPath(import.meta.url))

export default class Database {

    PERSONAL_DATA_PATH = path.join(__dirname, '../../db/personal')

    nodes = new Array(10000).fill(0);
    edges = new Array(10000).fill(0);

    constructor() {
        let values = Object.values(nodeModels)
        for(let i = 0; i < values.length; i++) {
            let key = values[i].constructor.name
            key = key.toLowerCase() + "s"
            this[key] = values[i]
        }
        let eValues = Object.values(edgeModels)
        for(let i = 0; i < eValues.length; i++) {
            let key = eValues[i].constructor.name
            this[key] = eValues[i]
        }
        this.edge = new Edge()
        this.loadData()
    }

    getCurrentIndex(model) {
        return model.indices[1] - model.indices[0]
    }

    getNextIndex(model) {
        return model.indices[1] - model.indices[0] + 1
    }

    // add a get in here that returns a safe copy, not the actual reference

    addNode(prefix, node) {
        try {
            let model = nodeModels[prefix]
            if(model) {
                let toAdd = {
                    id: this.getNextIndex(model),
                    ...node,
                }
                if(!toAdd.created) toAdd.created = global.currentTime()
                let schema = model.schema
                let result = schema.safeParse(toAdd)
                if(result.success) {
                    this.nodes[model.indices[1]] = toAdd
                    model.indices[1]++;
                } else {
                    console.error(result.error)
                    throw new global.ServerError(400, "Invalid Data!");
                }
            } else {
                throw new Error("Type does not exist for node: " + id)
            }
        } catch(e) {
            throw e
        }
    }

    deleteNode(prefix, id) {
        try {
            let model = nodeModels[prefix]
            if(model) {
                this.nodes[model.indices[0] + (id - 1)] = 0
            } else {
                throw new Error("Type does not exist for node: " + id)
            }
        } catch(e) {
            throw e
        }
    }

    updateNode(prefix, id, toEdit) {
        console.log("update node, ", toEdit)
        try {
            let model = nodeModels[prefix]
            if(model) {
                let schema = model.schema
                let result = schema.safeParse(toEdit)
                if(result.success) {
                    this.nodes[model.indices[0] + (id - 1)] = toEdit
                } else {
                    console.error(result.error)
                    throw new global.ServerError(400, "Invalid Data!");
                }
            } else {
                throw new Error("Type does not exist for node: " + prefix)
            }
        } catch(e) {
            throw e
        }
    }

    addEdge(prefix, edge) {
        try {
            let type = prefix
            let model = edgeModels[type]
            if(model) {
                let toAdd = {
                    id: model.indices[1] - model.indices[0] + 1,
                    ...edge
                }
                if(!toAdd.created) toAdd.created = global.currentTime()
                let schema = model.schema
                let result = schema.safeParse(toAdd)
                if(result.success) {
                    this.edges[model.indices[1]] = toAdd
                    model.indices[1]++;
                } else {
                    console.error(result.error)
                    throw new global.ServerError(400, "Invalid Data!");
                }
            } else {
                throw new Error("Type does not exist for edge: " + prefix)
            }
        } catch(e) {
            throw e
        }
    }

    deleteEdge(prefix, id) {
        try {
            let model = edgeModels[prefix]
            if(model) {
                console.log(model.indices[0] + (id - 1))
                this.edges[model.indices[0] + (id - 1)] = 0
                console.log(this.edges[model.indices[0] + (id - 1)])
            } else {
                throw new Error("Type does not exist for edge: " + prefix)
            }
        } catch(e) {
            throw e
        }
    }

    async loadData() {
        const dbData = await fs.readFile(path.join(process.cwd(), 'db/db.json'), 'utf8');
        let dbJson;
        try {
            dbJson = JSON.parse(dbData);
        } catch {
            dbJson = []
        }

        let nodes = dbJson["nodes"];
        let entries = Object.entries(nodes)
        for(let i=0; i<entries.length; i++) {
            let entry = entries[i]
            let id = entry[0]; let node = entry[1];
            this.addNode(id.split("-")[0], node)
        }

        let edges = dbJson["edges"];
        let edgeEntries = Object.entries(edges)
        for(let i=0; i<edgeEntries.length; i++) {
            let entry = edgeEntries[i]
            let id = entry[0]; let node = entry[1];
            this.addEdge(id.split("-")[0], node)
        }

        setInterval(() => {
            global.db.saveData()
        }, 5000)
    }

    async saveData() {
        let data = {
            "nodes": {},
            "edges": {}
        }

        let nModels = Object.values(nodeModels)
        this.nodes.forEach((entry, i) => {
            if(entry) {
                for(let j = 0; j < nModels.length; j++) {
                    let model = nModels[j]
                    let indices = model.indices
                    if(i >= indices[0] && i < indices[1]) {
                        let prefix = model.prefix
                        data.nodes[prefix + "-" + entry.id] = entry
                    }
                }
            }
        })

        let eModels = Object.values(edgeModels)
        this.edges.forEach((entry, i) => {
            if(entry) {
                for(let j = 0; j < eModels.length; j++) {
                    let model = eModels[j]
                    let indices = model.indices
                    if(i >= indices[0] && i < indices[1]) {
                        let prefix = model.prefix
                        data.edges[prefix + "-" + entry.id] = entry
                    }
                }
            }
        })

        let string = JSON.stringify(data, null, 4)
        await fs.writeFile(path.join(process.cwd(), 'db/db.json'), string, "utf8");
    }
}