import { useState, useEffect, useRef } from "react";
import { useParams, useNavigate } from "react-router-dom";
import type { GridMap, ListedMap, PublicAccess, Tool } from "../types";
import type { GridHandle } from "../components/Grid";
import { api } from "../api";
import { useAuth } from "../context/AuthContext";
import ControlPanel from "../components/ControlPanel";
import ColorPanel from "../components/ColorPanel";
import Grid from "../components/Grid";
import LoginModal from "../components/LoginModal";
import FloatingMapControls from "../components/FloatingMapControls";
import NewMapModal from "../components/NewMapModal";
import EditMapModal from "../components/EditMapModal";
import MapListModal from "../components/MapListModal";
import "../components/Modal.css";

const DEFAULT_COLORS = [
  "#6b7280",
  "#92400e",
  "#15803d",
  "#1d4ed8",
  "#7c3aed",
  "#dc2626",
  "#ca8a04",
  "#0f172a",
  "#f9fafb",
];

function getQueryParam(key: string): string | null {
  return new URLSearchParams(window.location.search).get(key);
}

function removeQueryParam(key: string) {
  const url = new URL(window.location.href);
  url.searchParams.delete(key);
  window.history.replaceState(
    null,
    "",
    url.pathname + (url.search !== "?" ? url.search : ""),
  );
}

interface Props {
  setMapTitle: (title: string | null) => void;
}

