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

import { DO_NOT_ARCHIVE_MARKER, DataType, type IPosition, type ItemPayload, type Payload } from "./frame.types";
import { attachExtras, decodeCommentExtras } from "./payload.extras";
import { isCompressedPosition, parseCompressedPosition, parseUncompressedPosition } from "./payload.position";
import Timestamp from "./timestamp";

export const decodeItemPayload = (
  raw: string,
  withStructure: boolean = false
): {
  payload: Payload | null;
  segment?: Segment[];
} => {
  // Item format is similar to Object but name may be 3-9 chars (stored in a 9-char field)
  // Example: )NNN... where ) is data type, next 9 chars are name, then state char, then timestamp, then position
  if (raw.length < 12) return { payload: null }; // minimal: 1 + 3 + 1 + 7

  let offset = 1; // skip data type identifier ')'
  const segment: Segment[] = withStructure ? [] : [];

  // Read 9-char name field (pad/truncate as present)
  const rawName = raw.substring(offset, offset + 9);
  const name = rawName.trimEnd();
  if (withStructure) {
    segment.push({
      name: "item name",
      data: new TextEncoder().encode(rawName).buffer,
      isString: true,
      fields: [{ type: FieldType.STRING, name: "name", length: 9 }]
    });
  }
  offset += 9;

  // State character: '*' = alive, '_' = killed
  const stateChar = raw.charAt(offset);
  if (stateChar !== "*" && stateChar !== "_") {
    return { payload: null };
  }
  const alive = stateChar === "*";
  if (withStructure) {
    segment.push({
      name: "item state",
      data: new TextEncoder().encode(stateChar).buffer,
      isString: true,
      fields: [
        {
          type: FieldType.CHAR,
          name: "State (* alive, _ killed)",
          length: 1
        }
      ]
    });
  }
  offset += 1;

  // Timestamp (7 chars)
  const timeStr = raw.substring(offset, offset + 7);
  const { timestamp, segment: timestampSection } = Timestamp.fromString(timeStr.substring(offset), withStructure);
  if (!timestamp) return { payload: null };
  if (timestampSection) segment.push(timestampSection);
  offset += 7;

  const isCompressed = isCompressedPosition(raw.substring(offset));

  // eslint-disable-next-line no-useless-assignment
  let position: IPosition | null = null;
  // eslint-disable-next-line no-useless-assignment
  let consumed = 0;

  if (isCompressed) {
    const { position: compressed, segment: compressedSection } = parseCompressedPosition(
      raw.substring(offset),
      withStructure
    );
    if (!compressed) return { payload: null };

    position = {
      latitude: compressed.latitude,
      longitude: compressed.longitude,
      symbol: compressed.symbol,
      altitude: compressed.altitude
    };
    consumed = 13;

    if (compressedSection) segment.push(compressedSection);
  } else {
    const { position: uncompressed, segment: uncompressedSection } = parseUncompressedPosition(
      raw.substring(offset),
      withStructure
    );
    if (!uncompressed) return { payload: null };

    position = {
      latitude: uncompressed.latitude,
      longitude: uncompressed.longitude,
      symbol: uncompressed.symbol,
      ambiguity: uncompressed.ambiguity
    };
    consumed = 19;

    if (uncompressedSection) segment.push(uncompressedSection);
  }

  offset += consumed;
  const remainder = raw.substring(offset);
  const doNotArchive = remainder.includes(DO_NOT_ARCHIVE_MARKER);
  let comment = remainder;

  const extras = decodeCommentExtras(comment, withStructure);
  comment = extras.comment;

  if (comment) {
    position.comment = comment;
    if (withStructure) {
      segment.push({
        name: "comment",
        data: new TextEncoder().encode(remainder).buffer,
        isString: true,
        fields: extras.fields || []
      });
    }
  } else if (withStructure && extras.fields) {
    // No free-text comment, but extras fields exist: emit comment-only segment
    segment.push({
      name: "comment",
      data: new TextEncoder().encode(remainder).buffer,
      isString: true,
      fields: extras.fields || []
    });
  }

  const payload: ItemPayload = {
    type: DataType.Item,
    doNotArchive,
    name,
    alive,
    position
  };
  attachExtras(payload, extras);

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

  return { payload };
};

export default decodeItemPayload;
