import { useRuntimeConfig } from "#app";
import { shallowRef, watch } from "vue";
import type { FetchResponse } from "ofetch";
import { normalizeWorkpulseApiBase } from "~/utils/workpulse-api-base";
import { workpulseApiRaw } from "~/utils/workpulse-api-fetch";
import { readWorkpulseAccessToken } from "~/utils/workpulse-session-user";
import { useAuth } from "./useAuth";

function httpBaseToWs(base: string) {
  const b = base.replace(/\/$/, "");
  if (b.startsWith("https://")) return `wss://${b.slice(8)}`;
  if (b.startsWith("http://")) return `ws://${b.slice(7)}`;
  return "";
}

type ApiEnvelope<T> = {
  ok: boolean;
  data?: T;
  error?: { code?: string; message?: string };
};

type WSTicketData = {
  ticket: string;
  path?: string;
};

type ChatMessageRealtimeRow = {
  id: number;
  userId: number;
  authorName: string;
  body?: string;
  attachments?: unknown[];
  createdAt?: string;
};

function bearerHeaders(): { Authorization: string } | undefined {
  const { accessToken } = useAuth();
  const t = accessToken.value || readWorkpulseAccessToken();
  if (!t) return undefined;
  return { Authorization: `Bearer ${t}` };
}

function parseEnvelope<T>(raw: FetchResponse<unknown>): T | null {
  let body = raw._data as ApiEnvelope<T> | string | undefined;
  if (typeof body === "string") {
    try {
      body = JSON.parse(body) as ApiEnvelope<T>;
    } catch {
      body = undefined;
    }
  }
  if (raw.status < 200 || raw.status >= 300 || !body?.ok || !body.data) return null;
  return body.data;
}

function wsRootFromApiBase(base: string) {
  const b = base.replace(/\/$/, "");
  const direct = httpBaseToWs(b);
  if (direct) return direct;
  if (!import.meta.client || !b.startsWith("/")) return "";
  const proto = window.location.protocol === "https:" ? "wss:" : "ws:";
  return `${proto}//${window.location.host}${b}`;
}

/**
 * WebSocket realtime ke API WorkPulse (client-only).
 * Path default: /api/v1/ws — sesuaikan dengan BE jika berbeda.
 */
export function useWorkpulseRealtime() {
  if (import.meta.server) return;

  const config = useRuntimeConfig();
  const { isAuthenticated } = useAuth();
  const wsRef = shallowRef<WebSocket | null>(null);
  let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
  let reconnectAttempts = 0;
  let chatPollTimer: ReturnType<typeof setInterval> | null = null;
  let lastChatMessageId = 0;
  let chatPollPrimed = false;

  const disconnect = () => {
    if (reconnectTimer) {
      clearTimeout(reconnectTimer);
      reconnectTimer = null;
    }
    if (chatPollTimer) {
      clearInterval(chatPollTimer);
      chatPollTimer = null;
    }
    try {
      wsRef.value?.close();
    } catch {
      /* ignore */
    }
    wsRef.value = null;
  };

  const pollLatestChat = async (base: string) => {
    if (!isAuthenticated.value) return;
    const raw = await workpulseApiRaw(`${base}/api/v1/chat/messages?limit=1`, {
      method: "GET",
      headers: bearerHeaders(),
      credentials: "include",
      ignoreResponseError: true
    });
    const rows = parseEnvelope<ChatMessageRealtimeRow[]>(raw);
    const latest = Array.isArray(rows) ? rows[0] : null;
    if (!latest?.id) {
      chatPollPrimed = true;
      return;
    }
    if (!chatPollPrimed) {
      lastChatMessageId = latest.id;
      chatPollPrimed = true;
      return;
    }
    if (latest.id <= lastChatMessageId) return;
    lastChatMessageId = latest.id;
    window.dispatchEvent(
      new CustomEvent("workpulse:realtime", {
        detail: { type: "chat.message.created", payload: latest, source: "poll" }
      })
    );
  };

  const startChatPolling = (base: string) => {
    if (chatPollTimer) return;
    void pollLatestChat(base);
    chatPollTimer = setInterval(() => {
      void pollLatestChat(base);
    }, 1500);
  };

  const connect = async () => {
    disconnect();
    if (!isAuthenticated.value) return;

    const base = normalizeWorkpulseApiBase(String(config.public.workpulseApiBase || "").trim());
    if (!base) return;
    if (base.startsWith("/")) {
      startChatPolling(base);
      return;
    }

    const raw = await workpulseApiRaw(`${base}/api/v1/ws/ticket`, {
      method: "POST",
      headers: bearerHeaders(),
      credentials: "include",
      body: {},
      ignoreResponseError: true
    });
    const ticketData = parseEnvelope<WSTicketData>(raw);
    if (!ticketData?.ticket) return;

    const wsRoot = wsRootFromApiBase(base);
    if (!wsRoot) return;

    const path = String(ticketData.path || config.public.workpulseWsPath || "/api/v1/ws");
    const sep = path.includes("?") ? "&" : "?";
    const url = `${wsRoot}${path.startsWith("/") ? path : `/${path}`}${sep}ticket=${encodeURIComponent(ticketData.ticket)}`;

    try {
      const socket = new WebSocket(url);
      socket.addEventListener("open", () => {
        reconnectAttempts = 0;
      });
      socket.addEventListener("message", (event) => {
        try {
          const data = JSON.parse(String(event.data));
          if (data?.type) {
            window.dispatchEvent(new CustomEvent("workpulse:realtime", { detail: data }));
          }
        } catch {
          /* ignore */
        }
      });
      socket.addEventListener("close", () => {
        if (wsRef.value === socket) wsRef.value = null;
        if (!isAuthenticated.value) return;
        reconnectAttempts += 1;
        const delay = Math.min(20_000, 1000 * reconnectAttempts);
        reconnectTimer = setTimeout(() => {
          void connect();
        }, delay);
      });
      wsRef.value = socket;
    } catch {
      /* ignore */
    }
  };

  watch(
    () => isAuthenticated.value,
    (ok) => {
      if (ok) void connect();
      else disconnect();
    },
    { immediate: true }
  );
}
