
// Check if running in WebView2
const isWebView = typeof window !== 'undefined' && !!window.chrome?.webview;

// 비동기 프록시 캐싱 (한 번만 초기화) - 매번 접근 시 오버헤드 제거
const machine = isWebView ? window.chrome!.webview!.hostObjects.machine : null;

type MessageCallback = (data: any) => void;

class CommunicationLayer {
    private listeners: MessageCallback[] = [];
    private ws: WebSocket | null = null;
    private isConnected = false;

    constructor() {
        if (isWebView) {
            console.log("[COMM] Running in WebView2 Mode");
            this.isConnected = true; // WebView2 is always connected
            window.chrome!.webview!.addEventListener('message', (event: any) => {
                this.notifyListeners(event.data);
            });
        } else {
            console.log("[COMM] Running in Browser Mode (WebSocket)");
            this.connectWebSocket();
        }
    }

    private connectWebSocket() {
        this.ws = new WebSocket('ws://localhost:8081');

        this.ws.onopen = () => {
            console.log("[COMM] WebSocket Connected");
            this.isConnected = true;
            this.notifyListeners({ type: 'CONNECTION_STATE', connected: true });
        };

        this.ws.onmessage = (event) => {
            try {
                const data = JSON.parse(event.data);
                this.notifyListeners(data);
            } catch (e) {
                console.error("[COMM] JSON Parse Error", e);
            }
        };

        this.ws.onclose = () => {
            console.log("[COMM] WebSocket Closed. Reconnecting...");
            this.isConnected = false;
            this.notifyListeners({ type: 'CONNECTION_STATE', connected: false });
            setTimeout(() => this.connectWebSocket(), 2000);
        };

        this.ws.onerror = (err) => {
            console.error("[COMM] WebSocket Error", err);
        };
    }

    private notifyListeners(data: any) {
        this.listeners.forEach(cb => cb(data));
    }

    public subscribe(callback: MessageCallback) {
        this.listeners.push(callback);
        return () => {
            this.listeners = this.listeners.filter(cb => cb !== callback);
        };
    }

    public getConnectionState(): boolean {
        return this.isConnected;
    }

    // --- API Methods ---

    public async getConfig(): Promise<string> {
        if (isWebView && machine) {
            return await machine.GetConfig();
        } else {
            // WebSocket Request/Response Pattern
            return new Promise((resolve, reject) => {
                // 1. Wait for Connection (max 2s)
                if (!this.isConnected) {
                    // Simple wait logic (could be improved)
                    setTimeout(() => {
                        if (!this.isConnected) reject("WebSocket connection timeout");
                    }, 2000);
                }

                // 2. Send Request with Timeout (max 10s)
                const timeoutId = setTimeout(() => {
                    this.listeners = this.listeners.filter(cb => cb !== handler);
                    reject("Config fetch timeout");
                }, 10000);

                const handler = (data: any) => {
                    if (data.type === 'CONFIG_DATA') {
                        clearTimeout(timeoutId);
                        this.listeners = this.listeners.filter(cb => cb !== handler);
                        resolve(JSON.stringify(data.data));
                    }
                };
                this.listeners.push(handler);
                this.ws?.send(JSON.stringify({ type: 'GET_CONFIG' }));
            });
        }
    }

    public async getIOList(): Promise<string> {
        if (isWebView && machine) {
            return await machine.GetIOList();
        } else {
            return new Promise((resolve, reject) => {
                if (!this.isConnected) {
                    setTimeout(() => {
                        if (!this.isConnected) reject("WebSocket connection timeout");
                    }, 2000);
                }

                const timeoutId = setTimeout(() => {
                    this.listeners = this.listeners.filter(cb => cb !== handler);
                    reject("IO fetch timeout");
                }, 10000);

                const handler = (data: any) => {
                    if (data.type === 'IO_LIST_DATA') {
                        clearTimeout(timeoutId);
                        this.listeners = this.listeners.filter(cb => cb !== handler);
                        resolve(JSON.stringify(data.data));
                    }
                };
                this.listeners.push(handler);
                this.ws?.send(JSON.stringify({ type: 'GET_IO_LIST' }));
            });
        }
    }

    public async getRecipeList(): Promise<string> {
        if (isWebView && machine) {
            return await machine.GetRecipeList();
        } else {
            return new Promise((resolve, reject) => {
                if (!this.isConnected) {
                    setTimeout(() => {
                        if (!this.isConnected) reject("WebSocket connection timeout");
                    }, 2000);
                }

                const timeoutId = setTimeout(() => {
                    this.listeners = this.listeners.filter(cb => cb !== handler);
                    reject("Recipe fetch timeout");
                }, 10000);

                const handler = (data: any) => {
                    if (data.type === 'RECIPE_LIST_DATA') {
                        clearTimeout(timeoutId);
                        this.listeners = this.listeners.filter(cb => cb !== handler);
                        resolve(JSON.stringify(data.data));
                    }
                };
                this.listeners.push(handler);
                this.ws?.send(JSON.stringify({ type: 'GET_RECIPE_LIST' }));
            });
        }
    }

    public async saveConfig(configJson: string): Promise<void> {
        if (isWebView && machine) {
            await machine.SaveConfig(configJson);
        } else {
            this.ws?.send(JSON.stringify({ type: 'SAVE_CONFIG', data: JSON.parse(configJson) }));
        }
    }

    public async sendControl(command: string) {
        if (isWebView && machine) {
            await machine.SystemControl(command);
        } else {
            this.ws?.send(JSON.stringify({ type: 'CONTROL', command }));
        }
    }

    public async moveAxis(axis: string, value: number) {
        if (isWebView && machine) {
            await machine.MoveAxis(axis, value);
        } else {
            this.ws?.send(JSON.stringify({ type: 'MOVE', axis, value }));
        }
    }

