<template>
  <div class="order-chat flex flex-col" :class="compact ? 'h-[400px]' : 'h-[750px] max-h-[80vh]'">
    <!-- Header -->
    <div class="flex items-center justify-between px-4 py-3 border-b border-border/50 bg-card/60 backdrop-blur-sm rounded-t-2xl">
      <div class="flex items-center gap-2">
        <MessageCircle class="w-4 h-4 text-primary" />
        <span class="text-sm font-bold">{{ t("chat.title") }}</span>
        <span class="w-2 h-2 rounded-full" :class="wsConnected ? 'bg-emerald-500' : 'bg-rose-500'" :title="wsConnected ? 'Connected' : 'Disconnected'" />
        <span v-if="otherPartyOnline" class="text-[10px] text-emerald-500 font-bold ml-2 flex items-center gap-1 animate-in fade-in zoom-in duration-300">
          <Eye class="w-3 h-3" /> Online
        </span>
      </div>
      <button v-if="closable" @click="$emit('close')" class="p-1 hover:bg-secondary rounded-lg transition-colors">
        <X class="w-4 h-4 text-muted-foreground" />
      </button>
    </div>

    <!-- Image Modal -->
    <div v-if="selectedImage" class="fixed inset-0 z-[100] flex items-center justify-center bg-background/90 backdrop-blur-md" @click="selectedImage = null">
      <div class="relative max-w-[90vw] max-h-[90vh]">
        <button @click.stop="selectedImage = null" class="absolute -top-12 right-0 p-2 text-foreground hover:text-primary transition-colors bg-card/50 rounded-full backdrop-blur-sm">
          <X class="w-6 h-6" />
        </button>
        <img :src="getImageUrl(selectedImage)" class="max-w-full max-h-[85vh] rounded-xl shadow-2xl object-contain border border-border/50 animate-in zoom-in-95 duration-200" @click.stop />
      </div>
    </div>

    <!-- Context Menu -->
    <Teleport to="body">
      <div v-if="contextMenu.visible" 
           class="fixed z-[9999] bg-card/95 backdrop-blur-md border border-border/50 shadow-2xl rounded-xl py-1 min-w-[140px] animate-in fade-in zoom-in-95 duration-100"
           :style="{ top: `${contextMenu.y}px`, left: `${contextMenu.x}px` }"
           @click.stop>
        <button @click="deleteMessage(contextMenu.msgId)" 
                class="w-full text-left px-4 py-2.5 text-sm text-rose-500 hover:bg-rose-500/10 font-bold transition-colors flex items-center gap-2">
          <Trash2 class="w-4 h-4" /> {{ t("chat.delete") }}
        </button>
      </div>
    </Teleport>

    <!-- Messages area -->
    <div ref="messagesContainer" class="flex-1 overflow-y-auto px-4 py-3 space-y-3 scrollbar-thin">
      <div v-if="isLoading" class="flex items-center justify-center h-full">
        <Loader2 class="w-6 h-6 animate-spin text-primary" />
      </div>

      <div v-else-if="messages.length === 0" class="flex flex-col items-center justify-center h-full text-center opacity-60">
        <MessageCircle class="w-10 h-10 text-muted-foreground mb-3 opacity-40" />
        <p class="text-sm text-muted-foreground">{{ t("chat.empty") }}</p>
      </div>

      <template v-else>
        <div
          v-for="msg in parsedMessages"
          :key="msg.id"
          class="flex"
          :class="msg.is_from_admin ? 'justify-start' : 'justify-end'"
        >
          <div
            class="max-w-[80%] px-3.5 py-2 rounded-2xl text-sm leading-relaxed shadow-sm transition-opacity hover:opacity-95"
            :class="msg.is_from_admin
              ? 'bg-secondary/80 text-foreground rounded-bl-md'
              : 'bg-primary text-primary-foreground rounded-br-md'"
            @contextmenu.prevent="openContextMenu($event, msg)"
          >
            <div v-if="msg.is_from_admin" class="flex items-center gap-1.5 mb-1">
              <ShieldCheck class="w-3 h-3 opacity-70" />
              <span class="text-[10px] font-bold uppercase tracking-wider opacity-70">
                {{ t("chat.admin") }}<span v-if="msg.first_name || msg.email" class="lowercase font-mono ml-1 text-[9px] opacity-70">({{ msg.first_name || msg.email.split('@')[0] }})</span>
              </span>
            </div>
            <div v-else class="flex items-center justify-end gap-1.5 mb-1">
              <span class="text-[10px] font-bold uppercase tracking-wider opacity-70">
                <span v-if="msg.first_name || msg.email" class="lowercase font-mono mr-1 text-[9px] opacity-70">{{ msg.first_name || msg.email.split('@')[0] }}</span>
              </span>
            </div>
            <div v-if="msg.imageUrl" class="mt-1 mb-1">
              <img :src="getImageUrl(msg.imageUrl)" @click="openImageModal(msg.imageUrl)" class="max-w-[150px] max-h-[150px] rounded-lg cursor-pointer hover:opacity-90 transition-opacity shadow-sm border border-border/20 object-cover" />
            </div>
            <p v-if="msg.parsedText" class="whitespace-pre-wrap break-words">{{ msg.parsedText }}</p>
            <p class="text-[10px] mt-1 opacity-50 text-right">{{ formatTime(msg.created_at) }}</p>
          </div>
        </div>
      </template>
    </div>

    <!-- Input area -->
    <div class="px-3 py-3 border-t border-border/50 bg-card/40 rounded-b-2xl">
      <form @submit.prevent="handleSend" class="flex gap-2 items-end">
        <textarea
          v-model="newMessage"
          :placeholder="t('chat.placeholder')"
          rows="1"
          class="flex-1 resize-none bg-background/80 border border-border/50 rounded-xl px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/40 transition-all placeholder:text-muted-foreground/50 max-h-[80px] overflow-y-auto"
          @keydown.enter.exact.prevent="handleSend"
          @input="autoResize($event)"
          @paste="handlePaste"
          ref="textareaRef"
        />
        <button
          type="submit"
          :disabled="!newMessage.trim() || isSending || cooldownLeft > 0"
          class="p-2.5 bg-primary text-primary-foreground rounded-xl hover:bg-primary/90 transition-all disabled:opacity-40 disabled:cursor-not-allowed flex-shrink-0 relative"
        >
          <Send v-if="!isSending && cooldownLeft === 0" class="w-4 h-4" />
          <Loader2 v-else-if="isSending" class="w-4 h-4 animate-spin" />
          <span v-else-if="cooldownLeft > 0" class="text-[10px] font-bold">{{ cooldownLeft }}s</span>
        </button>
      </form>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, nextTick, watch } from "vue";
