import mqtt, { type IClientOptions, type MqttClient } from "mqtt";
import { env } from "../../config/env";
import { createMqttMessage } from "../store/mqttMessageStore";

type SubscriberStatus = {
  enabled: boolean;
  connected: boolean;
  topics: string[];
  client_id: string | null;
  last_connected_at: string | null;
  last_disconnected_at: string | null;
  last_message_at: string | null;
  last_error: { message: string; at: string } | null;
  received_count: number;
  recorded_count: number;
  failed_count: number;
};

const status: SubscriberStatus = {
  enabled: String(env.MQTT_SUBSCRIBE_ENABLED).toLowerCase() === "true",
  connected: false,
  topics: env.MQTT_SUBSCRIBE_TOPICS.split(",").map((topic) => topic.trim()).filter(Boolean),
  client_id: null,
  last_connected_at: null,
  last_disconnected_at: null,
  last_message_at: null,
  last_error: null,
  received_count: 0,
  recorded_count: 0,
  failed_count: 0
};

let clientRef: MqttClient | null = null;
let started = false;

function parseTopic(topic: string) {
  const match = topic.match(/^devices\/([^/]+)\/uplink\/(.+)$/);
  if (!match) {
    return null;
  }

  return {
    device_id: match[1],
    message_type: match[2].replace(/\//g, "_")
  };
}

function parsePayload(raw: Buffer): Record<string, unknown> {
  const text = raw.toString("utf8");
  try {
    const parsed = JSON.parse(text) as unknown;
    return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : { raw_text: text };
  } catch (_error) {
    return { raw_text: text };
  }
}

export function startMqttSubscriber() {
  if (started || !status.enabled) {
    return;
  }
  started = true;

  if (!env.MQTT_BROKER_URL) {
    status.last_error = { message: "MQTT_BROKER_URL_MISSING", at: new Date().toISOString() };
    return;
  }

  const clientId = `${env.MQTT_CLIENT_ID}-subscriber`;
  status.client_id = clientId;
  const options: IClientOptions = {
    clientId,
    username: env.MQTT_USERNAME || undefined,
    password: env.MQTT_PASSWORD || undefined,
    connectTimeout: env.MQTT_CONNECT_TIMEOUT_MS,
    reconnectPeriod: 5000,
    clean: true
  };

  const client = mqtt.connect(env.MQTT_BROKER_URL, options);
  clientRef = client;

  client.on("connect", () => {
    status.connected = true;
    status.last_connected_at = new Date().toISOString();
    for (const topic of status.topics) {
      client.subscribe(topic, { qos: 1 }, (error) => {
        if (error) {
          status.last_error = { message: error.message, at: new Date().toISOString() };
        }
      });
    }
  });

  client.on("message", (topic, raw) => {
    status.received_count += 1;
    status.last_message_at = new Date().toISOString();
    const parsedTopic = parseTopic(topic);
    if (!parsedTopic) {
      status.failed_count += 1;
      status.last_error = { message: `UNSUPPORTED_TOPIC:${topic}`, at: new Date().toISOString() };
      return;
    }

    const payload = parsePayload(raw);
    createMqttMessage({
      direction: "uplink",
      device_id: parsedTopic.device_id,
      topic,
      message_type: String(payload.message_type || parsedTopic.message_type),
      correlation_id: typeof payload.correlation_id === "string" ? payload.correlation_id : undefined,
      payload_json: payload,
      publish_status: "recorded"
    })
      .then(() => {
        status.recorded_count += 1;
      })
      .catch((error: unknown) => {
        status.failed_count += 1;
        const message = error instanceof Error ? error.message : "unknown";
        status.last_error = { message, at: new Date().toISOString() };
      });
  });

  client.on("error", (error) => {
    status.last_error = { message: error.message, at: new Date().toISOString() };
  });

  client.on("close", () => {
    status.connected = false;
    status.last_disconnected_at = new Date().toISOString();
  });
}

export function getMqttSubscriberStatus() {
  return {
    ...status,
    connected: status.enabled ? Boolean(clientRef?.connected) : false
  };
}
