import React from "react";
import { fmtClock } from "./time";
import { LoadingTitle } from "./loading-indicator";
import { errorMessage, requestJson } from "./unidesk-error";
import { UniDeskErrorBanner } from "./unidesk-error-banner";

type AnyRecord = Record<string, any>;

const h = React.createElement;
const { useEffect } = React;
const useState: any = React.useState;
const useRef: any = React.useRef;

const filebrowserCompactCss = `
:root {
  --surfacePrimary: #ffffff;
  --surfaceSecondary: #f8fafc;
  --background: #f8fafc;
  --divider: #e5e7eb;
  --borderPrimary: #dbe3ee;
  --textPrimary: #17212f;
  --textSecondary: #64748b;
  --iconSecondary: #ffffff;
  --blue: #2283e8;
}
* {
  box-sizing: border-box !important;
}
html, body {
  font-size: 13px !important;
}
body {
  margin: 0 !important;
  background: var(--surfacePrimary) !important;
  color: var(--textPrimary) !important;
  font-family: Arial, sans-serif !important;
}
button,
input {
  font: inherit !important;
}
header {
  height: 42px !important;
  min-height: 42px !important;
  padding: 4px 8px !important;
}
header title {
  font-size: 14px !important;
  padding: 0 8px !important;
}
header img {
  height: 28px !important;
}
header .action {
  min-width: 30px !important;
  height: 30px !important;
  padding: 4px 6px !important;
}
header .search {
  max-width: 360px !important;
  height: 32px !important;
}
nav {
  top: 42px !important;
  width: 12rem !important;
}
nav .action {
  min-height: 34px !important;
  padding: 5px 8px !important;
  font-size: 13px !important;
}
nav .action i {
  margin-right: 6px !important;
}
nav > div {
  padding: 3px 0 !important;
}
main {
  width: calc(100% - 12rem) !important;
  margin-left: 12rem !important;
}
main > div {
  padding-top: 42px !important;
}
.material-icons {
  width: 22px !important;
  min-width: 22px !important;
  max-width: 22px !important;
  height: 22px !important;
  font-family: Arial, sans-serif !important;
  font-size: 0 !important;
  line-height: 22px !important;
  overflow: hidden !important;
  text-align: center !important;
  text-transform: none !important;
}
.material-icons::before {
  content: "." !important;
  display: inline-block !important;
  font-family: Arial, sans-serif !important;
  font-size: 11px !important;
  font-weight: 700 !important;
  line-height: 22px !important;
}
header .material-icons::before,
nav .material-icons::before {
  content: "op" !important;
  font-size: 10px !important;
}
#listing {
  min-height: 0 !important;
}
#listing h2 {
  margin: 4px 8px !important;
  font-size: 11px !important;
}
#listing > div {
  display: block !important;
}
#listing.mosaic,
#listing.list {
  width: 100% !important;
  margin: 0 !important;
  padding-top: 4px !important;
}
#listing.mosaic .header,
#listing.list .header {
  display: flex !important;
  min-height: 26px !important;
  background: var(--background) !important;
}
#listing.mosaic .item,
#listing.list .item {
  width: calc(100% - 8px) !important;
  min-height: 30px !important;
  margin: 0 4px !important;
  padding: 3px 8px !important;
  border-radius: 0 !important;
  border-bottom: 1px solid var(--divider) !important;
  box-shadow: none !important;
}
#listing.mosaic .item div:first-of-type,
#listing.list .item div:first-of-type {
  width: 30px !important;
  min-width: 30px !important;
  height: 24px !important;
}
#listing .item i {
  margin-right: 4px !important;
}
#listing .item i::before {
  content: "file" !important;
  color: inherit !important;
  font-size: 10px !important;
}
#listing .item[data-dir="true"] i::before {
  content: "dir" !important;
}
#listing.mosaic .item div:last-of-type,
#listing.list .item div:last-of-type {
  width: calc(100% - 34px) !important;
  display: grid !important;
  grid-template-columns: minmax(180px, 1fr) 90px 140px !important;
  gap: 8px !important;
  align-items: center !important;
}
#listing .item .name {
  min-width: 0 !important;
  font-size: 13px !important;
  font-weight: 600 !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
  white-space: nowrap !important;
}
#listing .item .size,
#listing .item .modified {
  min-width: 0 !important;
  color: var(--textSecondary) !important;
  font-size: 11px !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
  white-space: nowrap !important;
}
#multiple-selection {
  height: 36px !important;
  min-height: 36px !important;
  padding: 4px 8px !important;
}
@media (max-width: 780px) {
  nav {
    width: 9rem !important;
  }
  main {
    width: calc(100% - 9rem) !important;
    margin-left: 9rem !important;
  }
  #listing.mosaic .item div:last-of-type,
  #listing.list .item div:last-of-type {
    grid-template-columns: minmax(120px, 1fr) 80px !important;
  }
  #listing .item .modified {
    display: none !important;
  }
}
`;

