import { FieldType, type Segment } from "@hamradio/packet";

import {
  DataType,
  type Payload,
  type TelemetryBitSensePayload,
  type TelemetryCoefficientsPayload,
  type TelemetryDataPayload,
  type TelemetryParameterPayload,
  type TelemetryUnitPayload
} from "./frame.types";

export const decodeTelemetryPayload = (
  raw: string,
  withStructure: boolean = false
): {
  payload: Payload | null;
  segment?: Segment[];
} => {
  try {
    if (raw.length < 2) return { payload: null };

    const rest = raw.substring(1).trim();
    if (!rest) return { payload: null };

    const segments: Segment[] = withStructure ? [] : [];

    // Telemetry data: convention used here: starts with '#' then sequence then analogs and digital
    if (rest.startsWith("#")) {
      const parts = rest.substring(1).trim().split(/\s+/);
      const seq = parseInt(parts[0], 10);
      let analog: number[] = [];
      let digital = 0;

      if (parts.length >= 2) {
        // analogs as comma separated
        analog = parts[1].split(",").map((v) => parseFloat(v));
      }

      if (parts.length >= 3) {
        digital = parseInt(parts[2], 10);
      }

      if (withStructure) {
        segments.push({
          name: "telemetry sequence",
          data: new TextEncoder().encode(String(seq)).buffer,
          isString: true,
          fields: [
            {
              type: FieldType.STRING,
              name: "sequence",
              length: String(seq).length
            }
          ]
        });

        segments.push({
          name: "telemetry analog",
          data: new TextEncoder().encode(parts[1] || "").buffer,
          isString: true,
          fields: [
            {
              type: FieldType.STRING,
              name: "analogs",
              length: (parts[1] || "").length
            }
          ]
        });

        segments.push({
          name: "telemetry digital",
          data: new TextEncoder().encode(String(digital)).buffer,
          isString: true,
          fields: [
            {
              type: FieldType.STRING,
              name: "digital",
              length: String(digital).length
            }
          ]
        });
      }

      const payload: TelemetryDataPayload = {
        type: DataType.TelemetryData,
        variant: "data",
        sequence: isNaN(seq) ? 0 : seq,
        analog,
        digital: isNaN(digital) ? 0 : digital
      };

      if (withStructure) return { payload, segment: segments };
      return { payload };
    }

    // Telemetry parameters: 'PARAM' keyword
    if (/^PARAM/i.test(rest)) {
      const after = rest.replace(/^PARAM\s*/i, "");
      const names = after.split(/[,\s]+/).filter(Boolean);
      if (withStructure) {
        segments.push({
          name: "telemetry parameters",
          data: new TextEncoder().encode(after).buffer,
          isString: true,
          fields: [{ type: FieldType.STRING, name: "names", length: after.length }]
        });
      }
      const payload: TelemetryParameterPayload = {
        type: DataType.TelemetryData,
        variant: "parameters",
        names
      };
      if (withStructure) return { payload, segment: segments };
      return { payload };
    }

    // Telemetry units: 'UNIT'
    if (/^UNIT/i.test(rest)) {
      const after = rest.replace(/^UNIT\s*/i, "");
      const units = after.split(/[,\s]+/).filter(Boolean);
      if (withStructure) {
        segments.push({
          name: "telemetry units",
          data: new TextEncoder().encode(after).buffer,
          isString: true,
          fields: [{ type: FieldType.STRING, name: "units", length: after.length }]
        });
      }
      const payload: TelemetryUnitPayload = {
        type: DataType.TelemetryData,
        variant: "unit",
        units
      };
      if (withStructure) return { payload, segment: segments };
      return { payload };
    }

    // Telemetry coefficients: 'COEFF' a:,b:,c:
    if (/^COEFF/i.test(rest)) {
      const after = rest.replace(/^COEFF\s*/i, "");
      const aMatch = after.match(/A:([^\s;]+)/i);
      const bMatch = after.match(/B:([^\s;]+)/i);
      const cMatch = after.match(/C:([^\s;]+)/i);
      const parseList = (s?: string) => (s ? s.split(",").map((v) => parseFloat(v)) : []);
      const coefficients = {
        a: parseList(aMatch?.[1]),
        b: parseList(bMatch?.[1]),
        c: parseList(cMatch?.[1])
      };
      if (withStructure) {
        segments.push({
          name: "telemetry coefficients",
          data: new TextEncoder().encode(after).buffer,
          isString: true,
          fields: [{ type: FieldType.STRING, name: "coeffs", length: after.length }]
        });
      }
      const payload: TelemetryCoefficientsPayload = {
        type: DataType.TelemetryData,
        variant: "coefficients",
        coefficients
      };
      if (withStructure) return { payload, segment: segments };
      return { payload };
    }

    // Telemetry bitsense/project: 'BITS' <number> [project]
    if (/^BITS?/i.test(rest)) {
      const parts = rest.split(/\s+/).slice(1);
      const sense = parts.length > 0 ? parseInt(parts[0], 10) : 0;
      const projectName = parts.length > 1 ? parts.slice(1).join(" ") : undefined;
      if (withStructure) {
        segments.push({
          name: "telemetry bitsense",
          data: new TextEncoder().encode(rest).buffer,
          isString: true,
          fields: [{ type: FieldType.STRING, name: "bitsense", length: rest.length }]
        });
      }
      const payload: TelemetryBitSensePayload = {
        type: DataType.TelemetryData,
        variant: "bitsense",
        sense: isNaN(sense) ? 0 : sense,
        ...(projectName ? { projectName } : {})
      };
      if (withStructure) return { payload, segment: segments };
      return { payload };
    }

    return { payload: null };
  } catch {
    return { payload: null };
  }
};

export default decodeTelemetryPayload;
