import { useEffect, useMemo, useState } from "react";
import { AnimatePresence } from "framer-motion";
import { Plus, Play, RefreshCcw, Search, Server, ShieldCheck, Trash2 } from "lucide-react";
import Header from "./components/Header";
import StatsBar from "./components/StatsBar";
import ServerCard from "./components/ServerCard";
import ServerDetail from "./components/ServerDetail";
import BulkStatusPage from "./components/BulkStatusPage";
import ServerFormModal from "./components/modals/ServerFormModal";
import ServiceFormModal from "./components/modals/ServiceFormModal";
import ConfirmDialog from "./components/ui/ConfirmDialog";
import EmptyState from "./components/ui/EmptyState";
import Skeleton from "./components/ui/Skeleton";
import Button from "./components/ui/Button";
import { ToastProvider, useToast } from "./components/toast/ToastProvider";
import useServers from "./hooks/useServers";
import { API_BASE } from "./lib/api";

const THEME_KEY = "mtc-theme";
const VIEW_KEY = "mtc-service-view";

function AppShell() {
  const { notify } = useToast();
  const data = useServers();
  const { servers, loading, loadError, refresh } = data;

  const [query, setQuery] = useState("");
  const [sort, setSort] = useState("name");
  const [theme, setTheme] = useState(() => localStorage.getItem(THEME_KEY) || "dark");
  const [proxyCalls, setProxyCalls] = useState(0);

  const [pageServerId, setPageServerId] = useState(null);
  const [bulkRun, setBulkRun] = useState(null);
  const [proxyBusyId, setProxyBusyId] = useState(null);
  const [serviceView, setServiceView] = useState(() => localStorage.getItem(VIEW_KEY) || "grid");
  const [serverModal, setServerModal] = useState(null);
  const [serviceModal, setServiceModal] = useState(null);
  const [confirm, setConfirm] = useState(null);

  const activeServer = pageServerId ? servers.find((server) => server.id === pageServerId) || null : null;

  useEffect(() => {
    document.documentElement.classList.toggle("light", theme === "light");
    localStorage.setItem(THEME_KEY, theme);
  }, [theme]);

  useEffect(() => {
    localStorage.setItem(VIEW_KEY, serviceView);
  }, [serviceView]);

  useEffect(() => {
    if (activeServer) window.scrollTo({ top: 0 });
  }, [activeServer]);

  useEffect(() => {
    if (pageServerId && !activeServer && !loading) setPageServerId(null);
  }, [pageServerId, activeServer, loading]);

  const filtered = useMemo(() => {
    const needle = query.trim().toLowerCase();
    let list = servers;
    if (needle) {
      list = servers
        .map((server) => {
          const services = server.services.filter(
            (service) =>
              service.name.toLowerCase().includes(needle) ||
              service.endpoint.toLowerCase().includes(needle) ||
              String(service.port).includes(needle),
          );
          const matches =
            services.length > 0 ||
            server.name.toLowerCase().includes(needle) ||
            server.ipAddress.toLowerCase().includes(needle) ||
            (server.description || "").toLowerCase().includes(needle);
          return matches ? { ...server, services } : null;
        })
        .filter(Boolean);
    }
    return [...list].sort((a, b) =>
      sort === "count" ? b.services.length - a.services.length : a.name.localeCompare(b.name),
    );
  }, [servers, query, sort]);

  const serviceMatches = useMemo(() => {
    const needle = query.trim().toLowerCase();
    if (!needle) return [];
    const matches = [];
    for (const server of servers) {
      for (const service of server.services) {
        if (
          service.name.toLowerCase().includes(needle) ||
          service.endpoint.toLowerCase().includes(needle) ||
          String(service.port).includes(needle) ||
          (service.notes || "").toLowerCase().includes(needle)
        ) {
          matches.push({ service, server });
        }
      }
    }
    return matches.sort(
      (a, b) => a.service.name.localeCompare(b.service.name) || a.service.port - b.service.port,
    );
  }, [servers, query]);

  async function handleSaveServer(form) {
    if (serverModal?.initial) {
      const saved = await data.updateServer(serverModal.initial.id, form);
      notify("success", "Server updated", `${saved.name} (${saved.ipAddress})`);
    } else {
      const saved = await data.createServer(form);
      notify("success", "Server added", `${saved.name} (${saved.ipAddress})`);
    }
    setServerModal(null);
  }

  async function handleSaveService(form) {
    if (serviceModal?.service) {
      const saved = await data.updateService(serviceModal.service.id, form);
      notify("success", "Service updated", `${saved.name} ${saved.endpoint}`);
    } else {
      const saved = await data.createService(form);
      notify("success", "Service added", `${saved.name} ${saved.endpoint}`);
    }
    setServiceModal(null);
  }

  function requestDeleteServer(server) {
    setConfirm({
      title: "Delete Server",
      description: `"${server.name}" (${server.ipAddress}) and all of its ${server.services.length} service${server.services.length === 1 ? "" : "s"} will be permanently removed.`,
      confirmLabel: "Delete Server",
      action: async () => {
        await data.deleteServer(server.id);
        notify("success", "Server deleted", `${server.name} was removed.`);
      },
    });
  }

  function requestDeleteService(service) {
    setConfirm({
      title: "Delete Service",
      description: `"${service.name}" (${service.method} ${service.endpoint}) will be permanently removed.`,
      confirmLabel: "Delete Service",
      action: async () => {
        await data.deleteService(service.id);
        notify("success", "Service deleted", `${service.name} was removed.`);
      },
    });
  }

  async function handleApplyImport(payload, mode) {
    const result = await data.applyImport(payload, mode);
    await refresh();
    notify(
      "success",
      mode === "overwrite" ? "Import applied with overwrite" : "Import applied",
      `${result.applied.servicesCreated} services created, ${result.applied.duplicatesSkipped} duplicates skipped.`,
    );
  }

  async function handleProxy(service) {
    try {
      const result = await data.proxyRequest(service.id, undefined);
      setProxyCalls((count) => count + (result.ok ? 1 : 0));
      if (result.ok) {
        notify("success", "Proxy OK", `${service.name}:${service.port} → ${result.status}`);
      } else {
        notify("error", "Proxy failed", `${service.name}:${service.port} returned ${result.status}.`);
      }
    } catch (err) {
      notify("error", "Proxy failed", `${service.name}:${service.port} — ${err.message}`);
    }
  }

  async function handleBulkProxy(services) {
    const tasks = services.map((service) => ({
      id: service.id,
      service,
      status: "pending",
      statusCode: null,
      detail: "",
    }));
    setBulkRun({ serverId: activeServer?.id, tasks });

    const publish = () => setBulkRun((current) => ({ ...current, tasks: [...tasks] }));

    const outcomes = await Promise.all(
      tasks.map(async (task) => {
        task.status = "running";
        publish();
        try {
          const result = await data.proxyRequest(task.service.id, undefined);
          if (result.ok) {
            task.status = "success";
            task.statusCode = result.status;
            task.detail = result.target || "";
            return true;
          }
          task.status = "error";
          task.statusCode = result.status;
          task.detail = `HTTP ${result.status}`;
          return false;
        } catch (err) {
          task.status = "error";
          task.detail = err.message;
          return false;
        } finally {
          publish();
        }
      }),
    );

    const okCount = outcomes.filter(Boolean).length;
    setProxyCalls((count) => count + okCount);

    const failedCount = outcomes.length - okCount;
    notify("success", "Bulk proxy complete", `${okCount} of ${outcomes.length} succeeded.`);
    if (failedCount > 0) {
      notify(
        "error",
        `${failedCount} failed`,
        tasks
          .filter((task) => task.status === "error")
          .map((task) => `${task.service.name}:${task.service.port} — ${task.detail}`)
          .join(", "),
      );
    }
  }

  async function handleBulkDelete(services) {
    const results = await Promise.allSettled(services.map((service) => data.deleteService(service.id)));
    const failed = results
      .map((outcome, index) => (outcome.status === "rejected" ? services[index].name : null))
      .filter(Boolean);
    const okCount = services.length - failed.length;
    await refresh();

    notify("success", "Bulk delete complete", `${okCount} of ${services.length} services deleted.`);
    if (failed.length > 0) {
      notify("error", `${failed.length} failed`, failed.join(", "));
    }
  }

  async function fireProxy(service) {
    setProxyBusyId(service.id);
    try {
      await handleProxy(service);
    } finally {
      setProxyBusyId(null);
    }
  }

  async function handleRefresh() {
    try {
      await refresh();
      notify("info", "Data refreshed", `${servers.length} servers loaded.`);
    } catch (err) {
      notify("error", "Refresh failed", err.message);
    }
  }

  const showSkeletons = loading && servers.length === 0;

  return (
    <div className="min-h-screen">
      <Header
        query={query}
        onQueryChange={setQuery}
        theme={theme}
        onToggleTheme={() => setTheme((current) => (current === "dark" ? "light" : "dark"))}
        onAddServer={() => setServerModal({ initial: null })}
        onAddService={() => setServiceModal({ server: null, service: null })}
        onRefresh={handleRefresh}
        loading={loading}
        searchEnabled={!activeServer}
      />

      {activeServer ? (
        <main className="mx-auto grid max-w-7xl gap-6 px-4 py-6 sm:px-6 lg:px-8">
          {bulkRun ? (
            <BulkStatusPage server={activeServer} run={bulkRun} onBack={() => setBulkRun(null)} />
          ) : (
            <ServerDetail
              server={activeServer}
              viewMode={serviceView}
              onToggleViewMode={setServiceView}
              onBack={() => setPageServerId(null)}
              onEditServer={() => setServerModal({ initial: activeServer })}
              onDeleteServer={() => requestDeleteServer(activeServer)}
              onEditService={(service) => setServiceModal({ server: activeServer, service })}
              onDeleteService={(service) => requestDeleteService(service)}
              onProxy={handleProxy}
              onBulkProxy={handleBulkProxy}
              onBulkDelete={handleBulkDelete}
              onImportPreview={(payload) => data.previewImport(payload)}
              onImportApply={handleApplyImport}
            />
          )}
        </main>
      ) : (
        <main className="mx-auto grid max-w-7xl gap-6 px-4 py-6 sm:px-6 lg:px-8">
        <StatsBar servers={servers} proxyCalls={proxyCalls} />

        {serviceMatches.length > 0 && (
          <section className="grid content-start gap-3">
            <div className="flex items-center gap-2.5">
              <h2 className="text-lg font-bold text-ink">Matching Services</h2>
              <span className="rounded-lg bg-surface-2 px-2 py-0.5 text-xs font-semibold text-ink-soft">
                {serviceMatches.length}
              </span>
            </div>
            <div className="card overflow-hidden p-2">
              <div className="grid gap-1">
                {serviceMatches.map(({ service, server }) => (
                  <div
                    key={service.id}
                    className="flex items-center gap-3 rounded-lg px-3 py-2 transition hover:bg-surface-2/60"
                  >
                    <span className="mono flex h-8 w-14 shrink-0 items-center justify-center rounded-lg bg-surface-3/80 text-xs font-semibold text-ink-soft">
                      :{service.port}
                    </span>
                    <button
                      type="button"
                      onClick={() => setPageServerId(server.id)}
                      className="min-w-0 flex-1 truncate text-left text-sm font-semibold text-ink transition hover:text-accent"
                      title="Open server"
                    >
                      {service.name}
                    </button>
                    <button
                      type="button"
                      onClick={() => setPageServerId(server.id)}
                      className="hidden max-w-44 truncate text-[11px] text-ink-muted transition hover:text-ink sm:block"
                      title="Open server"
                    >
                      {server.name}
                    </button>
                    <Button
                      variant="primary"
                      size="sm"
                      icon={Play}
                      loading={proxyBusyId === service.id}
                      disabled={proxyBusyId !== null && proxyBusyId !== service.id}
                      onClick={() => fireProxy(service)}
                    >
                      Proxy
                    </Button>
                    <button
                      aria-label={`Delete ${service.name}`}
                      className="rounded-lg p-2 text-ink-muted transition hover:bg-rose-500/10 hover:text-rose-400"
                      onClick={() => requestDeleteService(service)}
                      type="button"
                    >
                      <Trash2 size={14} />
                    </button>
                  </div>
                ))}
              </div>
            </div>
          </section>
        )}

        {!showSkeletons && (
          <section className="flex flex-wrap items-center justify-between gap-3">
            <div className="flex items-center gap-2.5">
              <h2 className="text-lg font-bold text-ink">Servers</h2>
              <span className="rounded-lg bg-surface-2 px-2 py-0.5 text-xs font-semibold text-ink-soft">
                {filtered.length}
              </span>
              {query.trim() && (
                <button
                  className="flex items-center gap-1 rounded-lg border border-line bg-surface-2 px-2.5 py-1 text-xs font-medium text-ink-soft transition hover:text-ink"
                  onClick={() => setQuery("")}
                  type="button"
                >
                  <Search size={12} /> “{query.trim()}” <span className="text-ink-muted">clear</span>
                </button>
              )}
            </div>
            <label className="flex items-center gap-2 text-xs font-medium text-ink-muted">
              Sort
              <select
                className="input !w-auto !py-1.5 text-xs"
                value={sort}
                onChange={(event) => setSort(event.target.value)}
              >
                <option value="name">Name</option>
                <option value="count">Most services</option>
              </select>
            </label>
          </section>
        )}

        <AnimatePresence mode="popLayout">
          {showSkeletons && (
            <div className="grid gap-4" key="skeletons">
              <Skeleton className="h-36 w-full" />
              <Skeleton className="h-36 w-full" />
              <Skeleton className="h-36 w-full" />
            </div>
          )}

          {!showSkeletons && loadError && (
            <EmptyState
              key="error"
              icon={ShieldCheck}
              title="Can't reach the API"
              description={`${loadError} — make sure the backend is running and CORS allows this origin.`}
            >
              <Button variant="primary" icon={RefreshCcw} onClick={handleRefresh}>
                Try again
              </Button>
            </EmptyState>
          )}

          {!showSkeletons && !loadError && servers.length === 0 && (
            <EmptyState
              key="empty"
              icon={Server}
              title="No servers registered"
              description="Add your first internal server (e.g. the gateway machine), then use Bulk Import inside it to load your service catalog."
            >
              <Button variant="primary" icon={Plus} onClick={() => setServerModal({ initial: null })}>
                Add Server
              </Button>
            </EmptyState>
          )}

          {!showSkeletons && !loadError && servers.length > 0 && filtered.length === 0 && (
            <EmptyState
              key="no-match"
              icon={Search}
              title="No matches"
              description={`Nothing matches “${query.trim()}”. Try a different search term.`}
            >
              <Button variant="secondary" onClick={() => setQuery("")}>
                Clear search
              </Button>
            </EmptyState>
          )}

          {!showSkeletons && filtered.length > 0 && (
            <div className="grid gap-4" key="list">
              {filtered.map((server, index) => (
                <ServerCard
                  key={server.id}
                  server={server}
                  index={index}
                  onOpenServer={(item) => setPageServerId(item.id)}
                  onEditServer={(item) => setServerModal({ initial: item })}
                  onDeleteServer={requestDeleteServer}
                />
              ))}
            </div>
          )}
        </AnimatePresence>

        <footer className="flex flex-wrap items-center justify-between gap-2 border-t border-line pb-4 pt-6 text-[11px] text-ink-muted">
          <p>MTC Reverse Proxy Manager</p>
          <p className="mono">API {API_BASE}</p>
        </footer>
        </main>
      )}

      <ServerFormModal
        open={Boolean(serverModal)}
        onClose={() => setServerModal(null)}
        initial={serverModal?.initial}
        onSave={handleSaveServer}
      />
      <ServiceFormModal
        open={Boolean(serviceModal)}
        onClose={() => setServiceModal(null)}
        initial={serviceModal?.service}
        servers={servers}
        onSave={handleSaveService}
      />
      <ConfirmDialog
        open={Boolean(confirm)}
        onClose={() => setConfirm(null)}
        title={confirm?.title}
        description={confirm?.description}
        confirmLabel={confirm?.confirmLabel}
        onConfirm={async () => {
          try {
            await confirm.action();
            setConfirm(null);
          } catch (err) {
            notify("error", "Action failed", err.message);
            setConfirm(null);
          }
        }}
      />
    </div>
  );
}

export default function App() {
  return (
    <ToastProvider>
      <AppShell />
    </ToastProvider>
  );
}
