import { tcpService } from './TcpService';
import {
  buildSetupPacket,
  buildContinuousReq,
  buildSingleReq,
  buildStopReq,
  buildSplitFrameReq,
} from '../protocol/packet';
import {
  parseMetadata,
  parseAdcData,
  parseAck,
} from '../protocol/parser';
import { FuncCode, SplitFrameCmd, AckResult, AMP_GAIN } from '../protocol/constants';
import type { RawFrame, SetupConfig, MeasurementFrame, AckPacket } from '../protocol/types';
import { useConnectionStore } from '../stores/connectionStore';
import { useDeviceStore } from '../stores/deviceStore';
import { useDataStore } from '../stores/dataStore';
import * as StorageService from './StorageService';

// Pending ACK promise resolver keyed by func code
const pendingAcks = new Map<number, (ack: AckPacket) => void>();

// Split frame accumulation state
let splitChunks: Uint8Array[] = [];
let splitActive = false;
let splitFrameNo = 0;

export function handleIncomingFrame(frame: RawFrame) {
  const { func, payload } = frame;
  if (__DEV__) console.log(`[Device] incoming frame: func=0x${func.toString(16)} payloadLen=${payload.length} pendingAcks=[${[...pendingAcks.keys()].map(k => '0x' + k.toString(16))}]`);
  const devStore = useDeviceStore.getState();
  const dataStore = useDataStore.getState();

  switch (func) {
    // ── ACK responses ──────────────────────────────────────────────────
    case FuncCode.SETUP_ACK:
    case FuncCode.CONTINUOUS_ACK:
    case FuncCode.SINGLE_ACK:
    case FuncCode.STOP_ACK: {
      const ack = parseAck(payload);
      const resolver = pendingAcks.get(func);
      if (resolver) {
        pendingAcks.delete(func);
        resolver(ack);
      }
      if (func === FuncCode.CONTINUOUS_ACK && ack.result === AckResult.OK) {
        devStore.setDeviceStatus('running');
      }
      if (func === FuncCode.SINGLE_ACK && ack.result === AckResult.OK) {
        devStore.setDeviceStatus('single');
      }
      if (func === FuncCode.STOP_ACK) {
        devStore.setDeviceStatus('idle');
      }
      break;
    }

    // ── Measurement data (single or multi-source) ──────────────────
    case FuncCode.DATA_ACK:
    case FuncCode.COMP_DATA_ACK: {
      processMeasurementPayload(payload, devStore, dataStore);
      break;
    }

    // ── Split frame response ───────────────────────────────────────
    case FuncCode.SPLITFRAME_ACK: {
      const subCmd = payload[0];
      // Data starts at byte 3 (subCmd + frameNo uint16)
      const chunk = payload.slice(3);
      splitChunks.push(chunk);

      if (subCmd === SplitFrameCmd.LAST) {
        // Reassemble and process
        const total = splitChunks.reduce((s, c) => s + c.length, 0);
        const assembled = new Uint8Array(total);
        let off = 0;
        for (const c of splitChunks) {
          assembled.set(c, off);
          off += c.length;
        }
        splitChunks = [];
        splitActive = false;
        processMeasurementPayload(assembled, devStore, dataStore);
      } else {
        // Request next frame
        splitFrameNo++;
        tcpService.send(buildSplitFrameReq(SplitFrameCmd.REQUEST, splitFrameNo));
      }
      break;
    }
  }
}

let _lastUIUpdateTime = 0;
const UI_THROTTLE_MS = 150;
let _processing = false;

