import { createUeuse } from "@/lib/client";
import config from "@/lib/config";
import Memory from "@/lib/memory";
import { format } from "date-fns";
import { readFileSync } from "node:fs";
import { EOL } from "node:os";
import { WebSocket } from "ws";
import generateImage from "@/feature/earthquake/generateImage";

if (config.earthquake?.useHistoryData) {
  console.log("過去の地震情報を配信します");
  const history = JSON.parse(readFileSync(`${import.meta.dirname}/../../260420.json`, "utf-8"));
  history.reverse();

  let i = 0;
  setInterval(() => {
    processMessage(history[i]);
    i++;
  }, 10 * 1000);
} else {
  const connect = () => {
    const WEBSOCKET_URL = config.debug
      ? "wss://api-realtime-sandbox.p2pquake.net/v2/ws"
      : "wss://api.p2pquake.net/v2/ws";

    console.log("P2P地震情報のWebSocketに接続します");
    const socket = new WebSocket(WEBSOCKET_URL);

    socket.addEventListener("open", () => {
      console.log("P2P地震情報のWebSocketに接続しました");
    });

    socket.addEventListener("close", (err) => {
      const interval = config.earthquake.reconnectInterval;
      console.log(`WebSocketが切断されました。${interval / 1000}秒後に再接続します:`, err.reason);
      setTimeout(() => {
        connect();
      }, interval);
    });

    socket.addEventListener("error", (err) => {
      console.error("WebSocketでエラーが発生しました:", err);
      socket.close();
    });

    socket.addEventListener("message", async (event) => {
      let message;

      try {
        message = typeof event.data === "string"
          ? JSON.parse(event.data)
          : event.data;
      } catch (err) {
        console.error("地震情報メッセージのパースでエラーが発生:", err);
        return;
      }

      const id = message.id ?? message._id;
      const mem = Memory.memory;
      if (mem.processedInfo.includes(id) && !config.debug) {
        console.log("重複した地震情報:", message.id);
        return;
      }

      processMessage(message);

      if (!config.debug) {
        const mem = Memory.memory;
        mem.processedInfo = mem.processedInfo.concat([id]);
        Memory.memory = mem;
      }
    });
  }

  connect();
}

