/* ============ project detail pages ============ */

const PROJECT_DETAILS = {
  EDI: {
    code: "EDI",
    title: "Explainable Decision Intelligence",
    tagline: "Production-ready ML explainability — SHAP wrapped around scikit-learn behind a typed FastAPI surface.",
    status: "BUILDING",
    year: "2026",
    github: "https://github.com/Chethan616/explainable-decision-intelligence",
    repo: "Public · in active development",
    tags: ["FastAPI", "SHAP", "scikit-learn", "Python 3.10+", "NumPy", "Pydantic 2"],
    overview: [
      "EDI is a small, opinionated service that turns black-box scikit-learn models into auditable decisions. Every prediction returns the SHAP contributions that produced it — so reviewers, regulators and users can trace which features pushed the model toward 'approve' versus 'deny'.",
      "It's designed for the boring part of ML in production: typed schemas, repeatable scoring, JSON in / JSON out, and an explainability payload you can actually ship to a frontend.",
    ],
    features: [
      { t: "Local SHAP", d: "Per-prediction feature attributions returned alongside the prediction itself, ranked by magnitude." },
      { t: "Global insight", d: "Aggregate feature importance over a holdout split — what the model is doing on average." },
      { t: "Typed API", d: "Pydantic 2 schemas at every boundary. No magic dicts, no untyped JSON." },
      { t: "Async-first", d: "FastAPI + Uvicorn + httpx for fan-out to upstream feature services." },
      { t: "Pluggable models", d: "Any sklearn estimator can be loaded — classifier, regressor, pipeline. SHAP picks the right explainer." },
      { t: "Container-ready", d: "Stateless service, .env-driven config, Dockerfile baked in." },
    ],
    techStack: {
      "Runtime":      ["FastAPI 0.115+", "Uvicorn[standard]", "Pydantic 2.10+", "python-dotenv", "httpx 0.28+"],
      "ML / Explain": ["scikit-learn 1.6+", "SHAP 0.46+", "NumPy 1.26+"],
      "Dev":          ["pytest", "pytest-asyncio", ".env.example"],
    },
  },

  PXL: {
    code: "PXL",
    title: "PixelDNA",
    tagline: "Every pixel leaves a trace. A multi-algorithm image similarity engine that survives resize, compression, crop and rotation.",
    status: "SHIPPED",
    year: "2025",
    github: "https://github.com/Chethan616/PixelDNA",
    repo: "Public · MIT licensed · production-ready",
    tags: ["Flutter", "Go", "Python", "AWS", "OpenCV", "Material 3"],
    overview: [
      "PixelDNA combines perceptual hashing (dHash, pHash, wavelet), block-based hashing and ORB feature matching to detect duplicate and near-duplicate images even after they've been resized, compressed, cropped, rotated or colour-shifted.",
      "Architected as three services — a Go REST core, a Python image-processing layer, and a Flutter Material-3 desktop client — deployable on AWS (S3 + DynamoDB + ECS Fargate) or fully local via Docker Compose.",
    ],
    features: [
      { t: "Multi-algorithm", d: "Perceptual hashing (aHash · dHash · pHash · wavelet) + ORB feature descriptors + block hashing." },
      { t: "Hybrid scoring", d: "0.25 × global + 0.35 × block + 0.40 × feature — classified into exact, near-duplicate, similar, weak." },
      { t: "Robust transforms", d: "Survives JPEG compression, 50% resize, 25% crop, rotation, illumination changes." },
      { t: "AWS-native", d: "S3 storage · DynamoDB hashes · ECS Fargate compute · Cognito auth · CloudWatch logs." },
      { t: "Desktop UI", d: "Flutter Material 3 with AMOLED dark mode, drag-and-drop upload, side-by-side compare." },
      { t: "Sub-second", d: "<500ms for two-image comparison · <3s search across 1000 images · 100+ concurrent users." },
    ],
    techStack: {
      "Frontend":    ["Flutter 3+", "Material Design 3", "Provider"],
      "Backend":     ["Go 1.21+", "Gin", "JWT auth"],
      "Vision":      ["Python 3.10+", "OpenCV", "ImageHash", "NumPy"],
      "Cloud":       ["AWS S3", "DynamoDB", "ECS Fargate", "Cognito", "Lambda", "Terraform"],
      "DevOps":      ["Docker", "docker-compose", "GitHub Actions"],
    },
    arch: {
      title: "Hybrid scoring pipeline",
      steps: [
        { l: "Input",       d: "User uploads image via Flutter desktop client" },
        { l: "Preprocess",  d: "CLAHE normalize · resize · split into 16-block grid" },
        { l: "Hash",        d: "Global (pHash·dHash·wavelet) + Block (16×) + ORB descriptors" },
        { l: "Score",       d: "Weighted hybrid · 0.25 · 0.35 · 0.40 · classified by threshold" },
        { l: "Store",       d: "S3 image · DynamoDB hash + metadata · CloudWatch metric" },
      ],
    },
  },

  CPD: {
    code: "CPD",
    title: "Chat with PDF",
    tagline: "Drop in any PDF — ask it questions. RAG over documents using Gemini 1.5 Flash, LangChain and FAISS.",
    status: "LIVE",
    year: "2025",
    github: "https://github.com/Chethan616/ChatWithPDF",
    live: "https://chethan616-chatwithpdf-app-sd2s5u.streamlit.app",
    repo: "Public · deployed on Streamlit Cloud",
    tags: ["Streamlit", "Gemini 1.5", "LangChain", "FAISS", "PyPDF2"],
    overview: [
      "A Streamlit web app that lets you upload one or many PDFs and converse with them in natural language. Under the hood: text is extracted with PyPDF2, chunked, embedded with Google Generative AI embeddings, and stored in a FAISS vector index for fast semantic retrieval.",
      "Each chat turn flows through a LangChain ConversationalRetrievalChain backed by Gemini 1.5 Flash — so answers stay grounded in the documents and remember the conversation so far.",
    ],
    features: [
      { t: "Multi-PDF", d: "Upload an entire reading list — they're indexed into a single searchable corpus." },
      { t: "Semantic retrieval", d: "FAISS vector store with Google Generative AI embeddings — meaning-aware, not keyword-aware." },
      { t: "Conversation memory", d: "ConversationalRetrievalChain keeps context across turns — follow-ups just work." },
      { t: "Gemini 1.5 Flash", d: "Fast, cheap, long-context — perfect for document Q&A." },
      { t: "Streamlit UI", d: "Zero-config web interface — drop a PDF, ask a question, get an answer." },
      { t: "Deployed", d: "Live demo running on Streamlit Cloud — no local install needed." },
    ],
    techStack: {
      "App":         ["Streamlit", "Python 3.10+"],
      "RAG":         ["LangChain", "langchain-google-genai", "FAISS-cpu"],
      "Extraction":  ["PyPDF2", "Text chunking"],
      "Model":       ["Google Gemini 1.5 Flash", "GoogleGenerativeAIEmbeddings"],
      "Deploy":      ["Streamlit Cloud", "python-dotenv"],
    },
  },

  RWD: {
    code: "RWD",
    title: "Rewordium",
    tagline: "An AI writing assistant + system keyboard. Multi-provider LLM router, document chunking, Firebase credits — 20k+ Play Store users.",
    status: "IN PROGRESS",
    year: "2024 — 26",
    github: "https://play.google.com/store/apps/details?id=com.noxquill.rewordium",
    repo: "Private repository — currently extending into Rewordium - Ai writing agent",
    tags: ["Flutter", "Groq", "Qwen 3", "Gemini", "OpenAI", "Anthropic", "Firebase", "MLKit", "IME"],
    overview: [
      "Rewordium is a Flutter app + custom Android IME that brings paraphrasing, grammar correction, summarizing, tone editing, translation and AI detection into any text field on the device — not just inside its own editor.",
      "The core is a multi-provider LLM router that lets users (and the team) swap between Groq (qwen3-32b default), Gemini, OpenAI and Anthropic without touching feature code. Document chunking handles the long-input case; Firebase auth + credits handles billing. Currently being rewritten as Rewordium - Ai writing agent.",
    ],
    features: [
      { t: "Multi-provider router", d: "Groq / Gemini / OpenAI / Anthropic / custom OpenAI-compatible endpoints behind a single UnifiedAIService." },
      { t: "System keyboard (IME)", d: "Reboard — AOSP/FlorisBoard fork with floating Quick Rewrite overlay in any app." },
      { t: "8 writing tools", d: "Paraphraser · Grammar · Summarizer · Tone editor · Translator (100+) · AI detector · Jade chat · Quick rewrite." },
      { t: "Document ingestion", d: "PDF · DOCX · TXT · MD · URL via DocumentService. MLKit OCR for camera scans." },
      { t: "Credit-gated", d: "Firebase Auth gate + per-feature credit checks before provider calls." },
      { t: "Chunking + merge", d: "DocumentChunkingService splits long inputs, fans out in parallel, merges results." },
      { t: "Analytics", d: "UsageAnalyticsService records provider · feature · credits · errors to Firestore." },
    ],
    techStack: {
      "Mobile":      ["Flutter", "flutter_quill", "CunningDocumentScanner", "MLKit TextRecognizer"],
      "Keyboard":    ["Android IME", "Reboard (AOSP/FlorisBoard fork)", "Floating overlay"],
      "LLM router":  ["Groq (qwen/qwen3-32b)", "Gemini", "OpenAI GPT-4o", "Anthropic Claude 3.x", "Custom endpoints"],
      "Backend":     ["Firebase Auth", "Firestore", "Cloud Functions"],
      "Tooling":     ["_sanitizeUserText", "_stripThinkTags", "_extractJson"],
    },
    mermaid: `flowchart TD
    U([User]) --> INPUT

    subgraph INPUT["Text Input Layer"]
        direction TB
        EDITOR["FocusedEditor<br/>flutter_quill"]
        IMPORT["DocumentService<br/>PDF / DOCX / TXT / MD / URL"]
        SCAN["CunningDocumentScanner<br/>+ MLKit TextRecognizer"]
        KB["Reboard Keyboard<br/>IME · AOSP/FlorisBoard fork"]
    end

    INPUT --> ENGINE

    subgraph ENGINE["UnifiedAIService · Provider Router"]
        direction LR
        GATE{"Firebase Auth<br/>Gate"}
        GATE -->|logged in| CRED{"Credit Check<br/>FirebaseService"}
        CRED -->|sufficient| PROVIDER

        subgraph PROVIDER["LLM Provider"]
            GROQ["Groq<br/>qwen/qwen3-32b<br/>default"]
            GEMINI["Gemini<br/>generativelanguage API"]
            OAI["OpenAI<br/>GPT-4o / GPT-4"]
            CLAUDE["Anthropic<br/>Claude 3.x"]
            CUSTOM["Custom<br/>OpenAI-compat endpoint"]
        end

        PROVIDER --> SANITIZE["sanitizeUserText<br/>stripThinkTags<br/>extractJson"]
    end

    subgraph TOOLS["AI Writing Tools"]
        direction TB
        PARA["Paraphraser<br/>Tone modes + Custom prompt"]
        GRAM["Grammar Check<br/>Corrections + Error list"]
        SUMM["Summarizer<br/>Summary + Key points"]
        TONE["Tone Editor<br/>Change tracking"]
        TRANS["Translator<br/>100+ languages + Notes"]
        DETECT["AI Detector<br/>Per-sentence scores"]
        JADE["Jade AI Chat<br/>Persona-aware chatbot"]
        KB_AI["Keyboard Quick Rewrite<br/>Floating overlay"]
    end

    ENGINE --> TOOLS
    TOOLS --> OUT["Result Output<br/>same FocusedEditor view"]
    OUT --> U

    subgraph CHUNKING["DocumentChunkingService"]
        CHK["Splits text > limit<br/>into parallel chunks<br/>then merges results"]
    end

    IMPORT --> CHUNKING
    CHUNKING --> ENGINE

    subgraph ANALYTICS["UsageAnalyticsService"]
        ANA["Records provider · feature<br/>credits used · errors<br/>Firestore"]
    end

    ENGINE --> ANALYTICS`,
  },
};