function processMeasurementPayload(
  payload: Uint8Array,
  devStore: ReturnType<typeof useDeviceStore.getState>,
  dataStore: ReturnType<typeof useDataStore.getState>,
) {
  if (devStore.deviceStatus === 'idle') return;
  if (_processing) return;
  _processing = true;

  try {
    const meta = parseMetadata(payload);
    if (!meta) return;

    const cfg = devStore.config;
    const accNum = cfg.accNum || 1;
    const { adcRaw, adcUV } = parseAdcData(payload, meta.channelNum, accNum);

    const measurementFrame: MeasurementFrame = {
      meta,
      adcRaw,
      adcUV,
      accNum,
      gain:           AMP_GAIN[cfg.ampRatio] ?? 1,
      sampleFreqCode: cfg.sampleFreq,
      timestamp:      Date.now(),
      frameId:        dataStore.nextFrameId(),
    };

    void StorageService.persistFrame(measurementFrame, dataStore.sessionId);

    const isSingle = devStore.deviceStatus === 'single';
    const now = Date.now();
    if (isSingle || now - _lastUIUpdateTime >= UI_THROTTLE_MS) {
      _lastUIUpdateTime = now;
      dataStore.addFrameUI(measurementFrame);
    }

    devStore.updateTelemetry({
      batteryVolt: meta.batteryVolt,
      temperature: meta.temperature,
      gpsStatus:   meta.gpsStatus,
      sdStatus:    meta.sdStatus,
      frameId:     measurementFrame.frameId,
    });

    if (isSingle) {
      void deviceStop();
    }
  } finally {
    _processing = false;
  }
}

// ── Public API ──────────────────────────────────────────────────────────────

function sendAndWaitAck(pkt: Uint8Array, ackFunc: number, timeoutMs = 5000): Promise<AckPacket> {
  return new Promise((resolve, reject) => {
    if (__DEV__) console.log(`[Device] sendAndWaitAck: waiting for ack func=0x${ackFunc.toString(16)}, timeout=${timeoutMs}ms`);

    const timer = setTimeout(() => {
      pendingAcks.delete(ackFunc);
      if (__DEV__) console.warn(`[Device] ACK timeout for func=0x${ackFunc.toString(16)}, pendingAcks remaining:`, [...pendingAcks.keys()].map(k => '0x' + k.toString(16)));
      reject(new Error('ACK timeout'));
    }, timeoutMs);

    const existing = pendingAcks.get(ackFunc);
    if (existing) {
      pendingAcks.delete(ackFunc);
      existing({ result: 0x02, reason: 'superseded' });
    }

    pendingAcks.set(ackFunc, (ack) => {
      clearTimeout(timer);
      if (__DEV__) console.log(`[Device] ACK received for func=0x${ackFunc.toString(16)}, result=0x${ack.result.toString(16)}`);
      resolve(ack);
    });

    const sent = tcpService.send(pkt);
    if (!sent) {
      clearTimeout(timer);
      pendingAcks.delete(ackFunc);
      reject(new Error('TCP not connected'));
    }
  });
}

export async function deviceSetup(config: SetupConfig): Promise<AckPacket> {
  useDeviceStore.getState().setConfig(config);
  return sendAndWaitAck(buildSetupPacket(config), FuncCode.SETUP_ACK);
}

export async function deviceStartContinuous(): Promise<AckPacket> {
  return sendAndWaitAck(buildContinuousReq(), FuncCode.CONTINUOUS_ACK);
}

export async function deviceStartSingle(): Promise<AckPacket> {
  return sendAndWaitAck(buildSingleReq(), FuncCode.SINGLE_ACK);
}

export async function deviceStop(): Promise<AckPacket> {
  return sendAndWaitAck(buildStopReq(), FuncCode.STOP_ACK, 2000);
}

export function flushPendingData() {
  tcpService.resetParser();
  splitChunks = [];
  splitActive = false;
  splitFrameNo = 0;
  _processing = false;
}

// Initiate split frame transfer to request large data
export function deviceStartSplitFrame() {
  if (splitActive) return;
  splitActive = true;
  splitFrameNo = 0;
  splitChunks = [];
  tcpService.send(buildSplitFrameReq(SplitFrameCmd.START, 0));
}
