"use client";

import { ChangeEvent, DragEvent, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import type { Session } from "@supabase/supabase-js";
import { apiFetch } from "@/lib/api-client";
import { getSupabaseBrowser } from "@/lib/supabase-browser";
import type { FileItem, FolderItem, TeamSummary } from "@/lib/types";

type UploadStatus = "waiting" | "uploading" | "finalizing" | "done" | "error";
interface UploadItem { id: string; name: string; progress: number; status: UploadStatus; error?: string }
interface Crumb { id: string; name: string }

const SIMPLE_LIMIT = 100 * 1024 * 1024;

function formatBytes(bytes: number): string {
  if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
  const units = ["B", "KB", "MB", "GB", "TB"];
  const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
  const value = bytes / 1024 ** i;
  return `${value.toFixed(i === 0 ? 0 : value >= 10 ? 1 : 2)} ${units[i]}`;
}

function fileIcon(mime: string): string {
  if (mime.startsWith("image/")) return "🖼️";
  if (mime.startsWith("video/")) return "🎬";
  if (mime.startsWith("audio/")) return "🎵";
  if (mime.includes("pdf")) return "📕";
  if (mime.includes("zip") || mime.includes("rar") || mime.includes("7z")) return "🗜️";
  if (mime.includes("sheet") || mime.includes("excel")) return "📊";
  if (mime.includes("word") || mime.includes("document")) return "📝";
  return "📄";
}

function xhrPut(url: string, body: Blob, contentType: string | null, onProgress: (loaded: number) => void): Promise<string> {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("PUT", url);
    if (contentType) xhr.setRequestHeader("Content-Type", contentType);
    xhr.upload.onprogress = (event) => { if (event.lengthComputable) onProgress(event.loaded); };
    xhr.onerror = () => reject(new Error("Mất kết nối khi tải file lên R2."));
    xhr.onabort = () => reject(new Error("Upload đã bị hủy."));
    xhr.onload = () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(xhr.getResponseHeader("ETag") || xhr.getResponseHeader("etag") || "");
      } else {
        reject(new Error(`R2 từ chối upload (${xhr.status}). Hãy kiểm tra CORS và presigned URL.`));
      }
    };
    xhr.send(body);
  });
}