const processMessage = async (message: any) => {
  try {
    const scaleMessages: Record<string, string> = {
      "-1": "不明",
      "0": "震度0",
      "10": "震度1",
      "20": "震度2",
      "30": "震度3",
      "40": "震度4",
      "45": "震度5弱",
      "46": "**推定**震度5弱以上***(不明)***",
      "50": "震度5強",
      "55": "震度6弱",
      "60": "震度6強",
      "70": "震度7",
      "99": "程度以上",
    }

    switch (message.code) {
      case 555:
        console.log("ピアの地域分布情報を受信しました");
        break;
      case 551:
        {
          console.log("地震発生情報を受信しました");

          if (
            message.earthquake.maxScale !== -1 &&
            message.earthquake.maxScale < config.earthquake.requireMaxScale
          ) {
            console.log(`最大震度(${scaleMessages[message.earthquake.maxScale]})が投稿に必要な値(${scaleMessages[config.earthquake.requireMaxScale]})に満たないため、スキップします`);
            break;
          }

          const typeMessage: Record<string, string> = {
            "ScalePrompt": "震度速報",
            "Destination": "震源情報",
          }

          const domesticTsunamiMessages: Record<string, string> = {
            "None": "😌この地震による**国内**の津波の心配はありません。",
            "Unknown": "😕この地震による**国内**の***津波情報は***不明です。",
            "Checking": "🧐この地震による**国内**の津波情報を**調査中です。**",
            "NonEffective": "😌この地震による**国内**の**海面変動が予想されますが**、被害の心配はありません。",
            "Watch": "⚠️この地震により**国内**で津波注意報が発令しています。",
            "Warning": "🚨この地震による**国内**の津波予報があります。",
          }

          const foreignTsunamiMessages: Record<string, string> = {
            "None": "😌この地震による**国外**の津波の心配はありません。",
            "Unknown": "😕この地震による**国外**の***津波情報は***不明です。",
            "Checking": "🧐この地震による**国外**の津波情報を**調査中です。**",
            "NonEffectiveNearby": "😌この地震によって**国外**にて震源の近傍で**小さな津波の可能性はありますが**、被害の心配はありません。",
            "WarningNearby": "⚠️この地震によって**国外**にて震源の近傍で**津波の可能性**があります。",
            "WarningPacific": "⚠️この地震によって**太平洋**にて**津波の可能性**があります。",
            "WarningPacificWide": "🚨この地震によって**太平洋の広域**にて**津波の可能性**があります。",
            "WarningIndian": "⚠️この地震によって**インド洋**にて**津波の可能性**があります。",
            "WarningIndianWide": "🚨この地震によって**インド洋の広域**にて**津波の可能性**があります。",
            "Potential": "🚨この地震によって**一般的に**この規模では津波の可能性があると考えられています。",
          }

          const grouped: Record<string, { scale: number; prefs: Record<string, string[]> }> = {};

          for (const point of message.points) {
            const { addr, scale, pref } = point;
            const label = scaleMessages[String(scale)] ?? "不明";

            if (!grouped[label]) {
              grouped[label] = { scale, prefs: {} };
            }

            if (!grouped[label].prefs[pref]) {
              grouped[label].prefs[pref] = [];
            }

            grouped[label].prefs[pref].push(addr);
          }

          const pointsMsg = Object.entries(grouped)
            .sort((a, b) => b[1].scale - a[1].scale)
            .map(([label, { prefs }]) => {
              const prefLines = Object.entries(prefs)
                .map(([pref, addrs]) => `**${pref}:** ${addrs.join("・")}`)
                .join(EOL);

              return `【${label}】${EOL}${prefLines}`;
            })
            .join(EOL.repeat(2))
            .trim();

          const earthquakeNoticeText = [];
          earthquakeNoticeText.push(`### ${typeMessage[message.issue.type] ?? "地震情報"}`);
          earthquakeNoticeText.push(`⏰時刻: ${format(new Date(message.earthquake.time), "yyyy年M月d日 H:mm")}頃`);
          earthquakeNoticeText.push(...(message.earthquake.maxScale === -1
            ? []
            : [`🫨最大震度: ${scaleMessages[String(message.earthquake.maxScale)]}`]));
          earthquakeNoticeText.push(...(message.earthquake.hypocenter.name === ""
            ? []
            : [`📍震源地: ${message.earthquake.hypocenter.name}`]));
          earthquakeNoticeText.push(...(message.earthquake.hypocenter.magnitude === -1
            ? []
            : [`💪マグニチュード: M${message.earthquake.hypocenter.magnitude.toFixed(1)}`]));
          earthquakeNoticeText.push(...(message.earthquake.hypocenter.depth === -1
            ? []
            : [`🪨深さ: ${message.earthquake.hypocenter.depth === 0
              ? "ごく浅い"
              : `${message.earthquake.hypocenter.depth}km`}`]));
          earthquakeNoticeText.push(...((message.earthquake.domesticTsunami ?? "Unknown") === "Unknown"
            ? []
            : [domesticTsunamiMessages[(message.earthquake.domesticTsunami)]]));
          earthquakeNoticeText.push(...((message.earthquake.foreignTsunami ?? "Unknown") === "Unknown"
            ? []
            : [foreignTsunamiMessages[(message.earthquake.foreignTsunami)]]));
          earthquakeNoticeText.push(...(pointsMsg === ""
            ? []
            : [EOL + pointsMsg]));
          earthquakeNoticeText.push(...(message.comments.freeFormComment === ""
            ? []
            : [message.comments.freeFormComment]));
          earthquakeNoticeText.push(EOL + `🔬情報源: P2P地震速報 - ${message.issue.source ?? "不明"}`);
          const earthquakeUeuses = await createUeuse({
            text: earthquakeNoticeText.join(EOL),
          }, "地震発生情報");

          try {
            const image = await generateImage(message);

            if (typeof image === "string") {
              throw "情報が不足しているため、地震の画像生成ができませんでした";
            } else {
              const infoUeuseURL = earthquakeUeuses[0]?.uniqid
                ? `${config.uwuzu.origin}/!${earthquakeUeuses[0].uniqid}`
                : "不明";

              const earthquakeImageText = [];
              earthquakeImageText.push("地震の震度分布画像を生成しました。");
              earthquakeImageText.push(`地震情報ユーズ: ${infoUeuseURL}`);
              await createUeuse({
                text: earthquakeImageText.join(EOL),
                media: {
                  photo: [
                    image.toString("base64"),
                  ],
                },
              }, "震度分布画像");
            }
          } catch (err) {
            console.warn(err);
          }
        }
        break;
      case 552:
        {  
          console.log("津波予報情報を受信しました");

          if (message.cancelled) {
            const tsunamiCancelledNoticeText = [];
            tsunamiCancelledNoticeText.push("### 津波予報**解除**");
            tsunamiCancelledNoticeText.push(`⏰発表時刻: ${format(new Date(message.issue.time), "yyyy年M月d日 H:mm:ss")}`);
            tsunamiCancelledNoticeText.push(`🔬情報源: P2P地震速報 - ${message.issue.source ?? "不明"}`);
            await createUeuse({
              text: tsunamiCancelledNoticeText.join(EOL),
            }, "津波予報解除情報");

            break;
          }

          const gradeMessages: Record<string, string> = {
            "Unknown": "不明",
            "Watch": "津波注意報",
            "Warning": "津波警報",
            "MajorWarning": "大津波警報",
          }

          let areasMsg = "";
          for (const area of message.areas) {
            const tsunamiAreaText = [];
            tsunamiAreaText.push(`【${area.name}】`);
            tsunamiAreaText.push(...(area.immediate
              ? ["🚨***直ちに津波が来襲すると予想されています。***"]
              : []));
            tsunamiAreaText.push(`🏷️種別: ${gradeMessages[area.grade]}`);
            tsunamiAreaText.push(`⏳第1波到達予想時刻: ${format(new Date(area.firstHeight.arrivalTime), "yyyy年M月d日 H:mm")}`);
            tsunamiAreaText.push(`🌊第1波の状態: ${area.firstHeight.condition
              ? `${area.firstHeight.condition}されています`
              : "不明"}`);
            tsunamiAreaText.push(`🗼予想される津波の高さ: ${area.maxHeight.value === 0.2
              ? "0.2m未満"
              : (area.maxHeight.value
                ? `${area.maxHeight.value}m`
                : area.maxHeight.description)}`);
            areasMsg += tsunamiAreaText.join(EOL) + EOL.repeat(2);
          }

          const tsunamiNoticeText = [];
          tsunamiNoticeText.push("### 津波予報**発表**");
          tsunamiNoticeText.push(`⏰発表時刻: ${format(new Date(message.issue.time), "yyyy年M月d日 H:mm:ss")}`);
          tsunamiNoticeText.push(EOL + areasMsg.trim() + EOL);
          tsunamiNoticeText.push(`🔬情報源: P2P地震速報 - message.issue.source ?? "不明"`);
          await createUeuse({
            text: tsunamiNoticeText.join(EOL),
          }, "津波予報情報");
        }
        break;
      case 556:
        {
          console.log("緊急地震速報（警報）を受信しました");

          if (message.cancelled) {
            const eewCancelledNotice = [];
            eewCancelledNotice.push("### 緊急地震速報（警報）**解除**");
            eewCancelledNotice.push(message.test
              ? "⚒️これは**テストです。**"
              : "🚨これは**テストではありません。**");
            eewCancelledNotice.push(`⏰発表時刻: ${format(new Date(message.issue.time), "yyyy年M月d日 H:mm:ss")}`);
            eewCancelledNotice.push(EOL + "🔬情報源: P2P地震速報");
            await createUeuse({
              text: eewCancelledNotice.join(EOL),
            }, "緊急地震速報（警報）解除情報");
          }

          const kindMessages: Record<string, string> = {
            "10": "⏳主要動は、**未到達と予測**されています。",
            "11": "🫨主要動が、**既に到達していると予測**されています。",
            "19": "🧐PLUM法によると、主要動の***到達予想は***ありません。",
          }

          let areasMsg = "";
          for (const area of message.areas) {
            const eewAreaText = [];
            eewAreaText.push(`【${area.name}】`);
            eewAreaText.push(`🫨最大予測震度: ${scaleMessages[String(Math.floor(area.scaleFrom))] +
              (area.scaleTo === 99
                ? "程度以上"
                : area.scaleFrom !== area.scaleTo
                  ? `から${scaleMessages[String(Math.floor(area.scaleTo))]}`
                  : "")}`);
            eewAreaText.push(`⏰到達予想時刻: ${typeof area.arrivalTime === "string" && area.arrivalTime !== ""
              ? format(new Date(area.arrivalTime), "yyyy年M月d日 H:mm:ss")
              : "不明"}`);
            eewAreaText.push(kindMessages[area.kindCode] ?? "😕主要動の***到達予想は***ありません。");

            areasMsg += eewAreaText.join(EOL) + EOL.repeat(2);
          }

          const eewNoticeText = [];
          eewNoticeText.push("### ***緊急地震速報（警報）***");
          eewNoticeText.push(message.test
            ? "⚒️これは**テストです。**"
            : "🚨これは**テストではありません。**");
          eewNoticeText.push(...(message.earthquake.condition === "仮定震源要素"
            ? ["❓これは、仮定震源要素です。そのため、震源に関する情報が保証できません。"]
            : []));
          eewNoticeText.push(`⏰発表時刻: ${format(new Date(message.issue.time), "yyyy年M月d日 H:mm:ss")}`);
          eewNoticeText.push(`⏰地震発生時刻: ${format(new Date(message.earthquake.originTime), "yyyy年M月d日 H:mm:ss")}`);
          eewNoticeText.push(`⏰地震発現時刻: ${format(new Date(message.earthquake.arrivalTime), "yyyy年M月d日 H:mm:ss")}`);
          eewNoticeText.push(`📍震源地: ${message.earthquake.hypocenter.name ?? "不明"}`);
          eewNoticeText.push(`💪マグニチュード: ${message.earthquake.hypocenter.magnitude === undefined ||
            message.earthquake.hypocenter.magnitude === -1
              ? "不明"
              : `M${message.earthquake.hypocenter.magnitude.toFixed(1)}`}`);
          eewNoticeText.push(`🪨深さ: ${message.earthquake.hypocenter.depth === undefined ||
            message.earthquake.hypocenter.depth === -1
              ? "不明"
              : `${Math.floor(message.earthquake.hypocenter.depth)}km`}`);
          eewNoticeText.push(...(areasMsg !== ""
            ? [EOL + areasMsg.trim()]
            : []));
          eewNoticeText.push(EOL + "🔬情報源: P2P地震速報");

          await createUeuse({
            text: eewNoticeText.join(EOL),
          }, "緊急地震速報（警報）情報");
        }
        break;
      default:
        console.log("未対応の情報:", message);
        break;
    }
  } catch (err) {
    console.warn("メッセージの処理に失敗しました:", err);
  }
}