/**
 * Lego 02 / Lego 11 / Lego 13 — Vision Socket Service
 *
 * Opens a persistent Socket.io connection to NestJS /vision namespace.
 * Captures a single webcam frame on demand and emits it as a raw,
 * uncompressed Base64 string on vision:analyze.
 *
 * HARD RULES (Lego 11):
 *   - Frames MUST be sent as raw Base64 strings (canvas.toDataURL('image/jpeg'))
 *   - DO NOT use binary ArrayBuffer encoding
 *   - DO NOT use WebRTC
 *   - DO NOT reduce quality or compress the payload
 *   - One frame per user-initiated "Snap" — no continuous streaming interval
 *
 * The full Base64 payload (~33% larger than binary) rides the socket bus
 * on each snap intentionally — this is the I/O overhead being measured.
 */

import { Injectable, signal, OnDestroy } from '@angular/core';
import { io, Socket } from 'socket.io-client';
import { environment } from '../../environments/environment';
import { AnalysisResponse } from '../core/interfaces/palm-analysis.interface';

@Injectable({ providedIn: 'root' })
export class VisionSocketService implements OnDestroy {

  readonly connected = signal<boolean>(false);
  readonly analyzing = signal<boolean>(false);
  readonly lastResult = signal<AnalysisResponse | null>(null);
  readonly lastError = signal<string | null>(null);

  private socket: Socket;

  // Offscreen canvas used to capture a single frame from the video element
  private captureCanvas: HTMLCanvasElement;
  private captureCtx: CanvasRenderingContext2D;

  constructor() {
    this.captureCanvas = document.createElement('canvas');
    this.captureCanvas.width = 640;
    this.captureCanvas.height = 640;
    this.captureCtx = this.captureCanvas.getContext('2d')!;

    this.socket = io(`${environment.nestWsUrl}/vision`, {
      transports: ['websocket'],
      reconnection: true,
      reconnectionDelay: 1000,
    });

    this.socket.on('connect', () => this.connected.set(true));
    this.socket.on('disconnect', () => this.connected.set(false));

    this.socket.on('vision:result', (result: AnalysisResponse) => {
      this.lastResult.set(result);
      this.lastError.set(null);
      this.analyzing.set(false);
    });

    this.socket.on('vision:error', (err: { message: string }) => {
      this.lastError.set(err.message);
      this.analyzing.set(false);
    });
  }

  /**
   * Capture one frame from the given video element and emit it to NestJS
   * via vision:analyze. Returns the raw Base64 string that was sent,
   * so the caller can display it as a static snapshot.
   *
   * Lego 11: raw JPEG Base64, no quality reduction, no binary encoding.
   */
  snapAndSend(videoEl: HTMLVideoElement, batchId?: string): string | null {
    if (!videoEl || videoEl.readyState < 2) return null;

    this.captureCtx.drawImage(videoEl, 0, 0, 640, 640);

    // Lego 11 hard rule: raw, uncompressed Base64 — no quality param
    const rawBase64Frame: string = this.captureCanvas.toDataURL('image/jpeg');

    this.analyzing.set(true);
    this.lastResult.set(null);
    this.lastError.set(null);

    this.socket.emit('vision:analyze', {
      frame: rawBase64Frame,
      sourceLabel: 'webcam-snap',
      batchId,
    });

    return rawBase64Frame;
  }

  /**
   * Send a pre-existing Base64 image string (from gallery/file picker) to NestJS
   * via vision:analyze. Same socket path as snapAndSend — one event, one result.
   *
   * Lego 11: caller must supply a full data-URL (data:image/...;base64,...).
   */
  sendBase64(base64DataUrl: string, batchId?: string): void {
    this.analyzing.set(true);
    this.lastResult.set(null);
    this.lastError.set(null);

    this.socket.emit('vision:analyze', {
      frame: base64DataUrl,
      sourceLabel: 'gallery-image',
      batchId,
    });
  }

  clearResult(): void {
    this.lastResult.set(null);
    this.lastError.set(null);
  }

  ngOnDestroy() {
    // Zombie socket — intentionally kept alive during app lifetime (Lego 06).
    // Only disconnects on full Angular DI teardown.
    this.socket.disconnect();
  }
}
