import { TestBed } from "@angular/core/testing";
import { ModalController } from "@ionic/angular";
import { DummyWebsocket } from "src/app/shared/service/test/dummywebsocket";
import { ChannelAddress, Service, Websocket } from "src/app/shared/shared";
import { Edge } from "../../edge/edge";
import { EdgeConfig, PersistencePriority } from "../../edge/edgeconfig";
import { DummyConfig } from "../../edge/edgeconfig.spec";
import { DummyModalController } from "../../shared/testing/DummyModalController";
import { StatusSingleComponent } from "./status.component";

describe("StatusComponent", () => {
    const testComponent = new EdgeConfig.Component(
        "test0",
        "",
        true,
        false,
        "test",
        {},
        {
            testChannel: {
                accessMode: "RO",
                category: "STATE",
                type: "BOOLEAN",
                unit: "W",
                level: "OK",
                persistencePriority: PersistencePriority.HIGH,
                text: "",
            },
        },
    );

    let statusComponent: StatusSingleComponent;
    const serviceSpy = jasmine.createSpyObj("Service", ["getConfig", "getCurrentEdge"], ["currentEdge"]);
    const edgeSpy: Edge = jasmine.createSpyObj("Edge", [
        "subscribeChannels",
        "isVersionAtLeast",
        "unsubscribeChannels",
    ]);
    const edgeConfigSpy = jasmine.createSpyObj("EdgeConfig", ["listActiveComponents"], ["components"]);
    // initialize variables only in beforeEach, beforeAll
    beforeEach(() => {
        TestBed.configureTestingModule({
            // provide the component-under-test and dependent service
            providers: [
                StatusSingleComponent,
                { provide: ModalController, useClass: DummyModalController },
                { provide: Service, useValue: serviceSpy },
                { provide: Websocket, useClass: DummyWebsocket },
                { provide: Edge, useValue: edgeSpy },
                { provide: EdgeConfig, useValue: edgeConfigSpy },
            ],
        });
        const valueEdgeSpy = TestBed.inject(Edge) as jasmine.SpyObj<Edge>;
        valueEdgeSpy.isVersionAtLeast.and.returnValue(false); // check should be false then
        valueEdgeSpy.unsubscribeChannels.and.callThrough();

        const valueServiceSpy = TestBed.inject(Service) as jasmine.SpyObj<Service>;
        valueServiceSpy.getCurrentEdge.and.returnValue(Promise.resolve(DummyConfig.dummyEdge({})));

        const valueEdgeConfigSpy = TestBed.inject(EdgeConfig) as jasmine.SpyObj<EdgeConfig>;
        valueEdgeConfigSpy.listActiveComponents.and.returnValue([
            { category: { icon: "", title: "title" }, components: [testComponent] },
        ]);
        spyPropertyGetter(valueEdgeConfigSpy, "components").and.returnValue({ [testComponent.id]: testComponent });
        valueServiceSpy.getConfig.and.resolveTo(TestBed.inject(EdgeConfig));

        statusComponent = TestBed.inject(StatusSingleComponent);
    });

    it("Test add Channels for subscription", async () => {
        await statusComponent.ngOnInit();
        spyOn<any>(statusComponent, "getStateChannels")
            .withArgs(testComponent.id)
            .and.resolveTo({ ["testChannel"]: new ChannelAddress(testComponent.id, "testChannel") });

        await statusComponent.subscribeInfoChannels(testComponent);
        expect(statusComponent.subscribedInfoChannels.length).toBe(2);
        expect(statusComponent.subscribedInfoChannels).toContain(new ChannelAddress(testComponent.id, "State"));
        expect(statusComponent.subscribedInfoChannels).toContain(new ChannelAddress(testComponent.id, "testChannel"));
    });
});

// TODO should be some common method
function spyPropertyGetter<T, K extends keyof T>(spyObj: jasmine.SpyObj<T>, propName: K): jasmine.Spy<() => T[K]> {
    return Object.getOwnPropertyDescriptor(spyObj, propName)?.get as jasmine.Spy<() => T[K]>;
}