function Panel({ title, eyebrow, actions, children, className, loading }: AnyRecord) {
  return h("section", { className: `panel ${className || ""}` },
    h("div", { className: "panel-head" },
      h("div", null,
        eyebrow ? h("p", { className: "panel-eyebrow" }, eyebrow) : null,
        h(LoadingTitle, { title, loading }),
      ),
      actions ? h("div", { className: "panel-actions" }, actions) : null,
    ),
    h("div", { className: "panel-body" }, children),
  );
}

function RawButton({ title, data, onOpen, testId }: AnyRecord) {
  return h("button", {
    type: "button",
    className: "ghost-btn",
    "data-testid": testId,
    onClick: () => onOpen(title, data),
  }, "查看原始JSON");
}

function EmptyState({ title, text }: AnyRecord) {
  return h("div", { className: "empty-state" }, h("strong", null, title), h("span", null, text));
}

function microserviceRuntime(service: any): AnyRecord {
  return service?.runtime && typeof service.runtime === "object" && !Array.isArray(service.runtime) ? service.runtime : {};
}

function microserviceBackend(service: any): AnyRecord {
  return service?.backend && typeof service.backend === "object" && !Array.isArray(service.backend) ? service.backend : {};
}

function microserviceRepository(service: any): AnyRecord {
  return service?.repository && typeof service.repository === "object" && !Array.isArray(service.repository) ? service.repository : {};
}

function filebrowserServices(microservices: any[]): any[] {
  const services = microservices.filter((item) => item?.id === "filebrowser" || String(item?.id || "").startsWith("filebrowser-"));
  return services.sort((a, b) => {
    const rank = (item: any) => item.providerId === "D518" ? 0 : item.providerId === "D601" ? 1 : item.id === "filebrowser" ? 2 : 3;
    return rank(a) - rank(b) || String(a.id).localeCompare(String(b.id));
  });
}

function serviceLabel(service: any): string {
  if (service?.providerId === "D518") return "D518";
  return service?.providerId || service?.name || service?.id || "Unknown";
}

function browserProxyUrl(apiBaseUrl: string, serviceId: string, suffix = "/"): string {
  const path = suffix.startsWith("/") ? suffix : `/${suffix}`;
  return `${apiBaseUrl}/microservices/${encodeURIComponent(serviceId)}/proxy${path}`;
}

function healthUrl(apiBaseUrl: string, serviceId: string): string {
  return `${apiBaseUrl}/microservices/${encodeURIComponent(serviceId)}/health`;
}

async function requestHealthWithTimeout(url: string, timeoutMs = 16000): Promise<any> {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  try {
    return await requestJson(url, { signal: controller.signal, failureFields: [false] });
  } finally {
    clearTimeout(timer);
  }
}

function targetMountHint(service: any): string {
  if (service?.providerId === "main-server") return "host / -> /srv";
  if (service?.providerId === "D601" || service?.providerId === "D518") return "WSL / + /mnt/c -> /srv";
  return "provider / -> /srv";
}

