"use client";

/**
 * Read-only file viewer that pulls a file's content from Gitea via
 * `GET /api/projects/[projectId]/file?path=…`. No syntax highlight
 * yet — just monospaced text. Phase 2 can swap in Shiki/Prism if
 * the founders ever read enough code here to need it.
 */

import { useEffect, useState } from "react";
import { Loader2, AlertCircle, FileText } from "lucide-react";

interface GiteaFileViewerProps {
  projectId: string;
  /** Repo path of the file to view, e.g. "apps/web/package.json".
   *  When null, an empty state prompts the user to pick a file. */
  path: string | null;
}

interface ApiResponse {
  type: "file" | "dir";
  content?: string;
  encoding?: string;
  name?: string;
  error?: string;
}

export function GiteaFileViewer({ projectId, path }: GiteaFileViewerProps) {
  const [content, setContent] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (!path) {
      setContent(null);
      setError(null);
      setLoading(false);
      return;
    }

    let cancelled = false;
    setLoading(true);
    setError(null);
    setContent(null);

    fetch(`/api/projects/${projectId}/file?path=${encodeURIComponent(path)}`, {
      credentials: "include",
    })
      .then(async r => {
        const data = (await r.json()) as ApiResponse;
        if (!r.ok) throw new Error(data.error || `HTTP ${r.status}`);
        if (data.type !== "file") throw new Error("Not a file");
        return data.content ?? "";
      })
      .then(c => {
        if (!cancelled) setContent(c);
      })
      .catch(err => {
        if (!cancelled) setError(err.message || "Failed to load file");
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });

    return () => {
      cancelled = true;
    };
  }, [projectId, path]);

  if (!path) {
    return (
      <Centered>
        <FileText size={18} style={{ color: INK.muted }} />
        <span>Pick a file from the codebase to preview it here.</span>
      </Centered>
    );
  }

  if (loading) {
    return (
      <Centered>
        <Loader2 size={14} className="animate-spin" style={{ color: INK.muted }} />
        <span>Loading {basename(path)}…</span>
      </Centered>
    );
  }

  if (error) {
    return (
      <Centered>
        <AlertCircle size={14} style={{ color: INK.muted }} />
        <span>{error}</span>
      </Centered>
    );
  }

  return (
    <div style={wrap}>
      <pre style={pre}>
        <code>{content}</code>
      </pre>
    </div>
  );
}

function basename(p: string) {
  return p.split("/").pop() || p;
}

function Centered({ children }: { children: React.ReactNode }) {
  return (
    <div style={{
      flex: 1, display: "flex", alignItems: "center", justifyContent: "center",
      gap: 10, color: INK.mid, fontSize: "0.85rem", padding: "32px 16px", textAlign: "center",
    }}>
      {children}
    </div>
  );
}

const INK = {
  ink: "#1a1a1a",
  mid: "#5f5e5a",
  muted: "#a09a90",
  border: "#e8e4dc",
} as const;

const wrap: React.CSSProperties = {
  flex: 1,
  minHeight: 0,
  overflow: "auto",
  margin: "-4px -10px",
};

const pre: React.CSSProperties = {
  margin: 0,
  padding: "8px 10px",
  fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
  fontSize: "0.78rem",
  lineHeight: 1.55,
  color: INK.ink,
  whiteSpace: "pre",
  tabSize: 2,
};
