/**
 * StockWebSocket - 키움 주식 실시간 시세 WebSocket 클라이언트
 * 자동 재연결 (지수 백오프), 구독 목록 자동 복구 지원
 */
class StockWebSocket {
    constructor() {
        this.ws = null;
        this.subscriptions = new Set();   // 현재 구독 중인 종목 코드
        // 메시지 타입별 핸들러 맵: type → { code → callbacks[] }
        this.handlers = {
            price:     new Map(),
            orderbook: new Map(),
            program:   new Map(),
            meta:      new Map(),
        };
        // 전역 핸들러 (코드 무관한 메시지용)
        this.globalHandlers = {
            market: [],
        };
        this.reconnectDelay = 1000;       // 초기 재연결 대기 시간 (ms)
        this.maxReconnectDelay = 30000;   // 최대 재연결 대기 시간 (ms)
        this.intentionalClose = false;
        this.connect();
    }

    connect() {
        const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
        const url = `${protocol}//${location.host}/ws`;

        this.ws = new WebSocket(url);

        this.ws.onopen = () => {
            console.log('WebSocket 연결됨');
            this.reconnectDelay = 1000; // 성공 시 재연결 대기 시간 초기화

            // 기존 구독 목록 자동 복구
            this.subscriptions.forEach(code => this._send({ type: 'subscribe', code }));
        };

        this.ws.onmessage = (event) => {
            try {
                const msg = JSON.parse(event.data);
                this._handleMessage(msg);
            } catch (e) {
                console.error('메시지 파싱 실패:', e);
            }
        };

        this.ws.onclose = () => {
            if (!this.intentionalClose) {
                console.log(`WebSocket 연결 끊김. ${this.reconnectDelay}ms 후 재연결...`);
                setTimeout(() => this.connect(), this.reconnectDelay);
                // 지수 백오프
                this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
            }
        };

        this.ws.onerror = (err) => {
            console.error('WebSocket 오류:', err);
        };
    }

    // 종목 구독
    subscribe(code) {
        this.subscriptions.add(code);
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this._send({ type: 'subscribe', code });
        }
    }

    // 종목 구독 해제
    unsubscribe(code) {
        this.subscriptions.delete(code);
        Object.values(this.handlers).forEach(map => map.delete(code));
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this._send({ type: 'unsubscribe', code });
        }
    }

    // 현재가 수신 콜백 등록
    onPrice(code, callback) {
        this._addCodeHandler('price', code, callback);
    }

    // 호가창 수신 콜백 등록
    onOrderBook(code, callback) {
        this._addCodeHandler('orderbook', code, callback);
    }

    // 프로그램 매매 수신 콜백 등록
    onProgram(code, callback) {
        this._addCodeHandler('program', code, callback);
    }

    // 종목 메타 수신 콜백 등록
    onMeta(code, callback) {
        this._addCodeHandler('meta', code, callback);
    }

    // 장운영 상태 수신 콜백 등록 (전역)
    onMarket(callback) {
        this.globalHandlers.market.push(callback);
    }

    // 내부: 코드별 핸들러 등록
    _addCodeHandler(type, code, callback) {
        if (!this.handlers[type]) return;
        if (!this.handlers[type].has(code)) {
            this.handlers[type].set(code, []);
        }
        this.handlers[type].get(code).push(callback);
    }

    // 내부: 메시지 처리
    _handleMessage(msg) {
        const { type, code, data } = msg;

        if (type === 'market') {
            this.globalHandlers.market.forEach(fn => fn(data));
            return;
        }

        if (type === 'error') {
            console.warn(`서버 오류 [${code}]:`, data?.message);
            return;
        }

        if (this.handlers[type] && code) {
            const callbacks = this.handlers[type].get(code) || [];
            callbacks.forEach(fn => fn(data));
        }
    }

    // 내부: JSON 메시지 전송
    _send(data) {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(data));
        }
    }

    close() {
        this.intentionalClose = true;
        this.ws?.close();
    }
}

// 전역 인스턴스 (stock_detail.html에서 사용)
const stockWS = new StockWebSocket();
