import axios from "axios";
import type { Metadata } from "../Metadata";
import { ResourceEventType } from "./ResourceEventType";
import { AdvancedWebSocket } from "./AdvancedWebSocket";
import { WebsocketSession } from "./WebsocketSession";

export interface HasMetadata {
  metadata: Metadata
}

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

export class ResourceRepo<T extends HasMetadata>
{
    private resources: Ref<T[] | undefined> = ref(undefined);
    private websocket?: AdvancedWebSocket;

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

    listen(resource: string)
    {
        WebsocketSession.get((token: string) => {
            const webSocket = AdvancedWebSocket.open(StringUtils.format("%s/watch/%s/%s?token=%s", ApiConfig.getWsBase(), resource, this.getNamespace(), token),
                (event) => {
                    const data = JSON.parse(event.data) as ResourceEvent<T>;
                    switch (data.type)
                    {
                        case ResourceEventType.INIT: 
                        {
                            this.add(data.resources);
                            break;
                        }
                        case ResourceEventType.ADDED:
                        {
                            this.add(data.resources);
                            break;
                        }
                        case ResourceEventType.MODIFIED:
                        {
                            this.update(data.resources);
                            break;
                        }
                        case ResourceEventType.DELETED:
                        {
                            this.delete(data.resources);
                            break;
                        }
                    }
                }, 
                () => { }
            );
            this.websocket = webSocket;
        });
    }

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

    private delete(resources: T[])
    {
        if (this.resources.value == null)
        {
            this.resources.value = [];
        }
        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[])
    {
        if (this.resources.value == null)
        {
            this.resources.value = [];
        }
        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() 
    {
        if (this.websocket)
        {
            this.websocket.close();
        }
    }

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

    private refresh(resourceType: string)
    {
        const namespace = this.getNamespace();
        let url = StringUtils.format("%s/resources/%s", ApiConfig.getHttpBase(), 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;
    }
}