/* ---- Mermaid block ---- */
function MermaidBlock({ chart }) {
  const ref = React.useRef(null);
  const [ready, setReady] = React.useState(false);

  React.useEffect(() => {
    let cancelled = false;
    const tryRender = () => {
      if (!window.mermaid) { setTimeout(tryRender, 80); return; }
      try {
        window.mermaid.initialize({
          startOnLoad: false,
          theme: "base",
          themeVariables: {
            background: "#0a0a0a",
            primaryColor: "#15151310",
            primaryTextColor: "#f5f5f3",
            primaryBorderColor: "#3a3a37",
            lineColor: "#6b6b68",
            secondaryColor: "#1f1f1d",
            tertiaryColor: "#1a1a18",
            tertiaryTextColor: "#f5f5f3",
            tertiaryBorderColor: "#3a3a37",
            fontFamily: 'JetBrains Mono, ui-monospace, monospace',
            fontSize: "13px",
            clusterBkg: "#13131110",
            clusterBorder: "#2a2a27",
            edgeLabelBackground: "#0a0a0a",
            nodeTextColor: "#f5f5f3",
          },
          flowchart: { htmlLabels: true, curve: "basis", padding: 16, nodeSpacing: 24, rankSpacing: 36 },
          securityLevel: "loose",
        });
        const id = "mm-" + Math.random().toString(36).slice(2, 9);
        window.mermaid.render(id, chart).then(({ svg }) => {
          if (!cancelled && ref.current) {
            ref.current.innerHTML = svg;
            setReady(true);
          }
        }).catch((e) => { console.warn("mermaid render", e); });
      } catch (e) { console.warn("mermaid init", e); }
    };
    tryRender();
    return () => { cancelled = true; };
  }, [chart]);

  return (
    <div className="mermaid-wrap">
      {!ready && <div className="mermaid-loading">Rendering architecture diagram…</div>}
      <div ref={ref} className="mermaid-block" />
    </div>
  );
}