export default function DashboardApp() {
  const router = useRouter();
  const fileInput = useRef<HTMLInputElement>(null);
  const [session, setSession] = useState<Session | null>(null);
  const [authReady, setAuthReady] = useState(false);
  const [teams, setTeams] = useState<TeamSummary[]>([]);
  const [teamId, setTeamId] = useState("");
  const [folders, setFolders] = useState<FolderItem[]>([]);
  const [files, setFiles] = useState<FileItem[]>([]);
  const [crumbs, setCrumbs] = useState<Crumb[]>([]);
  const [loading, setLoading] = useState(false);
  const [message, setMessage] = useState("");
  const [search, setSearch] = useState("");
  const [view, setView] = useState<"grid" | "list">("grid");
  const [uploads, setUploads] = useState<UploadItem[]>([]);
  const [dragging, setDragging] = useState(false);

  const selectedTeam = teams.find((team) => team.id === teamId) || null;
  const currentFolderId = crumbs.at(-1)?.id || null;
  const canEdit = selectedTeam ? selectedTeam.role !== "viewer" : false;

  const filteredFolders = useMemo(() => folders.filter((item) => item.name.toLowerCase().includes(search.toLowerCase())), [folders, search]);
  const filteredFiles = useMemo(() => files.filter((item) => item.original_name.toLowerCase().includes(search.toLowerCase())), [files, search]);

  const updateUpload = useCallback((id: string, patch: Partial<UploadItem>) => {
    setUploads((items) => items.map((item) => item.id === id ? { ...item, ...patch } : item));
  }, []);

  useEffect(() => {
    const supabase = getSupabaseBrowser();
    supabase.auth.getSession().then(({ data }) => {
      if (!data.session) router.replace("/login");
      else setSession(data.session);
      setAuthReady(true);
    });
    const { data: listener } = supabase.auth.onAuthStateChange((_event, next) => {
      setSession(next);
      if (!next) router.replace("/login");
    });
    return () => listener.subscription.unsubscribe();
  }, [router]);

  const loadTeams = useCallback(async () => {
    const result = await apiFetch<{ teams: TeamSummary[] }>("/api/teams");
    setTeams(result.teams);
    if (!teamId && result.teams.length) {
      const requested = new URLSearchParams(window.location.search).get("team");
      const next = result.teams.some((team) => team.id === requested) ? requested! : result.teams[0].id;
      setTeamId(next);
    }
  }, [teamId]);

  const loadContents = useCallback(async () => {
    if (!teamId) { setFolders([]); setFiles([]); return; }
    setLoading(true); setMessage("");
    try {
      const suffix = `teamId=${encodeURIComponent(teamId)}${currentFolderId ? `&parentId=${encodeURIComponent(currentFolderId)}` : ""}`;
      const fileSuffix = `teamId=${encodeURIComponent(teamId)}${currentFolderId ? `&folderId=${encodeURIComponent(currentFolderId)}` : ""}`;
      const [folderResult, fileResult] = await Promise.all([
        apiFetch<{ folders: FolderItem[] }>(`/api/folders?${suffix}`),
        apiFetch<{ files: FileItem[] }>(`/api/files?${fileSuffix}`)
      ]);
      setFolders(folderResult.folders);
      setFiles(fileResult.files);
    } catch (error) {
      setMessage(error instanceof Error ? error.message : "Không tải được dữ liệu.");
    } finally { setLoading(false); }
  }, [teamId, currentFolderId]);

  useEffect(() => { if (session) loadTeams().catch((e) => setMessage(e.message)); }, [session, loadTeams]);
  useEffect(() => { loadContents(); }, [loadContents]);
  useEffect(() => { setCrumbs([]); }, [teamId]);

  async function createTeam() {
    const name = window.prompt("Tên nhóm mới:", "Nhóm của tôi")?.trim();
    if (!name) return;
    try {
      const result = await apiFetch<{ team: TeamSummary }>("/api/teams", { method: "POST", body: JSON.stringify({ name }) });
      await loadTeams(); setTeamId(result.team.id); setMessage("Đã tạo nhóm mới.");
    } catch (error) { setMessage(error instanceof Error ? error.message : "Không tạo được nhóm."); }
  }

  async function createFolder() {
    if (!selectedTeam || !canEdit) return;
    const name = window.prompt("Tên thư mục mới:")?.trim();
    if (!name) return;
    try {
      await apiFetch("/api/folders", { method: "POST", body: JSON.stringify({ teamId, parentId: currentFolderId, name }) });
      await loadContents();
    } catch (error) { setMessage(error instanceof Error ? error.message : "Không tạo được thư mục."); }
  }

  async function uploadSimple(file: File, queueId: string) {
    const presign = await apiFetch<{ multipartRequired: boolean; fileId?: string; uploadUrl?: string }>("/api/uploads/presign", {
      method: "POST",
      body: JSON.stringify({ teamId, folderId: currentFolderId, fileName: file.name, fileSize: file.size, mimeType: file.type || "application/octet-stream" })
    });
    if (presign.multipartRequired || file.size > SIMPLE_LIMIT) return uploadMultipart(file, queueId);
    if (!presign.uploadUrl || !presign.fileId) throw new Error("API không trả về URL upload.");
    updateUpload(queueId, { status: "uploading" });
    await xhrPut(presign.uploadUrl, file, file.type || "application/octet-stream", (loaded) => {
      updateUpload(queueId, { progress: Math.min(98, Math.round(loaded / file.size * 98)) });
    });
    updateUpload(queueId, { status: "finalizing", progress: 99 });
    await apiFetch("/api/uploads/complete", { method: "POST", body: JSON.stringify({ fileId: presign.fileId }) });
  }

  async function uploadMultipart(file: File, queueId: string) {
    const start = await apiFetch<{ fileId: string; uploadId: string; partSize: number; numberOfParts: number }>("/api/uploads/multipart/start", {
      method: "POST",
      body: JSON.stringify({ teamId, folderId: currentFolderId, fileName: file.name, fileSize: file.size, mimeType: file.type || "application/octet-stream" })
    });
    updateUpload(queueId, { status: "uploading" });
    const loaded = new Array(start.numberOfParts).fill(0);
    const completed: { partNumber: number; etag: string }[] = [];
    let cursor = 0;

    async function uploadPart(index: number) {
      const partNumber = index + 1;
      const begin = index * start.partSize;
      const end = Math.min(file.size, begin + start.partSize);
      const blob = file.slice(begin, end);
      let lastError: unknown;
      for (let attempt = 1; attempt <= 3; attempt++) {
        try {
          const signed = await apiFetch<{ uploadUrl: string }>("/api/uploads/multipart/sign-part", {
            method: "POST", body: JSON.stringify({ fileId: start.fileId, uploadId: start.uploadId, partNumber })
          });
          const etag = await xhrPut(signed.uploadUrl, blob, null, (partLoaded) => {
            loaded[index] = partLoaded;
            const total = loaded.reduce((sum, value) => sum + value, 0);
            updateUpload(queueId, { progress: Math.min(98, Math.round(total / file.size * 98)) });
          });
          if (!etag) throw new Error("Không đọc được ETag. Hãy thêm ETag vào ExposeHeaders trong CORS của R2.");
          loaded[index] = blob.size;
          completed.push({ partNumber, etag });
          return;
        } catch (error) {
          lastError = error;
          await new Promise((resolve) => setTimeout(resolve, 700 * attempt));
        }
      }
      throw lastError;
    }

    async function worker() {
      while (true) {
        const index = cursor++;
        if (index >= start.numberOfParts) return;
        await uploadPart(index);
      }
    }

    try {
      await Promise.all(Array.from({ length: Math.min(3, start.numberOfParts) }, () => worker()));
      updateUpload(queueId, { status: "finalizing", progress: 99 });
      await apiFetch("/api/uploads/multipart/complete", {
        method: "POST", body: JSON.stringify({ fileId: start.fileId, uploadId: start.uploadId, parts: completed })
      });
    } catch (error) {
      await apiFetch("/api/uploads/multipart/abort", {
        method: "POST", body: JSON.stringify({ fileId: start.fileId, uploadId: start.uploadId })
      }).catch(() => undefined);
      throw error;
    }
  }

  async function processFiles(list: FileList | File[]) {
    if (!teamId || !canEdit) return;
    const selected = Array.from(list);
    for (const file of selected) {
      const queueId = crypto.randomUUID();
      setUploads((items) => [...items, { id: queueId, name: file.name, progress: 0, status: "waiting" }]);
      try {
        await uploadSimple(file, queueId);
        updateUpload(queueId, { status: "done", progress: 100 });
        await loadContents(); await loadTeams();
      } catch (error) {
        updateUpload(queueId, { status: "error", error: error instanceof Error ? error.message : "Upload thất bại." });
      }
    }
  }

  function onFiles(event: ChangeEvent<HTMLInputElement>) {
    if (event.target.files?.length) processFiles(event.target.files);
    event.target.value = "";
  }

  function onDrop(event: DragEvent) {
    event.preventDefault(); setDragging(false);
    if (event.dataTransfer.files.length) processFiles(event.dataTransfer.files);
  }

  async function download(file: FileItem) {
    try {
      const result = await apiFetch<{ url: string }>(`/api/files/${file.id}/download`, { method: "POST" });
      window.location.href = result.url;
    } catch (error) { setMessage(error instanceof Error ? error.message : "Không tải được file."); }
  }

  async function share(file: FileItem) {
    const password = window.prompt("Mật khẩu liên kết (để trống nếu không cần):", "") ?? "";
    const daysText = window.prompt("Liên kết có hiệu lực bao nhiêu ngày?", "7");
    if (daysText === null) return;
    const expiresInDays = Math.max(1, Math.min(365, Number(daysText) || 7));
    try {
      const result = await apiFetch<{ shareUrl: string }>(`/api/files/${file.id}/share`, {
        method: "POST", body: JSON.stringify({ password, expiresInDays })
      });
      await navigator.clipboard.writeText(result.shareUrl).catch(() => undefined);
      window.prompt("Đã tạo link và cố gắng sao chép vào clipboard:", result.shareUrl);
    } catch (error) { setMessage(error instanceof Error ? error.message : "Không tạo được link."); }
  }

  async function invite() {
    if (!selectedTeam) return;
    try {
      const result = await apiFetch<{ inviteUrl: string }>(`/api/teams/${selectedTeam.id}/invite`, {
        method: "POST", body: JSON.stringify({ role: "member", expiresInDays: 7, maxUses: 20 })
      });
      await navigator.clipboard.writeText(result.inviteUrl).catch(() => undefined);
      window.prompt("Liên kết mời thành viên (hiệu lực 7 ngày):", result.inviteUrl);
    } catch (error) { setMessage(error instanceof Error ? error.message : "Không tạo được lời mời."); }
  }

  async function rename(file: FileItem) {
    const name = window.prompt("Tên file mới:", file.original_name)?.trim();
    if (!name || name === file.original_name) return;
    try {
      await apiFetch(`/api/files/${file.id}`, { method: "PATCH", body: JSON.stringify({ name }) });
      await loadContents();
    } catch (error) { setMessage(error instanceof Error ? error.message : "Không đổi được tên."); }
  }

  async function remove(file: FileItem) {
    if (!window.confirm(`Xóa vĩnh viễn “${file.original_name}”?`)) return;
    try {
      await apiFetch(`/api/files/${file.id}`, { method: "DELETE" });
      await loadContents(); await loadTeams();
    } catch (error) { setMessage(error instanceof Error ? error.message : "Không xóa được file."); }
  }

  async function logout() {
    await getSupabaseBrowser().auth.signOut();
    router.replace("/login");
  }

  if (!authReady || !session) return <main className="centerPage"><div className="spinner"></div><p>Đang mở kho dữ liệu…</p></main>;

  return (
    <main className="appShell" onDragOver={(e) => { e.preventDefault(); setDragging(true); }} onDragLeave={() => setDragging(false)} onDrop={onDrop}>
      {dragging && <div className="dropOverlay"><div>⬆️<strong>Thả file để tải lên</strong><span>File được giữ nguyên chất lượng gốc</span></div></div>}
      <aside className="sidebar">
        <div className="brand"><span className="brandMark">G</span> GroupVault</div>
        <button className="button primary full uploadMain" disabled={!canEdit} onClick={() => fileInput.current?.click()}>＋ Tải file lên</button>
        <input ref={fileInput} type="file" multiple hidden onChange={onFiles} />
        <nav className="sideNav"><button className="active">📁 <span>Tệp của nhóm</span></button><button disabled>🕘 <span>Gần đây</span></button><button disabled>🔗 <span>Được chia sẻ</span></button><button disabled>🗑️ <span>Thùng rác</span></button></nav>
        <div className="teamBox">
          <label>Nhóm đang mở</label>
          <select value={teamId} onChange={(e) => setTeamId(e.target.value)}>{teams.map((team) => <option key={team.id} value={team.id}>{team.name}</option>)}</select>
          <div className="teamActions"><button onClick={createTeam}>＋ Nhóm mới</button>{selectedTeam && ["owner", "admin"].includes(selectedTeam.role) && <button onClick={invite}>Mời người</button>}</div>
        </div>
        {selectedTeam && <div className="storage"><div><span>Dung lượng</span><b>{formatBytes(selectedTeam.storage_used_bytes)}</b></div><div className="storageBar"><i style={{ width: `${Math.min(100, selectedTeam.storage_used_bytes / selectedTeam.storage_limit_bytes * 100)}%` }}></i></div><small>Giới hạn {formatBytes(selectedTeam.storage_limit_bytes)}</small></div>}
        <button className="account" onClick={logout}><span>{session.user.email?.slice(0, 1).toUpperCase()}</span><div><b>{session.user.user_metadata?.display_name || session.user.email?.split("@")[0]}</b><small>Đăng xuất</small></div></button>
      </aside>

      <section className="workspace">
        <header className="topbar">
          <div className="searchBox">⌕<input value={search} onChange={(e) => setSearch(e.target.value)} placeholder="Tìm trong thư mục hiện tại…" /></div>
          <div className="viewSwitch"><button className={view === "grid" ? "active" : ""} onClick={() => setView("grid")}>▦</button><button className={view === "list" ? "active" : ""} onClick={() => setView("list")}>☷</button></div>
        </header>
        <div className="contentHead">
          <div className="breadcrumbs"><button onClick={() => setCrumbs([])}>Tệp của nhóm</button>{crumbs.map((crumb, index) => <span key={crumb.id}>› <button onClick={() => setCrumbs((items) => items.slice(0, index + 1))}>{crumb.name}</button></span>)}</div>
          <div className="contentActions"><button className="button ghost" disabled={!canEdit} onClick={createFolder}>📁 Thư mục mới</button><button className="button primary" disabled={!canEdit} onClick={() => fileInput.current?.click()}>⬆ Tải lên</button></div>
        </div>
        {message && <div className="notice inline">{message}<button onClick={() => setMessage("")}>×</button></div>}

        {!selectedTeam && <section className="emptyState"><div>🗂️</div><h2>Tạo nhóm đầu tiên</h2><p>Mỗi nhóm có thư mục, thành viên và kho file riêng.</p><button className="button primary" onClick={createTeam}>Tạo nhóm</button></section>}
        {selectedTeam && loading && <section className="emptyState"><div className="spinner"></div><p>Đang tải dữ liệu…</p></section>}
        {selectedTeam && !loading && filteredFolders.length === 0 && filteredFiles.length === 0 && <section className="emptyState"><div>☁️</div><h2>Thư mục đang trống</h2><p>Kéo thả file vào đây hoặc bấm “Tải lên”.</p>{canEdit && <button className="button primary" onClick={() => fileInput.current?.click()}>Chọn file</button>}</section>}

        {selectedTeam && !loading && (filteredFolders.length > 0 || filteredFiles.length > 0) && <div className={view === "grid" ? "fileGrid" : "fileList"}>
          {filteredFolders.map((folder) => <article key={folder.id} className="fileCard folderCard" onDoubleClick={() => setCrumbs((items) => [...items, { id: folder.id, name: folder.name }])}>
            <button className="cardMain" onClick={() => setCrumbs((items) => [...items, { id: folder.id, name: folder.name }])}><span className="bigIcon">📁</span><div><b>{folder.name}</b><small>Thư mục</small></div></button>
          </article>)}
          {filteredFiles.map((file) => <article key={file.id} className="fileCard">
            <button className="cardMain" onDoubleClick={() => file.status === "ready" && download(file)}><span className="bigIcon">{fileIcon(file.mime_type)}</span><div><b title={file.original_name}>{file.original_name}</b><small>{formatBytes(file.size_bytes)} · {file.status === "ready" ? "Sẵn sàng" : file.status}</small></div></button>
            <div className="cardActions"><button disabled={file.status !== "ready"} onClick={() => download(file)} title="Tải file gốc">↓</button>{canEdit && <button disabled={file.status !== "ready"} onClick={() => share(file)} title="Chia sẻ">↗</button>}{canEdit && <button onClick={() => rename(file)} title="Đổi tên">✎</button>}{canEdit && <button className="dangerText" onClick={() => remove(file)} title="Xóa">×</button>}</div>
          </article>)}
        </div>}
      </section>

      {uploads.length > 0 && <aside className="uploadQueue"><div className="queueHead"><b>Đang tải lên</b><button onClick={() => setUploads((items) => items.filter((item) => item.status !== "done"))}>Thu gọn</button></div>{uploads.map((item) => <div className="queueItem" key={item.id}><div className="queueLine"><span>{item.status === "done" ? "✓" : item.status === "error" ? "!" : "⬆"}</span><div><b>{item.name}</b><small>{item.status === "waiting" ? "Đang chờ" : item.status === "uploading" ? `${item.progress}%` : item.status === "finalizing" ? "Đang kiểm tra file…" : item.status === "done" ? "Hoàn tất" : item.error}</small></div></div><div className="progress"><i style={{ width: `${item.progress}%` }}></i></div></div>)}</aside>}
    </main>
  );
}