export default function MapPage({ setMapTitle }: Props) {
  const { mapId: urlMapId } = useParams();
  const navigate = useNavigate();
  const { user, authLoading, setUser } = useAuth();

  // ── Map state ──
  const [maps, setMaps] = useState<ListedMap[]>([]);
  const [selectedId, setSelectedId] = useState<string | null>(
    urlMapId ? decodeURIComponent(urlMapId) : null,
  );
  /** Info for maps accessed via URL that aren't in the user's list (e.g. public maps). */
  const [directMapInfo, setDirectMapInfo] = useState<GridMap | null>(null);
  /** True when the current selectedId returned 403 (no access). */
  const [accessDenied, setAccessDenied] = useState(false);
  const [accessRequestSent, setAccessRequestSent] = useState(false);

  // ── Tool + color ──
  const [tool, setTool] = useState<Tool>("pan");
  const [activeColor, setActiveColor] = useState(DEFAULT_COLORS[0]);
  const [mapColors, setMapColors] = useState<string[]>(DEFAULT_COLORS);
  const gridRef = useRef<GridHandle>(null);

  // ── Modal visibility ──
  const [showLoginModal, setShowLoginModal] = useState(false);
  const [showNewMap, setShowNewMap] = useState(false);
  const [showEditMap, setShowEditMap] = useState(false);
  const [showMapList, setShowMapList] = useState(false);

  // ── Derived ──
  const selectedMapFromList = maps.find((m) => m.id === selectedId) ?? null;
  const selectedMapInfo: GridMap | ListedMap | null =
    selectedMapFromList ?? directMapInfo;
  const isOwner =
    user !== null &&
    selectedMapInfo !== null &&
    selectedMapInfo.owner_id === user.id;

  // ── On mount: handle OAuth errors ──
  useEffect(() => {
    const error = getQueryParam("error");
    if (error) {
      console.error("OAuth error:", error);
      removeQueryParam("error");
    }
  }, []);

  // ── Load map list after auth resolves ──
  useEffect(() => {
    if (!authLoading) {
      api.listMaps().then(setMaps).catch(console.error);
    }
  }, [user, authLoading]);

  // ── Direct fetch for URL-accessed maps not in the user's list ──
  useEffect(() => {
    if (!selectedId || authLoading) {
      setDirectMapInfo(null);
      setAccessDenied(false);
      return;
    }
    const inList = maps.some((m) => m.id === selectedId);
    if (inList) {
      setDirectMapInfo(null);
      setAccessDenied(false);
      return;
    }

    setDirectMapInfo(null);
    setAccessDenied(false);
    setAccessRequestSent(false);

    api
      .getMap(selectedId)
      .then((state) => {
        setDirectMapInfo(state.map);
      })
      .catch((err) => {
        const msg = err instanceof Error ? err.message : String(err);
        if (msg.startsWith("403")) {
          setAccessDenied(true);
        } else {
          // 404 or unknown — clear invalid URL
          setSelectedId(null);
          navigate("/map", { replace: true });
        }
      });
  }, [selectedId, maps, authLoading]); // eslint-disable-line react-hooks/exhaustive-deps

  // ── Keep URL in sync ──
  useEffect(() => {
    const path = selectedId ? `/map/${encodeURIComponent(selectedId)}` : "/map";
    navigate(path, { replace: true });
  }, [selectedId]); // eslint-disable-line react-hooks/exhaustive-deps

  // ── Sync map title to header ──
  useEffect(() => {
    setMapTitle(selectedMapInfo?.name ?? null);
  }, [selectedMapInfo?.name]); // eslint-disable-line react-hooks/exhaustive-deps

  // ── Clear map title on unmount ──
  useEffect(() => {
    return () => setMapTitle(null);
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  // ── Reset palette + access state when map deselected ──
  useEffect(() => {
    if (!selectedId) {
      setMapColors(DEFAULT_COLORS);
      setActiveColor(DEFAULT_COLORS[0]);
      setAccessRequestSent(false);
    }
  }, [selectedId]);

  // ── Handlers ──

  async function handleCreate(name: string, publicAccess: PublicAccess) {
    const m = await api.createMap(name, publicAccess);
    const listed: ListedMap = {
      ...m,
      owner_username: user!.username,
      user_role: "owner",
      is_favorited: false,
    };
    setMaps((prev) => [listed, ...prev]);
    setSelectedId(m.id);
  }

  async function handleDelete() {
    if (!selectedId) return;
    if (!confirm("Delete this map? This cannot be undone.")) return;
    try {
      await api.deleteMap(selectedId);
      setMaps((prev) => prev.filter((m) => m.id !== selectedId));
      setSelectedId(null);
    } catch (err) {
      console.error("Failed to delete map", err);
    }
  }

  function handleMapUpdated(updated: GridMap) {
    setMaps((prev) =>
      prev.map((m) =>
        m.id === updated.id
          ? {
              ...m,
              name: updated.name,
              public_access: updated.public_access,
              updated_at: updated.updated_at,
            }
          : m,
      ),
    );
    if (directMapInfo?.id === updated.id) {
      setDirectMapInfo(updated);
    }
  }

  function handleColorsLoaded(colors: string[]) {
    setMapColors(colors);
    setActiveColor((prev) => (colors.includes(prev) ? prev : colors[0]));
  }

  function handleColorsChange(colors: string[]) {
    setMapColors(colors);
    gridRef.current?.sendColorUpdate(colors);
  }

  async function handleRequestAccess(role: "viewer" | "editor") {
    if (!selectedId) return;
    try {
      await api.requestAccess(selectedId, role);
      setAccessRequestSent(true);
    } catch (err) {
      console.error("Failed to request access", err);
    }
  }

  // ── Render ──
  return (
    <div className="app-grid-area">
      {/* Top-left floating map controls */}
      <FloatingMapControls
        isLoggedIn={!!user}
        hasSelectedMap={!!selectedId}
        isOwner={isOwner}
        onNewMap={() => setShowNewMap(true)}
        onViewMaps={() => setShowMapList(true)}
        onEditMap={() => setShowEditMap(true)}
        onDeleteMap={handleDelete}
      />

      {selectedId && !accessDenied ? (
        <>
          <Grid
            key={selectedId}
            ref={gridRef}
            mapId={selectedId}
            tool={tool}
            paintColor={activeColor}
            tokenColor={activeColor}
            onColorsLoaded={handleColorsLoaded}
          />
          <div className="floating-panels-container">
            <ControlPanel tool={tool} onToolChange={setTool} />
            <ColorPanel
              colors={mapColors}
              activeColor={activeColor}
              onColorChange={setActiveColor}
              onColorsChange={handleColorsChange}
            />
          </div>
        </>
      ) : accessDenied ? (
        <div className="access-denied-state">
          <p className="access-denied-title">
            You don't have access to this map
          </p>
          {!user ? (
            <p className="access-denied-hint">
              <button
                className="link-btn"
                onClick={() => setShowLoginModal(true)}
              >
                Log in
              </button>{" "}
              to request access or view your permissions.
            </p>
          ) : accessRequestSent ? (
            <p className="access-denied-hint access-request-sent">
              ✓ Access request sent! The map owner will be notified.
            </p>
          ) : (
            <div className="access-request-actions">
              <p className="access-denied-hint">
                Request access from the map owner:
              </p>
              <div className="access-request-btns">
                <button
                  className="btn-request-access"
                  onClick={() => handleRequestAccess("viewer")}
                >
                  Request Viewer Access
                </button>
                <button
                  className="btn-request-access"
                  onClick={() => handleRequestAccess("editor")}
                >
                  Request Editor Access
                </button>
              </div>
            </div>
          )}
        </div>
      ) : (
        <div className="empty-state">
          <p>Select or create a map to begin</p>
          <p className="empty-hint">
            {!user
              ? "Log in to create maps and access private maps"
              : maps.length === 0
                ? 'Click "+ New Map" in the top-left to get started'
                : 'Click "Maps" in the top-left to choose a map'}
          </p>
        </div>
      )}

      {/* ── Modals ── */}
      {showLoginModal && (
        <LoginModal
          onClose={() => setShowLoginModal(false)}
          onLogin={(u) => {
            setUser(u);
            setShowLoginModal(false);
            api.listMaps().then(setMaps).catch(console.error);
          }}
        />
      )}

      {showNewMap && (
        <NewMapModal
          onClose={() => setShowNewMap(false)}
          onCreate={handleCreate}
        />
      )}

      {showEditMap && selectedMapInfo && (
        <EditMapModal
          map={selectedMapInfo}
          onClose={() => setShowEditMap(false)}
          onUpdated={handleMapUpdated}
        />
      )}

      {showMapList && (
        <MapListModal
          maps={maps}
          selectedMapId={selectedId}
          onSelect={(id) => setSelectedId(id)}
          onClose={() => setShowMapList(false)}
          onMapsChange={setMaps}
        />
      )}
    </div>
  );
}