function healthOk(value: any): boolean {
  return value?.status === "OK" || value?.ok === true;
}

function TargetCard({ service, active, health, onSelect, onRaw }: AnyRecord) {
  const runtime = microserviceRuntime(service);
  const backend = microserviceBackend(service);
  const repository = microserviceRepository(service);
  const container = runtime.container || {};
  const ok = healthOk(health?.body);
  return h("button", {
    type: "button",
    className: `filebrowser-target-card ${active ? "active" : ""}`,
    "data-testid": `filebrowser-target-card-${service.id}`,
    onClick: onSelect,
  },
    h("span", { className: `status-badge ${ok ? "ok" : runtime.providerStatus === "online" ? "running" : "warn"}` }, ok ? "Health OK" : runtime.providerStatus || "unknown"),
    h("strong", null, service.name || service.id),
    h("span", null, targetMountHint(service)),
    h("code", null, `${backend.nodeBindHost || "--"}:${backend.nodePort || "--"}`),
    h("small", null, container.name ? `${container.name} / ${container.state || "--"}` : `${repository.composeService || "--"}`),
    h("span", { className: "filebrowser-card-raw", onClick: (event: any) => { event.stopPropagation(); onRaw(`${service.name} service`, service); } }, "JSON"),
  );
}

function frameDocument(iframe: any): Document | null {
  try {
    return iframe?.contentDocument || iframe?.contentWindow?.document || null;
  } catch {
    return null;
  }
}

function injectFilebrowserCompactStyle(iframe: any): boolean {
  const doc = frameDocument(iframe);
  if (doc === null || doc.head === null) return false;
  let style = doc.getElementById("unidesk-filebrowser-compact-style") as HTMLStyleElement | null;
  if (style === null) {
    style = doc.createElement("style");
    style.id = "unidesk-filebrowser-compact-style";
    doc.head.appendChild(style);
  }
  if (style.textContent !== filebrowserCompactCss) style.textContent = filebrowserCompactCss;
  return true;
}

function downloadBlob(blob: Blob, filename: string): void {
  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.href = url;
  link.download = filename;
  document.body.appendChild(link);
  link.click();
  link.remove();
  setTimeout(() => URL.revokeObjectURL(url), 2000);
}

