import axios from "axios";
import type { Metadata } from "./Metadata";

export interface HasMetadata {
  metadata: Metadata
}

export class ResourceEvent<T extends HasMetadata>
{
    constructor (
        public type: string,
        public resources: T[]
    ) {}
}

export class ResourceRepo<T extends HasMetadata>
{
    private resources: Ref<T[]> = ref([]);
    private interval: NodeJS.Timeout | undefined = undefined;
    private websocket: WebSocket | undefined = undefined;

    static init<T extends HasMetadata>()
    {
        return new ResourceRepo<T>();
    }

    listen(resource: string)
    {
        const websocket = new WebSocket(StringUtils.format("%s/api/watch/%s/%s", useRuntimeConfig().public.apiWsBase, resource, this.getNamespace()));
        websocket.addEventListener('open', () => {
            console.info("Opened Websocket.");
        })
        websocket.addEventListener("message", (event) => {
            const data = JSON.parse(event.data) as ResourceEvent<T>;
            console.info(StringUtils.format("[%s] Resource", data.type));
            switch (data.type)
            {
                case "INIT": 
                {
                    this.add(data.resources);
                    break;
                }
                case "ADDED":
                {
                    this.add(data.resources);
                    break;
                }
                case "MODIFIED":
                {
                    this.update(data.resources);
                    break;
                }
                case "DELETED":
                {
                    this.delete(data.resources);
                    break;
                }
            }
        });
        const interval = setInterval(() => {
            console.info("[PING]");
            websocket.send('[PING]');
        }, 5000);
        websocket.addEventListener("close", () => {
            console.info("Closing websocket.");
            clearTimeout(interval);
        });
        this.websocket = websocket;
    }

    private add(resources: T[])
    {
        this.resources.value.push(...resources);
    }

    private delete(resources: T[])
    {
        for (const resource of resources)
        {
            const index = this.resources.value.findIndex(item => item.metadata.uid === resource.metadata.uid);
            if (index != null)
            {
                this.resources.value.splice(index, 1);
            }
        }
    }

    private update(resources: T[])
    {
        for (const resource of resources)
        {
            const index = this.resources.value.findIndex(item => item.metadata.uid === resource.metadata.uid);
            if (index != null)
            {
                this.resources.value[index] = resource;
            }
        }
    }

    load(resourceType: string)
    {
        this.refresh(resourceType);
        return this;
    }

    clear() 
    {
        clearTimeout(this.interval);
        if (this.websocket != null)
        {
            this.websocket.close();
        }
    }

    get()
    {
        return computed(() => {
            return this.resources.value.toSorted((a, b) => a.metadata.name.localeCompare(b.metadata.name));
        })
    }

    private refresh(resourceType: string)
    {
        const namespace = this.getNamespace();
        let url = StringUtils.format("%s/resources/%s", useRuntimeConfig().public.apiBase, resourceType);
        if (namespace)
        {
            url = StringUtils.format("%s/%s", url, namespace);
        }
        axios.get<T[]>(url)
        .then((response) => {
            this.resources.value = response.data;
        });
    }

    private getNamespace()
    {
        return useRoute().params.namespace as string;
    }
}