import { useCallback, useEffect, useMemo, useState } from "react";
import { API_BASE } from "./config";

const FIT = {
  gpu: { label: "🟢 fits GPU", color: "#8aff8a" },
  ram: { label: "🟡 runs on RAM (CPU)", color: "#e8c65a" },
  no: { label: "🔴 too big", color: "#ff8a80" },
};

const TABS = [
  { key: "required", label: "Required" },
  { key: "recommended", label: "Recommended" },
  { key: "installed", label: "Installed" },
];

const GRID_STYLE = {
  display: "grid",
  gridTemplateColumns: "repeat(auto-fill, minmax(260px, 1fr))",
  gap: "0.75rem",
  alignItems: "stretch",
};

const withTag = (n) => (n.includes(":") ? n : `${n}:latest`).toLowerCase();

function ModelCard({ model, installed, pulling, pullingName, locked, onPull }) {
  const fit = FIT[model.fit] || FIT.no;
  const isPullingThis = pulling && pullingName === model.name;
  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        gap: "0.5rem",
        padding: "0.85rem",
        background: locked ? "#131313" : "#0f0f0f",
        border: `1px solid ${model.required ? "#3a5a3a" : "#262626"}`,
        borderRadius: "10px",
        opacity: locked ? 0.45 : 1,
        transition: "opacity 0.15s ease",
      }}
    >
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: "0.5rem" }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ color: "#eee", fontSize: "0.9rem", fontWeight: 500, wordBreak: "break-word" }}>
            {model.name}
          </div>
          <div style={{ color: "#666", fontSize: "0.75rem" }}>
            {model.params} · {model.size_gb} GB · {model.role}
          </div>
        </div>
        {model.required && (
          <span style={{
            fontSize: "0.65rem", color: "#8aff8a", border: "1px solid #3a5a3a",
            borderRadius: "999px", padding: "0.1rem 0.5rem", whiteSpace: "nowrap",
          }}>
            required
          </span>
        )}
      </div>
      <div style={{ color: "#888", fontSize: "0.75rem", flexGrow: 1 }}>{model.note}</div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: "0.5rem" }}>
        <span style={{ color: fit.color, fontSize: "0.72rem", whiteSpace: "nowrap" }}>{fit.label}</span>
        {installed ? (
          <span style={{ color: "#8aff8a", fontSize: "0.75rem", whiteSpace: "nowrap" }}>✓ installed</span>
        ) : (
          <button
            onClick={() => onPull(model.name)}
            disabled={pulling || locked}
            title={locked ? "Install the required models first" : undefined}
            style={{
              padding: "0.3rem 0.7rem",
              background: pulling || locked ? "#555" : "#28a745",
              color: "#fff",
              border: "none",
              borderRadius: "6px",
              cursor: pulling || locked ? "not-allowed" : "pointer",
              fontSize: "0.75rem",
              whiteSpace: "nowrap",
            }}
          >
            {isPullingThis ? "Pulling..." : "Pull"}
          </button>
        )}
      </div>
    </div>
  );
}

