/**
 * ╔═══════════════════════════════════════════════════════════════╗
 * ║  LkvService — Last Known Value Store                          ║
 * ║  Singleton In-Memory хранилище последних значений датчиков    ║
 * ╚═══════════════════════════════════════════════════════════════╝
 *
 * LKV (Last Known Value) — паттерн из промышленных SCADA-систем.
 * Каждый датчик хранит своё последнее известное значение.
 * При кратковременном сбое связи фронтенд видит последнее значение,
 * а не пустоту — это критично для производственного дашборда.
 *
 * Архитектура:
 *  ┌──────────────┐     set()    ┌──────────────┐
 *  │ ModbusService│ ──────────► │              │
 *  │ OpcuaService │             │  LkvService  │ ──► /api/sensors
 *  │ SimService   │             │  (Singleton) │ ──► WebSocket broadcast
 *  └──────────────┘             └──────────────┘
 *
 * @Global() — доступен во всех модулях без дополнительного импорта.
 */

import { Injectable, Logger } from '@nestjs/common';

/** Полная структура ответа /api/sensors (1:1 с Express-бэкендом) */
export interface SensorSnapshot {
  past_steklo: number;  // T° Пастеризатор Стекло, °C
  past_sig:    number;  // T° Пастеризатор SIG, °C
  past_tetra:  number;  // T° Пастеризатор Tetra, °C
  ph:          number;  // pH розлив
  brix:        number;  // Brix купажа, °Bx
  acid:        number;  // Кислотность, %
  oee:         number;  // OEE линии, %
  output:      number;  // Выпуск, л
  power_kw:    number;  // Мощность, кВт
  steam_bar:   number;  // Давление пара, бар
  air_bar:     number;  // Давление воздуха, бар
  ts:          string;  // ISO timestamp последнего обновления
  source:      string;  // 'simulation' | 'modbus' | 'opcua' | 'mqtt'
}

/** Ответ /api/resources */
export interface ResourceSnapshot {
  water_today:  number; // Вода за смену, м³
  power_today:  number; // Электроэнергия за смену, кВт·ч
  steam_today:  number; // Пар за смену, т
  gas_today:    number; // Газ за смену, м³
  air_today:    number; // Воздух за смену, м³/ч
  ww_today:     number; // Сточные воды за смену, м³
  ts:           string; // ISO timestamp
}

/** Статус 1С ERP — ответ /api/erp/status */
export interface ErpStatus {
  enabled:   boolean;
  url:       string;
  lastSync:  string | null;
  connected: boolean;
}

@Injectable()
export class LkvService {
  private readonly logger = new Logger(LkvService.name);

  // ── Датчики — начальные значения (имитация) ───────────────────
  private sensors: SensorSnapshot = {
    past_steklo: 78.4,
    past_sig:    81.2,
    past_tetra:  75.9,
    ph:          3.68,
    brix:        11.8,
    acid:        0.42,
    oee:         92.4,
    output:      12_480,
    power_kw:    142.3,
    steam_bar:   6.2,
    air_bar:     6.8,
    ts:          new Date().toISOString(),
    source:      'simulation',
  };

  // ── Ресурсы ───────────────────────────────────────────────────
  private resources: ResourceSnapshot = {
    water_today:  1840,
    power_today:  3420,
    steam_today:  18.4,
    gas_today:    0,
    air_today:    0,
    ww_today:     1200,
    ts:           new Date().toISOString(),
  };

  // ── ERP (1С) ──────────────────────────────────────────────────
  private erpStatus: ErpStatus = {
    enabled:   false,
    url:       '',
    lastSync:  null,
    connected: false,
  };

  // ── ERP данные ────────────────────────────────────────────────
  private erpData = {
    remains:    [] as unknown[],
    shipments:  [] as unknown[],
    output:     [] as unknown[],
    operations: [] as unknown[],
  };

  // ─────────────────────────────────────────────────────────────
  // SENSORS
  // ─────────────────────────────────────────────────────────────

  /** Получить текущий снимок всех датчиков */
  getSensors(): Readonly<SensorSnapshot> {
    return this.sensors;
  }

  /** Обновить весь снимок (из SimService или реального устройства) */
  setSensors(snapshot: SensorSnapshot): void {
    this.sensors = snapshot;
  }

  /**
   * Обновить одно конкретное поле датчика.
   * Используется Modbus/OPC-UA: каждый датчик обновляет только своё поле.
   *
   * @param key    - поле (например 'past_steklo')
   * @param value  - новое значение
   * @param source - источник ('modbus' | 'opcua' | 'mqtt' | 'simulation')
   */
  setSensorField(
    key:    keyof Omit<SensorSnapshot, 'ts' | 'source'>,
    value:  number,
    source: string = 'realtime',
  ): void {
    (this.sensors as Record<string, unknown>)[key] = value;
    this.sensors.ts     = new Date().toISOString();
    this.sensors.source = source;
  }

  // ─────────────────────────────────────────────────────────────
  // RESOURCES
  // ─────────────────────────────────────────────────────────────

  getResources(): Readonly<ResourceSnapshot> {
    return this.resources;
  }

  setResources(snapshot: ResourceSnapshot): void {
    this.resources = snapshot;
  }

  setResourceField(
    key:   keyof Omit<ResourceSnapshot, 'ts'>,
    value: number,
  ): void {
    (this.resources as Record<string, unknown>)[key] = value;
    this.resources.ts = new Date().toISOString();
  }

  // ─────────────────────────────────────────────────────────────
  // ERP (1С)
  // ─────────────────────────────────────────────────────────────

  getErpStatus(): Readonly<ErpStatus> {
    return this.erpStatus;
  }

  setErpStatus(status: Partial<ErpStatus>): void {
    this.erpStatus = { ...this.erpStatus, ...status };
  }

  getErpData(key: keyof typeof this.erpData): unknown[] {
    return this.erpData[key];
  }

  setErpData(key: keyof typeof this.erpData, data: unknown[]): void {
    this.erpData[key] = data;
  }

  /** Полный ответ для /api/erp/remains и т.д. (1:1 с Express) */
  buildErpResponse(key: keyof typeof this.erpData): object {
    return {
      data:   this.erpData[key],
      ts:     this.erpStatus.lastSync,
      source: this.erpStatus.connected ? '1С' : 'cache',
    };
  }
}