import { useI18n } from "vue-i18n";
import { MessageCircle, Send, Loader2, ShieldCheck, X, Eye, Trash2 } from "lucide-vue-next";
import { getOrderMessages, sendOrderMessage, deleteOrderMessage, RESOURCES_BASE_URL } from "@/lib/api";
import { useAuthStore } from "@/stores/auth";
import { toast } from "vue-sonner";

import { getWsUrl } from "@/lib/utils";

const authStore = useAuthStore();

const props = defineProps<{
  orderId: number;
  compact?: boolean;
  closable?: boolean;
}>();

defineEmits<{ (e: 'close'): void }>();

const { t, locale } = useI18n();
const messages = ref<any[]>([]);
const newMessage = ref("");
const isLoading = ref(true);
const isSending = ref(false);
const cooldownLeft = ref(0);
const wsConnected = ref(false);
const messagesContainer = ref<HTMLElement | null>(null);
const textareaRef = ref<HTMLTextAreaElement | null>(null);
const otherPartyOnline = ref(false);
const selectedImage = ref<string | null>(null);
let cooldownInterval: ReturnType<typeof setInterval> | null = null;
const contextMenu = ref({ visible: false, x: 0, y: 0, msgId: null as number | null });

const openContextMenu = (e: MouseEvent, msg: any) => {
  const myRole = authStore.user?.role;
  const isMine = (myRole === 'admin' && msg.is_from_admin) || (myRole !== 'admin' && !msg.is_from_admin);
  if (myRole !== 'admin' && !isMine) return; // Users can only delete their own messages, Admin can delete any
  
  // Keep menu on screen
  let x = e.clientX ?? (e as any).touches?.[0]?.clientX ?? 0;
  let y = e.clientY ?? (e as any).touches?.[0]?.clientY ?? 0;
  if (window.innerWidth - x < 150) x -= 140;
  if (window.innerHeight - y < 100) y -= 60;

  contextMenu.value = { visible: true, x, y, msgId: msg.id };
};