export function Models({ onPullStateChange }) {
  const [models, setModels] = useState([]); // installed, chat-facing (embed models hidden)
  const [allInstalled, setAllInstalled] = useState([]); // installed, unfiltered (includes embed)
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  const [pulling, setPulling] = useState(false);
  const [pullingName, setPullingName] = useState("");
  const [modelName, setModelName] = useState("");
  const [pullProgress, setPullProgress] = useState("");
  const [recommended, setRecommended] = useState(null); // {hardware, models}
  const [tab, setTab] = useState("required");
  const [manualOpen, setManualOpen] = useState(false);

  const checkOllamaStatus = useCallback(async () => {
    try {
      const response = await fetch(`${API_BASE}/ollama/status`);
      const data = await response.json();
      return data.status === "running";
    } catch {
      return false;
    }
  }, []);

  const loadModels = useCallback(async () => {
    setLoading(true);
    setError("");
    try {
      const isRunning = await checkOllamaStatus();
      if (!isRunning) {
        setError("Ollama service is not running");
        setModels([]);
        setAllInstalled([]);
        return;
      }
      const response = await fetch(`${API_BASE}/models/details`);
      if (!response.ok) throw new Error("Failed to fetch models");
      const data = await response.json();
      const all = data.models || [];
      setAllInstalled(all);
      // Embedding models (nomic-embed-text) are infrastructure — hide from the
      // Installed list, but they still count via allInstalled for the Required tab.
      setModels(all.filter((m) => !m.name.toLowerCase().includes("embed")));
    } catch (err) {
      setError(`Error loading models: ${err.message}`);
      setModels([]);
      setAllInstalled([]);
    } finally {
      setLoading(false);
    }
  }, [checkOllamaStatus]);

  const pullModel = async (nameArg) => {
    const name = (typeof nameArg === "string" ? nameArg : modelName).trim();
    if (!name) {
      setError("Please enter a model name");
      return;
    }

    setPulling(true);
    setPullingName(name);
    onPullStateChange?.(true);
    setError("");
    setPullProgress(`Pulling ${name}…`);

    try {
      const response = await fetch(`${API_BASE}/models/pull`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name }),
      });

      if (!response.ok) throw new Error("Failed to pull model");

      const reader = response.body.getReader();
      const decoder = new TextDecoder();

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        const chunk = decoder.decode(value);
        const lines = chunk.split("\n");

        for (const line of lines) {
          if (line.trim()) {
            try {
              const json = JSON.parse(line);
              if (json.error) {
                setError(`Pull error: ${json.error}`);
              } else if (json.status) {
                setPullProgress(json.completed && json.total
                  ? `${json.status} — ${Math.round((json.completed / json.total) * 100)}%`
                  : json.status
                );
              }
            } catch {
              // ignore incomplete JSON lines
            }
          }
        }
      }

      setModelName("");
      setPullProgress("");
      await loadModels();
    } catch (err) {
      setError(`Error pulling model: ${err.message}`);
    } finally {
      setPulling(false);
      setPullingName("");
      onPullStateChange?.(false);
    }
  };

  const deleteModel = async (modelId) => {
    if (!confirm(`Are you sure you want to delete ${modelId}?`)) return;

    setError("");
    try {
      const response = await fetch(`${API_BASE}/models/${encodeURIComponent(modelId)}`, {
        method: "DELETE",
      });
      if (!response.ok) {
        const text = await response.text().catch(() => "");
        throw new Error(text || response.statusText);
      }
      await loadModels();
    } catch (err) {
      setError(`Error deleting model: ${err.message}`);
    }
  };

  useEffect(() => {
    loadModels();
    fetch(`${API_BASE}/models/recommended`).then(r => r.ok ? r.json() : null).then(setRecommended).catch(() => {});
    const interval = setInterval(loadModels, 30000);

    return () => clearInterval(interval);
  }, [loadModels]);

  // Ollama treats a bare name as ":latest" — you pull "nomic-embed-text" and it
  // comes back installed as "nomic-embed-text:latest". Comparing raw names left
  // any untagged catalog entry permanently "missing", which locked the Required
  // gate shut no matter how many times it was pulled.
  const installedNames = useMemo(
    () => new Set(allInstalled.map(m => withTag(m.name))),
    [allInstalled]
  );

  const requiredModels = useMemo(
    () => (recommended?.models ?? []).filter(m => m.required),
    [recommended]
  );
  const otherModels = useMemo(
    () => (recommended?.models ?? []).filter(m => !m.required),
    [recommended]
  );
  const requiredMissing = requiredModels.filter(m => !installedNames.has(withTag(m.name)));
  const requiredSatisfied = recommended != null && requiredMissing.length === 0;

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100%", flexGrow: 1 }}>
      {error && (
        <div style={{
          padding: "0.75rem 1rem",
          background: "#3d2d2d",
          border: "1px solid #ff6b6b",
          borderRadius: "8px",
          color: "#ff8888",
          marginBottom: "1rem",
          fontSize: "0.9rem"
        }}>
          {error}
        </div>
      )}

      <div style={{
        flexGrow: 1,
        background: "#161616",
        border: "1px solid #333",
        borderRadius: "12px",
        padding: "1rem",
        display: "flex",
        flexDirection: "column",
        minHeight: 0,
      }}>
        {/* Header: hardware summary + tabs */}
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: "0.5rem", marginBottom: "0.85rem" }}>
          <div style={{ display: "flex", gap: "0.35rem" }}>
            {TABS.map(t => {
              const active = tab === t.key;
              const badge = t.key === "required" && recommended
                ? (requiredSatisfied ? "✓" : requiredMissing.length)
                : t.key === "installed" ? models.length : null;
              return (
                <button
                  key={t.key}
                  onClick={() => setTab(t.key)}
                  style={{
                    padding: "0.45rem 0.9rem",
                    background: active ? "#28a745" : "transparent",
                    color: active ? "#fff" : "#aaa",
                    border: `1px solid ${active ? "#28a745" : "#333"}`,
                    borderRadius: "999px",
                    cursor: "pointer",
                    fontSize: "0.82rem",
                    display: "flex",
                    alignItems: "center",
                    gap: "0.4rem",
                  }}
                >
                  {t.label}
                  {badge !== null && badge !== undefined && (
                    <span style={{
                      fontSize: "0.68rem",
                      color: active ? "#fff" : (t.key === "required" && !requiredSatisfied ? "#ff8a80" : "#888"),
                    }}>
                      {badge}
                    </span>
                  )}
                </button>
              );
            })}
          </div>
          {recommended && (
            <div style={{ fontSize: "0.78rem", color: "#777" }}>
              {recommended.hardware.ram_gb ? `${recommended.hardware.ram_gb} GB RAM` : "RAM unknown"}
              {" · "}
              {recommended.hardware.vram_gb ? `${recommended.hardware.vram_gb} GB GPU` : "GPU VRAM unknown — sized by RAM"}
            </div>
          )}
          <button
            onClick={loadModels}
            disabled={loading}
            style={{
              padding: "0.45rem 0.9rem",
              background: "#007acc",
              color: "#fff",
              border: "none",
              borderRadius: "6px",
              cursor: loading ? "not-allowed" : "pointer",
              opacity: loading ? 0.6 : 1,
              fontSize: "0.82rem",
            }}
          >
            {loading ? "Refreshing..." : "Refresh"}
          </button>
        </div>

        {pullProgress && (
          <div style={{ marginBottom: "0.85rem", fontSize: "0.85rem", color: "#999" }}>
            {pullProgress}
          </div>
        )}

        {/* Scrollable tab content */}
        <div style={{ flexGrow: 1, overflowY: "auto", overflowX: "hidden", paddingRight: "0.25rem" }}>
          {!recommended ? (
            <div style={{ padding: "2rem", textAlign: "center", color: "#666" }}>Loading catalog...</div>
          ) : tab === "required" ? (
            <>
              <div style={{ marginBottom: "0.85rem", fontSize: "0.85rem", color: requiredSatisfied ? "#8aff8a" : "#e8c65a" }}>
                {requiredSatisfied
                  ? "All required models are installed. Everything else is unlocked on the Recommended tab."
                  : "NexusOS needs both of these to fully work (memory + recall). Install them before pulling anything else."}
              </div>
              <div style={GRID_STYLE}>
                {requiredModels.map(m => (
                  <ModelCard
                    key={m.name}
                    model={m}
                    installed={installedNames.has(withTag(m.name))}
                    pulling={pulling}
                    pullingName={pullingName}
                    locked={false}
                    onPull={pullModel}
                  />
                ))}
              </div>
            </>
          ) : tab === "recommended" ? (
            <>
              {!requiredSatisfied && (
                <div style={{ marginBottom: "0.85rem", fontSize: "0.85rem", color: "#e8c65a" }}>
                  Dimmed until the Required tab is complete — install those first.
                </div>
              )}
              <div style={GRID_STYLE}>
                {otherModels.map(m => (
                  <ModelCard
                    key={m.name}
                    model={m}
                    installed={installedNames.has(withTag(m.name))}
                    pulling={pulling}
                    pullingName={pullingName}
                    locked={!requiredSatisfied}
                    onPull={pullModel}
                  />
                ))}
              </div>
            </>
          ) : (
            <>
              {models.length === 0 ? (
                <div style={{ padding: "2rem", textAlign: "center", color: "#666" }}>
                  {loading ? "Loading models..." : "No models installed yet. Start with the Required tab."}
                </div>
              ) : (
                <div style={GRID_STYLE}>
                  {models.map((model) => (
                    <div
                      key={model.name}
                      style={{
                        display: "flex",
                        flexDirection: "column",
                        gap: "0.5rem",
                        padding: "0.85rem",
                        background: "#1b1b1b",
                        borderRadius: "10px",
                        border: "1px solid #2a2a2a",
                      }}
                    >
                      <div style={{ fontWeight: 500, color: "#ddd", wordBreak: "break-word" }}>{model.name}</div>
                      <div style={{ fontSize: "0.8rem", color: "#aaa" }}>
                        {(model.size / (1024 * 1024 * 1024)).toFixed(2)} GB
                      </div>
                      {model.modified_at && (
                        <div style={{ fontSize: "0.78rem", color: "#888" }}>
                          Modified: {new Date(model.modified_at).toLocaleDateString()}
                        </div>
                      )}
                      <button
                        onClick={() => deleteModel(model.name)}
                        style={{
                          alignSelf: "flex-start",
                          padding: "0.4rem 0.9rem",
                          background: "#d63031",
                          color: "#fff",
                          border: "none",
                          borderRadius: "6px",
                          cursor: "pointer",
                          fontSize: "0.8rem",
                        }}
                      >
                        Delete
                      </button>
                    </div>
                  ))}
                </div>
              )}
            </>
          )}
        </div>

        {/* Manual pull — collapsed by default */}
        <div style={{ marginTop: "0.85rem", paddingTop: "0.75rem", borderTop: "1px solid #333" }}>
          {!manualOpen ? (
            <button
              onClick={() => setManualOpen(true)}
              style={{
                background: "none",
                border: "none",
                color: "#777",
                fontSize: "0.82rem",
                cursor: "pointer",
                padding: 0,
              }}
            >
              Pull a different model...
            </button>
          ) : (
            <div>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "0.5rem" }}>
                <h3 style={{ margin: 0, fontSize: "0.85rem", color: "#bbb" }}>Pull model from registry</h3>
                <button
                  onClick={() => setManualOpen(false)}
                  style={{ background: "none", border: "none", color: "#666", fontSize: "0.78rem", cursor: "pointer" }}
                >
                  Collapse
                </button>
              </div>
              <div style={{ display: "flex", gap: "0.5rem" }}>
                <input
                  type="text"
                  placeholder="e.g., llama3, mistral, gemma"
                  value={modelName}
                  onChange={(e) => setModelName(e.target.value)}
                  onKeyPress={(e) => e.key === "Enter" && !pulling && pullModel()}
                  disabled={pulling}
                  style={{
                    flex: 1,
                    padding: "0.6rem 0.75rem",
                    background: "#0a0a0a",
                    color: "#eee",
                    border: "1px solid #333",
                    borderRadius: "6px",
                    fontSize: "0.9rem"
                  }}
                />
                <button
                  onClick={() => pullModel()}
                  disabled={pulling}
                  style={{
                    padding: "0.6rem 1.25rem",
                    background: pulling ? "#666" : "#28a745",
                    color: "#fff",
                    border: "none",
                    borderRadius: "6px",
                    cursor: pulling ? "not-allowed" : "pointer",
                    fontSize: "0.9rem",
                    whiteSpace: "nowrap"
                  }}
                >
                  {pulling ? "Pulling..." : "Pull"}
                </button>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}