/* ---- Architecture step list (used when no mermaid) ---- */
function ArchSteps({ arch }) {
  if (!arch) return null;
  return (
    <div className="arch">
      <div className="arch__title">{arch.title}</div>
      <ol className="arch__steps">
        {arch.steps.map((s, i) => (
          <li key={i}>
            <span className="arch__n">{String(i+1).padStart(2,"0")}</span>
            <div>
              <div className="arch__l">{s.l}</div>
              <div className="arch__d">{s.d}</div>
            </div>
          </li>
        ))}
      </ol>
    </div>
  );
}

/* ---- The modal itself ---- */
function ProjectModal({ code, onClose }) {
  const open = !!code;
  const data = code && PROJECT_DETAILS[code];

  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, onClose]);

  React.useEffect(() => {
    document.body.style.overflow = open ? "hidden" : "";
    return () => { document.body.style.overflow = ""; };
  }, [open]);

  if (!data && !open) return null;

  return (
    <div className={"pmodal " + (open ? "is-open" : "")} aria-hidden={!open}>
      <div className="pmodal__bg" onClick={onClose} />
      <div className="pmodal__panel">
        <div className="pmodal__chrome">
          <button className="pmodal__close" onClick={onClose} data-cursor="link" aria-label="Close project">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" width="18" height="18">
              <path d="M6 6l12 12M6 18L18 6"/>
            </svg>
            <span>CLOSE</span>
            <span className="pmodal__esc">ESC</span>
          </button>
          <div className="pmodal__breadcrumb">
            {data && (
              <React.Fragment>
                <span>WORK</span>
                <span>/</span>
                <span>{data.code}</span>
                <span>·</span>
                <span>{data.year}</span>
              </React.Fragment>
            )}
          </div>
        </div>

        {data && (
          <div className="pmodal__scroll">
            <div className="pmodal__inner shell">
              <div className="pmodal__hero">
                <div className="pmodal__meta">
                  <span>{data.code} · {data.status}</span>
                  <span>{data.repo}</span>
                </div>
                <h2 className="pmodal__title">{data.title}</h2>
                <p className="pmodal__tagline">{data.tagline}</p>
                <div className="pmodal__tags">
                  {data.tags.map(t => <span key={t} className="pmodal__tag">{t}</span>)}
                </div>
                <div className="pmodal__cta-row">
                  {data.live && (
                    <a className="pmodal__cta pmodal__cta--solid" href={data.live} target="_blank" rel="noreferrer" data-cursor="link">
                      OPEN LIVE DEMO
                      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M7 17L17 7M9 7h8v8"/></svg>
                    </a>
                  )}
                  <a className="pmodal__cta" href={data.github} target="_blank" rel="noreferrer" data-cursor="link">
                    {data.github.includes("play.google") ? "OPEN ON PLAY STORE" : "VIEW SOURCE"}
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M7 17L17 7M9 7h8v8"/></svg>
                  </a>
                </div>
              </div>

              <div className="pmodal__glyph">
                <ProjectGlyph code={data.code} />
              </div>

              <div className="pmodal__section">
                <div className="pmodal__sechead"><span>01</span><span>OVERVIEW</span></div>
                {data.overview.map((p, i) => <p key={i} className="pmodal__p">{p}</p>)}
              </div>

              <div className="pmodal__section">
                <div className="pmodal__sechead"><span>02</span><span>KEY FEATURES</span></div>
                <div className="pmodal__features">
                  {data.features.map((f, i) => (
                    <div key={i} className="pmodal__feat">
                      <span className="pmodal__feat-n">{String(i+1).padStart(2,"0")}</span>
                      <div className="pmodal__feat-t">{f.t}</div>
                      <div className="pmodal__feat-d">{f.d}</div>
                    </div>
                  ))}
                </div>
              </div>

              {(data.arch || data.mermaid) && (
                <div className="pmodal__section">
                  <div className="pmodal__sechead"><span>03</span><span>ARCHITECTURE</span></div>
                  {data.mermaid
                    ? <MermaidBlock chart={data.mermaid} />
                    : <ArchSteps arch={data.arch} />
                  }
                </div>
              )}

              <div className="pmodal__section">
                <div className="pmodal__sechead">
                  <span>{(data.arch || data.mermaid) ? "04" : "03"}</span>
                  <span>TECHNOLOGY STACK</span>
                </div>
                <div className="pmodal__stack">
                  {Object.entries(data.techStack).map(([k, list]) => (
                    <div key={k} className="pmodal__stack-col">
                      <div className="pmodal__stack-k">{k}</div>
                      <ul>{list.map(t => <li key={t}>{t}</li>)}</ul>
                    </div>
                  ))}
                </div>
              </div>

              <div className="pmodal__foot">
                <a href={data.github} target="_blank" rel="noreferrer" className="pmodal__cta pmodal__cta--solid" data-cursor="link">
                  {data.github.includes("play.google") ? "OPEN ON PLAY STORE" : "OPEN ON GITHUB"}
                  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M7 17L17 7M9 7h8v8"/></svg>
                </a>
                <button onClick={onClose} className="pmodal__cta" data-cursor="link">
                  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M19 12H5M11 18l-6-6 6-6"/></svg>
                  BACK TO PORTFOLIO
                </button>
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { ProjectModal, PROJECT_DETAILS, MermaidBlock });
