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

import { DataType, type IPosition, type Payload, type WeatherPayload } from "./frame.types";
import { isCompressedPosition, parseCompressedPosition, parseUncompressedPosition } from "./payload.position";
import Timestamp from "./timestamp";

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

    let offset = 1; // skip '_' data type
    const segments: Segment[] = withStructure ? [] : [];

    // Try optional timestamp (7 chars)
    let timestamp;
    if (raw.length >= offset + 7) {
      const timeStr = raw.substring(offset, offset + 7);
      const parsed = Timestamp.fromString(timeStr, withStructure);
      timestamp = parsed.timestamp;
      if (parsed.segment) {
        segments.push(parsed.segment);
      }
      if (timestamp) offset += 7;
    }

    // Try optional position following timestamp
    let position: IPosition | undefined;
    let consumed = 0;
    const tail = raw.substring(offset);
    if (tail.length > 0) {
      // If the tail starts with a wind token like DDD/SSS, treat it as weather data
      // and do not attempt to parse it as a position (avoids mis-detecting wind
      // values as compressed position fields).
      if (/^\s*\d{3}\/\d{1,3}/.test(tail)) {
        // no position present; leave consumed = 0
      } else if (isCompressedPosition(tail)) {
        const parsed = parseCompressedPosition(tail, withStructure);
        if (parsed.position) {
          position = {
            latitude: parsed.position.latitude,
            longitude: parsed.position.longitude,
            symbol: parsed.position.symbol,
            altitude: parsed.position.altitude
          };
          if (parsed.segment) segments.push(parsed.segment);
          consumed = 13;
        }
      } else {
        const parsed = parseUncompressedPosition(tail, withStructure);
        if (parsed.position) {
          position = {
            latitude: parsed.position.latitude,
            longitude: parsed.position.longitude,
            symbol: parsed.position.symbol,
            ambiguity: parsed.position.ambiguity
          };
          if (parsed.segment) segments.push(parsed.segment);
          consumed = 19;
        }
      }
    }

    offset += consumed;

    const rest = raw.substring(offset).trim();

    const payload: WeatherPayload = {
      type: DataType.WeatherReportNoPosition
    };
    if (timestamp) payload.timestamp = timestamp;
    if (position) payload.position = position;

    if (rest && rest.length > 0) {
      // Parse common tokens
      // Wind: DDD/SSS [gGGG]
      const windMatch = rest.match(/(\d{3})\/(\d{1,3})(?:g(\d{1,3}))?/);
      if (windMatch) {
        payload.windDirection = parseInt(windMatch[1], 10);
        payload.windSpeed = parseInt(windMatch[2], 10);
        if (windMatch[3]) payload.windGust = parseInt(windMatch[3], 10);
      }

      // Temperature: tNNN (F)
      const tempMatch = rest.match(/t(-?\d{1,3})/i);
      if (tempMatch) payload.temperature = parseInt(tempMatch[1], 10);

      // Rain: rNNN (last hour), pNNN (24h), PNNN (since midnight) - values are hundredths of inch
      const rMatch = rest.match(/r(\d{3})/);
      if (rMatch) payload.rainLastHour = parseInt(rMatch[1], 10);
      const pMatch = rest.match(/p(\d{3})/);
      if (pMatch) payload.rainLast24Hours = parseInt(pMatch[1], 10);
      const PMatch = rest.match(/P(\d{3})/);
      if (PMatch) payload.rainSinceMidnight = parseInt(PMatch[1], 10);

      // Humidity: hNN
      const hMatch = rest.match(/h(\d{1,3})/);
      if (hMatch) payload.humidity = parseInt(hMatch[1], 10);

      // Pressure: bXXXX or bXXXXX (tenths of millibar)
      const bMatch = rest.match(/b(\d{4,5})/);
      if (bMatch) payload.pressure = parseInt(bMatch[1], 10);

      // Add raw comment
      payload.comment = rest;

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

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

export default decodeWeatherPayload;