const closeContextMenu = () => {
  contextMenu.value.visible = false;
};

async function deleteMessage(msgId: number | null) {
  if (!msgId) return;
  try {
    await deleteOrderMessage(props.orderId, msgId, locale.value);
    messages.value = messages.value.filter(m => m.id !== msgId);
    toast.success("Message deleted");
  } catch (err: any) {
    toast.error(err.message);
  } finally {
    closeContextMenu();
  }
}

const parsedMessages = computed(() => {
  return messages.value.map(msg => {
    let text = msg.message;
    let imageUrl = null;
    try {
      const parsed = JSON.parse(msg.message);
      if (parsed && typeof parsed === 'object') {
        if (parsed.image_url) imageUrl = parsed.image_url;
        if (parsed.text !== undefined) text = parsed.text;
        else if (parsed.image_url) text = ""; // If only image_url is present, don't render the json string as text
      }
    } catch {
      // Normal string message (fallback)
    }
    return { ...msg, parsedText: text, imageUrl };
  });
});

function startCooldown() {
  if (authStore.user?.role === 'admin') return;
  cooldownLeft.value = 10;
  if (cooldownInterval) clearInterval(cooldownInterval);
  cooldownInterval = setInterval(() => {
    cooldownLeft.value--;
    if (cooldownLeft.value <= 0) {
      if (cooldownInterval) clearInterval(cooldownInterval);
    }
  }, 1000);
}



let ws: WebSocket | null = null;
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;

function connectWebSocket() {
  const token = localStorage.getItem("token");
  if (!token) return;
  const url = `${getWsUrl()}/chat?token=${encodeURIComponent(token)}&order_id=${props.orderId}`;
  ws = new WebSocket(url);

  ws.onopen = () => {
    wsConnected.value = true;
  };

  ws.onmessage = (event) => {
    try {
      const msg = JSON.parse(event.data);
      
      if (msg.type === "presence") {
        const myRole = authStore.user?.role === 'admin' ? 'admin' : 'user';
        const otherRole = myRole === 'admin' ? 'user' : 'admin';
        otherPartyOnline.value = msg.online_roles.includes(otherRole);
        return;
      }
      
      if (msg.type === "message_deleted") {
        messages.value = messages.value.filter(m => m.id != msg.id);
        return;
      }
      
      // If it's a message (type 'message' or has essential fields)
      if (msg.type === "message" || (msg.message && msg.id)) {
        // Use lenient comparison (==) for ID to avoid string/int mismatches
        const exists = messages.value.some(m => m.id == msg.id);
        if (!exists) {
          messages.value.push(msg);
          scrollToBottom();
          
          // Audio & Read Receipt for incoming messages
          const myRole = authStore.user?.role === 'admin' ? 'admin' : 'user';
          const isMsgFromMe = (msg.is_from_admin && myRole === 'admin') || (!msg.is_from_admin && myRole === 'user');
          if (!isMsgFromMe) {
            authStore.playChatSound();
            ws?.send("read");
            setTimeout(() => authStore.refreshUnreadCount(), 300);
          }
        }
      }
    } catch (e) {
      console.error("WS parse error:", e);
    }
  };

  ws.onclose = (event) => {
    wsConnected.value = false;
    // Auto-reconnect after 3 seconds
    reconnectTimer = setTimeout(() => connectWebSocket(), 3000);
  };

  ws.onerror = (error) => {
    ws?.close();
  };
}

function disconnectWebSocket() {
  if (reconnectTimer) {
    clearTimeout(reconnectTimer);
    reconnectTimer = null;
  }
  if (ws) {
    ws.onclose = null; // prevent auto-reconnect
    ws.close();
    ws = null;
  }
  wsConnected.value = false;
}

async function loadMessages() {
  try {
    messages.value = await getOrderMessages(props.orderId, locale.value);
    authStore.refreshUnreadCount();
  } catch (e) {
    console.error("Failed to load messages:", e);
  } finally {
    isLoading.value = false;
  }
}