    public async setIO(id: number, state: boolean) {
        if (isWebView && machine) {
            await machine.SetIO(id, false, state);
        } else {
            this.ws?.send(JSON.stringify({ type: 'SET_IO', id, state }));
        }
    }

    public async selectRecipe(recipeId: string): Promise<{ success: boolean; message: string; recipeId?: string }> {
        if (isWebView && machine) {
            const resultJson = await machine.SelectRecipe(recipeId);
            return JSON.parse(resultJson);
        } else {
            return new Promise((resolve, reject) => {
                if (!this.isConnected) {
                    setTimeout(() => {
                        if (!this.isConnected) reject({ success: false, message: "WebSocket connection timeout" });
                    }, 2000);
                }

                const timeoutId = setTimeout(() => {
                    this.listeners = this.listeners.filter(cb => cb !== handler);
                    reject({ success: false, message: "Recipe selection timeout" });
                }, 10000);

                const handler = (data: any) => {
                    if (data.type === 'RECIPE_SELECTED') {
                        clearTimeout(timeoutId);
                        this.listeners = this.listeners.filter(cb => cb !== handler);
                        resolve(data.data);
                    }
                };
                this.listeners.push(handler);
                this.ws?.send(JSON.stringify({ type: 'SELECT_RECIPE', recipeId }));
            });
        }
    }

    public async copyRecipe(recipeId: string, newName: string): Promise<{ success: boolean; message: string; newRecipe?: any }> {
        if (isWebView && machine) {
            const resultJson = await machine.CopyRecipe(recipeId, newName);
            return JSON.parse(resultJson);
        } else {
            return new Promise((resolve, reject) => {
                if (!this.isConnected) {
                    setTimeout(() => {
                        if (!this.isConnected) reject({ success: false, message: "WebSocket connection timeout" });
                    }, 2000);
                }

                const timeoutId = setTimeout(() => {
                    this.listeners = this.listeners.filter(cb => cb !== handler);
                    reject({ success: false, message: "Recipe copy timeout" });
                }, 10000);

                const handler = (data: any) => {
                    if (data.type === 'RECIPE_COPIED') {
                        clearTimeout(timeoutId);
                        this.listeners = this.listeners.filter(cb => cb !== handler);
                        resolve(data.data);
                    }
                };
                this.listeners.push(handler);
                this.ws?.send(JSON.stringify({ type: 'COPY_RECIPE', recipeId, newName }));
            });
        }
    }

    public async deleteRecipe(recipeId: string): Promise<{ success: boolean; message: string; recipeId?: string }> {
        if (isWebView && machine) {
            const resultJson = await machine.DeleteRecipe(recipeId);
            return JSON.parse(resultJson);
        } else {
            return new Promise((resolve, reject) => {
                if (!this.isConnected) {
                    setTimeout(() => {
                        if (!this.isConnected) reject({ success: false, message: "WebSocket connection timeout" });
                    }, 2000);
                }

                const timeoutId = setTimeout(() => {
                    this.listeners = this.listeners.filter(cb => cb !== handler);
                    reject({ success: false, message: "Recipe delete timeout" });
                }, 10000);

                const handler = (data: any) => {
                    if (data.type === 'RECIPE_DELETED') {
                        clearTimeout(timeoutId);
                        this.listeners = this.listeners.filter(cb => cb !== handler);
                        resolve(data.data);
                    }
                };
                this.listeners.push(handler);
                this.ws?.send(JSON.stringify({ type: 'DELETE_RECIPE', recipeId }));
            });
        }
    }

    public async getRecipe(recipeTitle: string): Promise<string> {
        if (isWebView && machine) {
            return await machine.GetRecipe(recipeTitle);
        } else {
            return new Promise((resolve, reject) => {
                if (!this.isConnected) {
                    setTimeout(() => {
                        if (!this.isConnected) reject("WebSocket connection timeout");
                    }, 2000);
                }

                const timeoutId = setTimeout(() => {
                    this.listeners = this.listeners.filter(cb => cb !== handler);
                    reject("Recipe fetch timeout");
                }, 10000);

                const handler = (data: any) => {
                    if (data.type === 'RECIPE_DATA') {
                        clearTimeout(timeoutId);
                        this.listeners = this.listeners.filter(cb => cb !== handler);
                        resolve(JSON.stringify(data.data));
                    }
                };
                this.listeners.push(handler);
                this.ws?.send(JSON.stringify({ type: 'GET_RECIPE', recipeTitle }));
            });
        }
    }

    public async saveRecipe(recipeTitle: string, recipeData: string): Promise<{ success: boolean; message: string; recipeId?: string }> {
        if (isWebView && machine) {
            const resultJson = await machine.SaveRecipe(recipeTitle, recipeData);
            return JSON.parse(resultJson);
        } else {
            return new Promise((resolve, reject) => {
                if (!this.isConnected) {
                    setTimeout(() => {
                        if (!this.isConnected) reject({ success: false, message: "WebSocket connection timeout" });
                    }, 2000);
                }

                const timeoutId = setTimeout(() => {
                    this.listeners = this.listeners.filter(cb => cb !== handler);
                    reject({ success: false, message: "Recipe save timeout" });
                }, 10000);

                const handler = (data: any) => {
                    if (data.type === 'RECIPE_SAVED') {
                        clearTimeout(timeoutId);
                        this.listeners = this.listeners.filter(cb => cb !== handler);
                        resolve(data.data);
                    }
                };
                this.listeners.push(handler);
                this.ws?.send(JSON.stringify({ type: 'SAVE_RECIPE', recipeTitle, recipeData }));
            });
        }
    }
}

export const comms = new CommunicationLayer();