function exportIframeScreenshot(iframe: any, filename: string): void {
  const doc = frameDocument(iframe);
  if (doc === null || doc.documentElement === null) throw new Error("无法访问 File Browser iframe 文档");
  injectFilebrowserCompactStyle(iframe);
  const width = Math.max(640, Math.ceil(iframe.clientWidth || doc.documentElement.clientWidth || 1280));
  const height = Math.max(480, Math.ceil(iframe.clientHeight || doc.documentElement.clientHeight || 720));
  const clone = doc.documentElement.cloneNode(true) as HTMLElement;
  clone.querySelectorAll("script, style, link[rel='stylesheet'], link[rel='preload'], link[rel='icon']").forEach((node) => node.remove());
  clone.querySelectorAll("img").forEach((image) => {
    image.removeAttribute("src");
    image.removeAttribute("srcset");
  });
  let head = clone.querySelector("head");
  if (head === null) {
    head = doc.createElement("head");
    clone.insertBefore(head, clone.firstChild);
  }
  const style = doc.createElement("style");
  style.textContent = `${filebrowserCompactCss}\nhtml,body{width:${width}px!important;min-height:${height}px!important;overflow:hidden!important;}`;
  head.appendChild(style);
  const xhtml = new XMLSerializer().serializeToString(clone);
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><foreignObject width="100%" height="100%">${xhtml}</foreignObject></svg>`;
  downloadBlob(new Blob([svg], { type: "image/svg+xml;charset=utf-8" }), filename.replace(/\.png$/i, ".svg"));
}

export function FileBrowserPage({ microservices, onRaw, apiBaseUrl = "/api" }: AnyRecord) {
  const services = filebrowserServices(Array.isArray(microservices) ? microservices : []);
  const rawRequestedTarget = new URLSearchParams(window.location.search).get("target") || "";
  const requestedTarget = rawRequestedTarget === "filebrowser-d518" ? "filebrowser" : rawRequestedTarget;
  const initialId = services.some((service) => service.id === requestedTarget) ? requestedTarget : services[0]?.id || "";
  const [activeId, setActiveId] = useState(initialId);
  const [state, setState] = useState({ loading: false, refreshedAt: null as string | null, health: {} as AnyRecord, error: "" });
  const [screenshot, setScreenshot] = useState({ exporting: false, message: "", error: "" });
  const iframeRef = useRef(null);
  const activeService = services.find((service) => service.id === activeId) || services[0] || null;
  const activeRuntime = microserviceRuntime(activeService);
  const activeBackend = microserviceBackend(activeService);
  const activeRepository = microserviceRepository(activeService);
  const activeHealth = activeService ? state.health[activeService.id] : null;
  const frameSrc = activeService ? browserProxyUrl(apiBaseUrl, activeService.id, "/") : "about:blank";

  useEffect(() => {
    if (services.length === 0) return;
    if (!activeId || !services.some((service) => service.id === activeId)) setActiveId(services[0].id);
  }, [services.map((service) => service.id).join(",")]);

  useEffect(() => {
    let attempts = 0;
    const timer = setInterval(() => {
      attempts += 1;
      const applied = injectFilebrowserCompactStyle(iframeRef.current);
      if (applied || attempts >= 24) clearInterval(timer);
    }, 500);
    return () => clearInterval(timer);
  }, [frameSrc]);

  useEffect(() => {
    if (services.length === 0) return;
    let cancelled = false;
    async function load() {
      setState((previous: AnyRecord) => ({ ...previous, loading: true, error: "" }));
      const entries = await Promise.all(services.map(async (service) => {
        try {
          const body = await requestHealthWithTimeout(healthUrl(apiBaseUrl, service.id));
          return [service.id, { ok: true, body }] as const;
        } catch (error) {
          return [service.id, { ok: false, error: errorMessage(error, "File Browser health failed") }] as const;
        }
      }));
      if (cancelled) return;
      setState({ loading: false, refreshedAt: new Date().toISOString(), health: Object.fromEntries(entries), error: "" });
    }
    load();
    const timer = setInterval(load, 30000);
    return () => {
      cancelled = true;
      clearInterval(timer);
    };
  }, [services.map((service) => `${service.id}:${service.runtime?.providerStatus || ""}`).join(","), apiBaseUrl]);

  function selectTarget(id: string) {
    setActiveId(id);
    const url = new URL(window.location.href);
    url.searchParams.set("target", id);
    window.history.replaceState({}, "", `${url.pathname}${url.search}`);
  }

  async function exportScreenshot() {
    if (screenshot.exporting) return;
    setScreenshot({ exporting: true, message: "", error: "" });
    try {
      const stamp = new Date().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
      await exportIframeScreenshot(iframeRef.current, `unidesk-filebrowser-${activeService?.id || "target"}-${stamp}.png`);
      setScreenshot({ exporting: false, message: "截图已导出", error: "" });
    } catch (error) {
      setScreenshot({ exporting: false, message: "", error: errorMessage(error, "截图导出失败") });
    }
  }

  if (services.length === 0) return h(EmptyState, { title: "File Browser 未登记", text: "请在 config.json 的 microservices 中登记 id=filebrowser 或 filebrowser-* 用户服务" });

  return h("div", { className: "filebrowser-page", "data-testid": "filebrowser-page" },
    state.error ? h(UniDeskErrorBanner, { error: state.error, wide: true }) : null,
    h(Panel, {
      title: "文件管理器",
      eyebrow: "File Browser / Host Files",
      loading: state.loading,
      actions: h("div", { className: "panel-actions" },
        activeService ? h("button", { type: "button", className: "ghost-btn", onClick: exportScreenshot, disabled: screenshot.exporting, "data-testid": "filebrowser-export-screenshot" }, screenshot.exporting ? "导出中..." : "导出截图") : null,
        activeService ? h("a", { className: "ghost-btn", href: frameSrc, target: "_blank", rel: "noreferrer" }, "新窗口打开") : null,
        activeService ? h(RawButton, { title: "File Browser 当前目标", data: { service: activeService, health: activeHealth }, onOpen: onRaw, testId: "raw-filebrowser-active" }) : null,
      ),
    },
      h("div", { className: "filebrowser-hero" },
        h("div", null,
          h("span", { className: `status-badge ${healthOk(activeHealth?.body) ? "ok" : "warn"}` }, healthOk(activeHealth?.body) ? "Health OK" : "Health Pending"),
          h("h3", null, activeService?.name || "File Browser"),
          h("p", { className: "muted paragraph" }, activeService?.description || "通过 UniDesk 登录态代理访问，不开放 File Browser 公网端口。"),
          screenshot.error ? h("p", { className: "filebrowser-shot-error" }, screenshot.error) : null,
          screenshot.message ? h("p", { className: "filebrowser-shot-ok" }, screenshot.message) : null,
        ),
        h("div", { className: "microservice-ref-card" }, h("span", null, "Provider"), h("strong", null, activeService?.providerId || "--"), h("code", null, activeRuntime.providerName || activeService?.providerId || "--")),
        h("div", { className: "microservice-ref-card" }, h("span", null, "Private Backend"), h("strong", null, `${activeBackend.nodeBindHost || "--"}:${activeBackend.nodePort || "--"}`), h("code", null, activeBackend.nodeBaseUrl || "--")),
        h("div", { className: "microservice-ref-card" }, h("span", null, "Image"), h("strong", null, activeRepository.dockerfile || "filebrowser/filebrowser:v2.63.3"), h("code", null, activeRepository.commitId || "--")),
        h("div", { className: "microservice-ref-card" }, h("span", null, "Mount"), h("strong", null, targetMountHint(activeService)), h("code", null, activeService?.providerId === "main-server" ? "/root, /var, /home" : "/home, /mnt/c, /mnt/d")),
      ),
    ),
    h(Panel, { title: "浏览目标", eyebrow: `${services.length} host targets`, loading: state.loading },
      h("div", { className: "filebrowser-target-grid" },
        services.map((service) => h(TargetCard, {
          key: service.id,
          service,
          active: service.id === activeService?.id,
          health: state.health[service.id],
          onSelect: () => selectTarget(service.id),
          onRaw,
        })),
      ),
    ),
    h(Panel, {
      title: `${serviceLabel(activeService)} 文件视图`,
      eyebrow: activeHealth?.body ? `Health ${healthOk(activeHealth.body) ? "OK" : "UNKNOWN"} / ${state.refreshedAt ? fmtClock(state.refreshedAt) : "--"}` : "Embedded WebUI",
      className: "filebrowser-frame-panel",
    },
      h("div", { className: "filebrowser-frame-shell" },
        h("div", { className: "filebrowser-frame-toolbar" },
          h("span", null, "BaseURL"),
          h("code", null, `/api/microservices/${activeService?.id || "filebrowser"}/proxy`),
          h("span", null, "Root"),
          h("code", null, "/srv"),
          h("span", { className: "filebrowser-compact-note" }, "Compact layout injected"),
        ),
        h("iframe", {
          ref: iframeRef,
          key: frameSrc,
          title: `${activeService?.name || "File Browser"} WebUI`,
          src: frameSrc,
          className: "filebrowser-frame",
          "data-testid": "filebrowser-frame",
          onLoad: (event: any) => injectFilebrowserCompactStyle(event.currentTarget),
          sandbox: "allow-downloads allow-forms allow-modals allow-same-origin allow-scripts",
        }),
      ),
    ),
  );
}