async function handleSend() {
  const text = newMessage.value.trim();
  if (!text || isSending.value) return;

  isSending.value = true;
  try {
    // If WS is connected and text only, send through it for "True Chat" experience
    if (ws && wsConnected.value && ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify({ type: "message", text }));
      newMessage.value = "";
      if (textareaRef.value) {
        textareaRef.value.style.height = "auto";
      }
    } else {
      // Fallback to API if WS is down
      const response = await sendOrderMessage(props.orderId, text, null, locale.value);
      newMessage.value = "";
      if (textareaRef.value) {
        textareaRef.value.style.height = "auto";
      }
      
      // Add message to list only if it hasn't arrived via WS yet
      if (!messages.value.some(m => m.id == response.id)) {
        const myRole = authStore.user?.role === 'admin' ? 'admin' : 'user';
        messages.value.push({
          id: response.id,
          message: response.message || text,
          is_from_admin: myRole === 'admin',
          created_at: new Date().toISOString()
        });
        scrollToBottom();
      }
    }
    
    // Start flood control cooldown
    startCooldown();
  } catch (e) {
    console.error("Failed to send message:", e);
  } finally {
    isSending.value = false;
  }
}

async function handlePaste(e: ClipboardEvent) {
  const items = e.clipboardData?.items;
  if (!items) return;
  for (let i = 0; i < items.length; i++) {
    if (items[i].type.indexOf("image") !== -1) {
      const file = items[i].getAsFile();
      if (file) {
        e.preventDefault();
        await uploadImage(file);
      }
    }
  }
}

async function uploadImage(file: File) {
  if (isSending.value) return;
  isSending.value = true;
  try {
    const response = await sendOrderMessage(props.orderId, "", file, locale.value);
    // Add message to list only if it hasn't arrived via WS yet
    if (!messages.value.some(m => m.id == response.id)) {
      const myRole = authStore.user?.role === 'admin' ? 'admin' : 'user';
      messages.value.push({
        id: response.id,
        message: response.message,
        is_from_admin: myRole === 'admin',
        created_at: new Date().toISOString()
      });
      scrollToBottom();
    }
    startCooldown();
  } catch (e: any) {
    console.error("Failed to upload image:", e);
    toast.error(t("errors.upload_failed") || "Failed to upload image");
  } finally {
    isSending.value = false;
  }
}

function getImageUrl(url: string) {
  if (!url) return '';
  if (url.startsWith('http')) return url;
  return `${RESOURCES_BASE_URL}/${url}`;
}

function openImageModal(url: string) {
  selectedImage.value = url;
}

function handleKeydown(e: KeyboardEvent) {
  if (e.key === 'Escape' && selectedImage.value) {
    selectedImage.value = null;
  }
}

function scrollToBottom() {
  nextTick(() => {
    if (messagesContainer.value) {
      messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
    }
  });
}

function autoResize(e: Event) {
  const el = e.target as HTMLTextAreaElement;
  el.style.height = "auto";
  el.style.height = Math.min(el.scrollHeight, 80) + "px";
}

function formatTime(dt: string) {
  if (!dt) return "";
  const d = new Date(dt);
  return d.toLocaleString(undefined, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
}

onMounted(async () => {
  await loadMessages();
  scrollToBottom();
  connectWebSocket();
  window.addEventListener('keydown', handleKeydown);
  document.addEventListener('click', closeContextMenu);
  document.addEventListener('scroll', closeContextMenu, true);
});

onUnmounted(() => {
  disconnectWebSocket();
  window.removeEventListener('keydown', handleKeydown);
  document.removeEventListener('click', closeContextMenu);
  document.removeEventListener('scroll', closeContextMenu, true);
});

watch(() => props.orderId, async () => {
  disconnectWebSocket();
  isLoading.value = true;
  await loadMessages();
  scrollToBottom();
  connectWebSocket();
});
</script>

<style scoped>
.scrollbar-thin::-webkit-scrollbar { width: 4px; }
.scrollbar-thin::-webkit-scrollbar-thumb { background: hsl(var(--border)); border-radius: 9999px; }
.scrollbar-thin::-webkit-scrollbar-track { background: transparent; }
</style>
