import React, { useState, useMemo, useCallback } from "react";
import { createRoot } from "react-dom/client";
import { useApp, useHostStyles } from "@modelcontextprotocol/ext-apps/react";

/* ──────────────────────────── types ──────────────────────────── */
interface Template {
  id: string;
  name: string;
  description: string;
  fullDescription: string;
  author: string;
  category: string;
  task: string;
  complexity: "Beginner" | "Intermediate" | "Advanced";
  nodes: string[];
  views: number;
  setupSteps: string[];
  createdAt: string;
}

type SortMode = "popular" | "newest" | "most-nodes";

/* ──────────────────────────── data ──────────────────────────── */
const TEMPLATES: Template[] = [
  // AI
  { id: "t-001", name: "Build an AI Agent with OpenAI", description: "Create an autonomous AI agent that can use tools, browse the web, and complete multi-step tasks using OpenAI function calling.", fullDescription: "This template sets up a full AI agent pipeline using OpenAI's function calling API. The agent can autonomously decide which tools to use, chain multiple actions together, and handle complex multi-step tasks. Includes error handling, memory management, and conversation history.", author: "n8n Team", category: "AI", task: "AI Automation", complexity: "Advanced", nodes: ["OpenAI", "HTTP Request", "Code", "IF", "Set", "Merge"], views: 45200, setupSteps: ["Add your OpenAI API key", "Configure the system prompt for your use case", "Add custom tools as sub-workflows", "Enable the workflow and test with a webhook trigger"], createdAt: "2025-11-15" },
  { id: "t-002", name: "Summarize Documents with AI", description: "Automatically summarize PDFs, docs, and text files dropped into a folder using GPT-4 with smart chunking.", fullDescription: "Watch a folder for new documents, extract text from PDFs and Word docs, split into manageable chunks, summarize each chunk, then combine into a final summary. Supports PDF, DOCX, TXT, and MD formats.", author: "David Chen", category: "AI", task: "AI Automation", complexity: "Intermediate", nodes: ["Google Drive Trigger", "Extract Document", "OpenAI", "Code", "Gmail"], views: 32100, setupSteps: ["Connect Google Drive account", "Set the folder to watch", "Add OpenAI credentials", "Configure output destination (email, Slack, etc.)"], createdAt: "2025-10-20" },
  { id: "t-003", name: "AI Customer Support Bot", description: "Intelligent support bot that answers questions from your knowledge base, escalates complex issues, and logs all interactions.", fullDescription: "A complete customer support automation using RAG (Retrieval Augmented Generation). The bot searches your knowledge base for relevant answers, generates helpful responses, detects sentiment, and automatically escalates negative or complex interactions to human agents.", author: "n8n Team", category: "AI", task: "AI Automation", complexity: "Advanced", nodes: ["Webhook", "OpenAI", "Pinecone", "IF", "Slack", "Postgres", "Code"], views: 28700, setupSteps: ["Set up Pinecone vector store with your docs", "Configure OpenAI with your system prompt", "Connect Slack for escalation channel", "Set up Postgres for logging interactions"], createdAt: "2025-12-01" },
  { id: "t-004", name: "RAG Pipeline with Pinecone", description: "Build a retrieval-augmented generation pipeline that indexes documents and answers questions using vector search.", fullDescription: "Complete RAG setup: ingest documents, chunk them intelligently, generate embeddings, store in Pinecone, and query with semantic search. Includes a webhook endpoint for Q&A and a batch indexing workflow for new documents.", author: "Sarah Kim", category: "AI", task: "AI Automation", complexity: "Advanced", nodes: ["Webhook", "OpenAI", "Pinecone", "Code", "Split In Batches", "HTTP Request"], views: 21400, setupSteps: ["Create Pinecone index (1536 dimensions for ada-002)", "Add OpenAI API key", "Run the indexing workflow with your documents first", "Test the Q&A endpoint with sample questions"], createdAt: "2025-09-10" },
  { id: "t-005", name: "Image Analysis with GPT-4 Vision", description: "Analyze images from URLs or uploads using GPT-4 Vision. Extract text, describe content, and classify images automatically.", fullDescription: "Process images through GPT-4 Vision for OCR, content description, object detection, and custom classification. Supports batch processing, URL-based and base64 image inputs, with structured JSON output for downstream processing.", author: "Mike Torres", category: "AI", task: "AI Automation", complexity: "Intermediate", nodes: ["Webhook", "OpenAI", "Code", "IF", "Google Sheets"], views: 19800, setupSteps: ["Add OpenAI API key with GPT-4 Vision access", "Configure the analysis prompt for your use case", "Set up output destination (Sheets, database, etc.)", "Test with sample image URLs"], createdAt: "2025-11-28" },

  // Automation
  { id: "t-006", name: "Slack to Email Forwarder", description: "Forward important Slack messages to email with smart filtering. Supports mentions, keywords, and channel-specific rules.", fullDescription: "Monitor Slack channels for messages matching your criteria (mentions, keywords, reactions) and forward them as formatted emails. Includes deduplication, batching for digest mode, and customizable email templates.", author: "n8n Team", category: "Automation", task: "Slack Integration", complexity: "Beginner", nodes: ["Slack Trigger", "IF", "Code", "Gmail", "Set"], views: 38900, setupSteps: ["Connect your Slack workspace", "Configure trigger channels and keywords", "Add Gmail or SMTP credentials", "Set forwarding rules and email template"], createdAt: "2025-08-15" },
  { id: "t-007", name: "GitHub Issue Notifier", description: "Get instant notifications when new issues are created, assigned, or labeled in your GitHub repositories.", fullDescription: "Watch GitHub repositories for issue events and send notifications to Slack, email, or any other channel. Supports filtering by label, assignee, and repository. Includes rich formatting with issue details, links, and action buttons.", author: "Alex Rivera", category: "Automation", task: "Webhook Processing", complexity: "Beginner", nodes: ["GitHub Trigger", "IF", "Slack", "Set", "Code"], views: 27600, setupSteps: ["Connect GitHub account", "Select repositories to watch", "Configure Slack webhook or email", "Set up label/assignee filters"], createdAt: "2025-07-20" },
  { id: "t-008", name: "RSS Feed Monitor", description: "Monitor multiple RSS feeds and get notified of new posts. Supports filtering, deduplication, and multi-channel delivery.", fullDescription: "Poll RSS feeds at configurable intervals, detect new entries using persistent deduplication, filter by keywords or categories, and deliver formatted notifications. Supports Slack, email, Discord, and webhook delivery.", author: "n8n Team", category: "Automation", task: "Scheduling", complexity: "Beginner", nodes: ["Schedule Trigger", "RSS Read", "IF", "Code", "Slack", "Postgres"], views: 35200, setupSteps: ["Add RSS feed URLs", "Set polling interval (default: 15 min)", "Configure notification channels", "Set keyword filters if needed"], createdAt: "2025-06-10" },
  { id: "t-009", name: "Auto-Backup Google Drive", description: "Automatically backup important files from Google Drive to another cloud storage or local NAS on a schedule.", fullDescription: "Scheduled workflow that syncs files from specified Google Drive folders to a backup destination. Supports incremental backups (only new/modified files), folder structure preservation, and backup verification with reporting.", author: "Lisa Park", category: "Automation", task: "Scheduling", complexity: "Intermediate", nodes: ["Schedule Trigger", "Google Drive", "S3", "Code", "IF", "Gmail"], views: 22300, setupSteps: ["Connect Google Drive account", "Configure source folders", "Set up S3 or backup destination credentials", "Set backup schedule (daily recommended)"], createdAt: "2025-10-05" },
  { id: "t-010", name: "Daily Report Generator", description: "Generate and send daily summary reports from multiple data sources including databases, APIs, and spreadsheets.", fullDescription: "Aggregate data from multiple sources every morning, generate formatted reports with charts and tables, and distribute via email, Slack, or save to Google Sheets. Includes comparison with previous day and trend indicators.", author: "n8n Team", category: "Automation", task: "Scheduling", complexity: "Intermediate", nodes: ["Schedule Trigger", "Postgres", "HTTP Request", "Code", "Gmail", "Google Sheets"], views: 31500, setupSteps: ["Connect your data sources", "Customize report template and metrics", "Set schedule (default: 8am weekdays)", "Add email recipients or Slack channel"], createdAt: "2025-09-25" },

  // Integration
  { id: "t-011", name: "Sync Contacts CRM to Sheets", description: "Two-way sync between your CRM contacts and Google Sheets. Changes in either direction are automatically reflected.", fullDescription: "Bidirectional sync between HubSpot/Salesforce CRM and Google Sheets. Detect changes in both systems, handle conflicts with configurable resolution strategy, and maintain an audit log of all sync operations.", author: "James Wu", category: "Integration", task: "Data Sync", complexity: "Intermediate", nodes: ["Schedule Trigger", "HubSpot", "Google Sheets", "Code", "IF", "Merge"], views: 26800, setupSteps: ["Connect CRM account (HubSpot or Salesforce)", "Connect Google Sheets", "Map contact fields between systems", "Set sync interval and conflict resolution"], createdAt: "2025-11-01" },
  { id: "t-012", name: "Webhook to Database Logger", description: "Receive webhooks from any service and log structured data to PostgreSQL with automatic schema detection.", fullDescription: "Universal webhook receiver that accepts any JSON payload, automatically detects and creates database schema, logs all incoming data with timestamps and source info. Includes payload validation, rate limiting, and error handling.", author: "n8n Team", category: "Integration", task: "Webhook Processing", complexity: "Beginner", nodes: ["Webhook", "Code", "Postgres", "IF", "Set"], views: 41200, setupSteps: ["Set up the webhook URL", "Connect PostgreSQL database", "Configure the target table (auto-created if needed)", "Share webhook URL with sending services"], createdAt: "2025-05-20" },
  { id: "t-013", name: "Stripe Payment Alerts", description: "Get real-time alerts for Stripe payments, refunds, disputes, and subscription changes with rich formatting.", fullDescription: "Process Stripe webhook events for payments, refunds, subscription changes, and disputes. Format rich notifications with customer details, amounts, and direct links to Stripe dashboard. Supports Slack, email, and SMS alerts.", author: "Maria Garcia", category: "Integration", task: "Webhook Processing", complexity: "Beginner", nodes: ["Webhook", "IF", "Code", "Slack", "Gmail", "Set"], views: 33400, setupSteps: ["Add Stripe webhook secret", "Configure Stripe webhook URL in dashboard", "Connect Slack and/or email", "Select which events to notify on"], createdAt: "2025-08-30" },
  { id: "t-014", name: "Jira-Slack Sync", description: "Sync Jira issues with Slack threads. New issues create threads, status changes update them, and comments flow both ways.", fullDescription: "Complete bidirectional sync between Jira and Slack. New Jira issues create formatted Slack threads in the appropriate channel. Status changes, assignments, and comments in Jira update the thread. Replies in Slack threads are posted as Jira comments.", author: "n8n Team", category: "Integration", task: "Slack Integration", complexity: "Advanced", nodes: ["Jira Trigger", "Slack", "Webhook", "Code", "IF", "Postgres", "Set"], views: 24100, setupSteps: ["Connect Jira with webhook or OAuth", "Connect Slack workspace", "Map Jira projects to Slack channels", "Set up Postgres for thread tracking"], createdAt: "2025-10-15" },
  { id: "t-015", name: "Airtable Form Processor", description: "Process Airtable form submissions with validation, enrichment, notifications, and automated follow-up sequences.", fullDescription: "Trigger on new Airtable form records, validate and enrich data (email verification, company lookup), send confirmation emails, notify the team, and start automated follow-up sequences based on form type.", author: "Rachel Lee", category: "Integration", task: "Data Sync", complexity: "Intermediate", nodes: ["Airtable Trigger", "HTTP Request", "Code", "Gmail", "Slack", "IF"], views: 18900, setupSteps: ["Connect Airtable account and select base", "Configure form validation rules", "Set up email templates for confirmations", "Add Slack channel for team notifications"], createdAt: "2025-11-10" },

  // Data
  { id: "t-016", name: "CSV to Database Importer", description: "Import CSV files into PostgreSQL or MySQL with automatic column mapping, data validation, and error reporting.", fullDescription: "Watch a folder or email for CSV files, parse them with configurable delimiters, validate data types and constraints, map columns to database fields, and bulk insert with error handling. Generates import reports with success/failure counts.", author: "n8n Team", category: "Data", task: "Data Transform", complexity: "Intermediate", nodes: ["Google Drive Trigger", "Spreadsheet File", "Code", "Postgres", "IF", "Gmail"], views: 29700, setupSteps: ["Connect file source (Drive, S3, or email)", "Connect database", "Configure column mapping", "Set validation rules and error handling"], createdAt: "2025-07-15" },
  { id: "t-017", name: "API Data Aggregator", description: "Collect data from multiple APIs on a schedule, normalize the responses, and store in a unified format.", fullDescription: "Scheduled workflow that calls multiple REST APIs in parallel, normalizes different response formats into a unified schema, handles pagination and rate limiting, and stores the aggregated data. Supports incremental updates and data quality checks.", author: "Tom Anderson", category: "Data", task: "API Integration", complexity: "Intermediate", nodes: ["Schedule Trigger", "HTTP Request", "Code", "Merge", "Postgres", "Set"], views: 20500, setupSteps: ["Add API endpoints and authentication", "Configure normalization mappings", "Set up database table for aggregated data", "Set collection schedule"], createdAt: "2025-09-05" },
  { id: "t-018", name: "Multi-Source Data Merge", description: "Merge data from Sheets, Airtable, and APIs into a single master dataset with conflict resolution.", fullDescription: "Pull data from multiple sources, match records using configurable keys, merge with priority-based conflict resolution, and output a unified master dataset. Supports full and incremental merge modes with detailed merge reports.", author: "n8n Team", category: "Data", task: "Data Transform", complexity: "Advanced", nodes: ["Schedule Trigger", "Google Sheets", "Airtable", "HTTP Request", "Merge", "Code", "Postgres"], views: 17600, setupSteps: ["Connect all data sources", "Define merge keys for record matching", "Set priority order for conflict resolution", "Configure output destination"], createdAt: "2025-08-20" },
  { id: "t-019", name: "Scheduled Data Cleanup", description: "Automatically clean, deduplicate, and archive old records in your database on a configurable schedule.", fullDescription: "Run scheduled maintenance on your database: remove duplicates using configurable matching rules, archive records older than a threshold, clean up orphaned records, and generate cleanup reports with before/after statistics.", author: "Amy Wright", category: "Data", task: "Database Ops", complexity: "Intermediate", nodes: ["Schedule Trigger", "Postgres", "Code", "IF", "Gmail", "Set"], views: 15800, setupSteps: ["Connect database", "Configure deduplication rules", "Set archive threshold (e.g., 90 days)", "Set schedule and notification email"], createdAt: "2025-10-30" },
  { id: "t-020", name: "ETL Pipeline Template", description: "Complete Extract-Transform-Load pipeline template for moving and transforming data between systems.", fullDescription: "Production-ready ETL pipeline with configurable extract, transform, and load stages. Supports multiple source and destination types, includes data validation, error handling, retry logic, and monitoring. Generates detailed run logs.", author: "n8n Team", category: "Data", task: "Data Transform", complexity: "Advanced", nodes: ["Schedule Trigger", "HTTP Request", "Postgres", "Code", "IF", "Split In Batches", "Merge"], views: 23400, setupSteps: ["Configure source connection", "Define transformation rules in Code node", "Set up destination connection", "Enable monitoring and alerting"], createdAt: "2025-06-25" },

  // More
  { id: "t-021", name: "Email Sequence Sender", description: "Send automated email sequences with delays, personalization, and engagement tracking for outreach campaigns.", fullDescription: "Marketing automation email sequence: define multi-step email campaigns with configurable delays, merge tags for personalization, open/click tracking, and automatic sequence branching based on recipient engagement.", author: "n8n Team", category: "Automation", task: "Email Automation", complexity: "Intermediate", nodes: ["Schedule Trigger", "Postgres", "Code", "Gmail", "IF", "Wait", "Set"], views: 36500, setupSteps: ["Set up email templates with merge tags", "Import recipient list to database", "Configure sequence timing and delays", "Set up tracking pixel endpoint"], createdAt: "2025-11-20" },
  { id: "t-022", name: "Social Media Scheduler", description: "Schedule and auto-publish content across Twitter, LinkedIn, and Instagram from a single content calendar.", fullDescription: "Manage a content calendar in Google Sheets or Airtable, automatically publish posts at scheduled times across multiple platforms. Supports images, threads, carousel posts, and hashtag management.", author: "Chris Blake", category: "Automation", task: "Scheduling", complexity: "Intermediate", nodes: ["Schedule Trigger", "Google Sheets", "Twitter", "HTTP Request", "Code", "IF"], views: 28300, setupSteps: ["Connect social media accounts", "Set up content calendar spreadsheet", "Configure posting schedule", "Add image hosting if using visuals"], createdAt: "2025-09-15" },
  { id: "t-023", name: "Inventory Alert System", description: "Monitor inventory levels across warehouses and get alerts when stock drops below configurable thresholds.", fullDescription: "Connect to your inventory management system or database, monitor stock levels in real-time, send tiered alerts (warning/critical) when items fall below thresholds, and optionally auto-generate purchase orders.", author: "Pat Morgan", category: "Automation", task: "Scheduling", complexity: "Intermediate", nodes: ["Schedule Trigger", "HTTP Request", "Postgres", "IF", "Slack", "Gmail", "Code"], views: 14700, setupSteps: ["Connect inventory data source", "Set threshold levels per product/category", "Configure alert channels", "Optional: set up auto-reorder rules"], createdAt: "2025-10-10" },
  { id: "t-024", name: "Customer Feedback Analyzer", description: "Collect feedback from multiple channels, analyze sentiment with AI, categorize issues, and route to the right team.", fullDescription: "Aggregate customer feedback from surveys, emails, support tickets, and social media. Use AI to analyze sentiment and categorize topics. Route negative feedback for urgent handling, generate weekly insight reports with trends.", author: "n8n Team", category: "AI", task: "AI Automation", complexity: "Advanced", nodes: ["Webhook", "OpenAI", "Postgres", "Code", "IF", "Slack", "Gmail", "Google Sheets"], views: 19200, setupSteps: ["Connect feedback sources", "Configure OpenAI for sentiment analysis", "Set up routing rules by category", "Create weekly report schedule"], createdAt: "2025-12-05" },
  { id: "t-025", name: "Lead Scoring Workflow", description: "Automatically score incoming leads based on demographic data, behavior signals, and engagement metrics.", fullDescription: "Score leads using a configurable point system: company size, industry, website visits, email engagement, content downloads, and more. Automatically route high-score leads to sales, nurture medium scores, and archive low scores.", author: "Jordan Ellis", category: "Automation", task: "Data Sync", complexity: "Advanced", nodes: ["Webhook", "HubSpot", "Code", "IF", "Postgres", "Slack", "Gmail"], views: 16300, setupSteps: ["Connect CRM (HubSpot/Salesforce)", "Configure scoring criteria and weights", "Set score thresholds for routing", "Connect sales notification channels"], createdAt: "2025-11-05" },
  { id: "t-026", name: "Invoice Generator", description: "Generate professional PDF invoices from order data, send to customers, and track payment status automatically.", fullDescription: "Pull order/billing data from your system, generate formatted PDF invoices using HTML templates, email to customers with payment links, track payment status, and send automated reminders for overdue invoices.", author: "n8n Team", category: "Automation", task: "File Processing", complexity: "Intermediate", nodes: ["Webhook", "Code", "HTML", "Gmail", "Postgres", "Schedule Trigger", "IF"], views: 25100, setupSteps: ["Customize invoice HTML template", "Connect billing data source", "Configure email templates", "Set up payment reminder schedule"], createdAt: "2025-08-10" },
  { id: "t-027", name: "Meeting Notes Transcriber", description: "Automatically transcribe meeting recordings, generate summaries, extract action items, and distribute to attendees.", fullDescription: "Process meeting recordings from Zoom/Google Meet: transcribe audio using Whisper API, generate meeting summary and action items with GPT-4, format as structured notes, and distribute to all attendees with assigned tasks.", author: "Sam Wilson", category: "AI", task: "AI Automation", complexity: "Advanced", nodes: ["Webhook", "HTTP Request", "OpenAI", "Code", "Gmail", "Google Calendar", "Slack"], views: 22800, setupSteps: ["Connect meeting platform (Zoom/Meet)", "Add OpenAI API key with Whisper access", "Configure note template", "Set up distribution channels"], createdAt: "2025-12-10" },
  { id: "t-028", name: "Error Monitoring Dashboard", description: "Aggregate errors from multiple services, deduplicate, prioritize by severity, and alert the right team members.", fullDescription: "Receive error webhooks from Sentry, custom apps, and APIs. Deduplicate similar errors, track frequency and trends, assign severity levels, and send targeted alerts. Includes a daily error digest and resolution tracking.", author: "n8n Team", category: "Automation", task: "Webhook Processing", complexity: "Intermediate", nodes: ["Webhook", "Code", "Postgres", "IF", "Slack", "Gmail", "Set"], views: 20100, setupSteps: ["Configure webhook endpoints for each service", "Set up error classification rules", "Connect notification channels", "Configure daily digest schedule"], createdAt: "2025-09-20" },
  { id: "t-029", name: "API Health Checker", description: "Monitor API endpoints for uptime, response time, and correctness. Get instant alerts when services degrade.", fullDescription: "Periodically check multiple API endpoints for availability, response time, status codes, and response body validation. Track metrics over time, generate uptime reports, and send instant alerts when thresholds are breached.", author: "Dana Ortiz", category: "Automation", task: "API Integration", complexity: "Beginner", nodes: ["Schedule Trigger", "HTTP Request", "IF", "Code", "Slack", "Postgres"], views: 24600, setupSteps: ["Add API endpoints to monitor", "Set response time thresholds", "Configure alert channels", "Set check frequency (default: 5 min)"], createdAt: "2025-07-30" },
  { id: "t-030", name: "Content Approval Flow", description: "Multi-step content approval workflow with reviewer assignments, feedback loops, and automatic publishing.", fullDescription: "Submit content for review through a form or API. Automatically assign reviewers based on content type, track approval status, handle revision requests with feedback, and auto-publish approved content to CMS or social channels.", author: "n8n Team", category: "Automation", task: "Email Automation", complexity: "Intermediate", nodes: ["Webhook", "Slack", "IF", "Wait", "Code", "HTTP Request", "Gmail"], views: 17900, setupSteps: ["Configure submission form/webhook", "Set up reviewer assignments", "Connect publishing destinations", "Customize notification templates"], createdAt: "2025-10-25" },
];

const ALL_TASKS = [
  "All", "AI Automation", "Data Sync", "Webhook Processing", "Email Automation",
  "Slack Integration", "Data Transform", "File Processing", "Scheduling",
  "API Integration", "Database Ops",
];

const TASK_ICONS: Record<string, string> = {
  "All": "📋", "AI Automation": "🤖", "Data Sync": "🔄", "Webhook Processing": "🪝",
  "Email Automation": "📧", "Slack Integration": "💬", "Data Transform": "⚡",
  "File Processing": "📁", "Scheduling": "⏰", "API Integration": "🔌", "Database Ops": "🗄️",
};

const COMPLEXITY_COLORS: Record<string, string> = {
  Beginner: "var(--color-success)",
  Intermediate: "var(--color-warning)",
  Advanced: "var(--color-error)",
};

const NODE_ICONS: Record<string, string> = {
  "OpenAI": "🧠", "HTTP Request": "🌐", "Code": "💻", "IF": "🔀", "Set": "📝",
  "Merge": "🔗", "Google Drive": "📂", "Google Drive Trigger": "📂", "Gmail": "📧",
  "Extract Document": "📄", "Slack": "💬", "Slack Trigger": "💬", "Webhook": "🪝",
  "Postgres": "🐘", "Pinecone": "🌲", "GitHub Trigger": "🐙", "Schedule Trigger": "⏰",
  "RSS Read": "📡", "S3": "☁️", "HubSpot": "🟠", "Google Sheets": "📊",
  "Airtable": "📋", "Airtable Trigger": "📋", "Spreadsheet File": "📑",
  "Twitter": "🐦", "Jira Trigger": "🔵", "Split In Batches": "📦",
  "HTML": "🌐", "Wait": "⏳", "Google Calendar": "📅",
};

/* ──────────────────────────── app styles ──────────────────────────── */
const css = `
  .tg-search-bar { margin-bottom: 20px; }
  .tg-search-bar .input { padding: 12px 16px 12px 42px; font-size: 14px; border-radius: var(--radius-md); background: var(--color-bg-secondary); }
  .tg-search-bar .search-icon { font-size: 16px; left: 14px; }

  .tg-filters { margin-bottom: 20px; }
  .tg-filter-section { margin-bottom: 12px; }
  .tg-filter-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--color-text-dim); margin-bottom: 8px; font-weight: 600; }
  .tg-pills { display: flex; gap: 6px; flex-wrap: wrap; }
  .tg-pill { padding: 6px 14px; border-radius: 20px; font-size: 12px; font-weight: 500; border: 1px solid var(--color-border); background: var(--color-bg-secondary); color: var(--color-text-muted); cursor: pointer; transition: all var(--transition); display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
  .tg-pill:hover { border-color: var(--color-n8n); color: var(--color-text); }
  .tg-pill.active { background: rgba(255,109,90,0.15); border-color: var(--color-n8n); color: var(--color-n8n); }

  .tg-toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; flex-wrap: wrap; gap: 8px; }
  .tg-result-count { font-size: 13px; color: var(--color-text-muted); }
  .tg-sort { display: flex; gap: 4px; }

  .tg-card { cursor: pointer; display: flex; flex-direction: column; transition: all var(--transition), transform 0.2s ease; }
  .tg-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }

  .tg-card-top { padding: 20px; flex: 1; }
  .tg-card-name { font-size: 15px; font-weight: 700; margin-bottom: 6px; line-height: 1.3; }
  .tg-card-desc { font-size: 12px; color: var(--color-text-muted); line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: 12px; }
  .tg-card-author { font-size: 11px; color: var(--color-text-dim); margin-bottom: 12px; }
  .tg-card-author strong { color: var(--color-text-muted); }

  .tg-nodes { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 12px; }
  .tg-node-badge { padding: 2px 8px; border-radius: 12px; font-size: 10px; background: var(--color-bg-tertiary); border: 1px solid var(--color-border); color: var(--color-text-muted); display: inline-flex; align-items: center; gap: 3px; }

  .tg-card-meta { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
  .tg-complexity { padding: 3px 10px; border-radius: 12px; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.3px; }
  .tg-views { font-size: 11px; color: var(--color-text-dim); display: flex; align-items: center; gap: 4px; }

  .tg-card-footer { padding: 12px 20px; border-top: 1px solid var(--color-border); display: flex; justify-content: flex-end; }

  .tg-expanded { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--color-border); animation: fadeIn 0.2s ease; }
  .tg-expanded-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: var(--color-text-muted); margin-bottom: 8px; margin-top: 16px; }
  .tg-expanded-title:first-child { margin-top: 0; }
  .tg-expanded-desc { font-size: 13px; color: var(--color-text-muted); line-height: 1.6; }
  .tg-expanded-nodes { display: flex; flex-wrap: wrap; gap: 6px; }
  .tg-expanded-node { padding: 6px 12px; border-radius: var(--radius-sm); font-size: 12px; background: var(--color-bg-tertiary); border: 1px solid var(--color-border); display: inline-flex; align-items: center; gap: 5px; }
  .tg-setup-step { display: flex; gap: 10px; align-items: flex-start; padding: 8px 0; font-size: 12px; color: var(--color-text-muted); }
  .tg-step-num { width: 22px; height: 22px; border-radius: 50%; background: rgba(255,109,90,0.15); color: var(--color-n8n); display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; flex-shrink: 0; }

  @keyframes fadeIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
`;

/* ──────────────────────────── component ──────────────────────────── */
function TemplateGallery() {
  const [search, setSearch] = useState("");
  const [activeTask, setActiveTask] = useState("All");
  const [activeComplexity, setActiveComplexity] = useState("All");
  const [sortMode, setSortMode] = useState<SortMode>("popular");
  const [expandedId, setExpandedId] = useState<string | null>(null);
  const [toast, setToast] = useState<{ msg: string; type: string } | null>(null);

  const { app, isConnected, error } = useApp({
    appInfo: { name: "n8n-template-gallery", version: "1.0.0" },
    capabilities: {},
    onAppCreated: (createdApp) => {
      createdApp.ontoolresult = () => {
        // Gallery is all client-side data — nothing to hydrate from server
      };
    },
  });

  useHostStyles(app, app?.getHostContext());

  const showToast = useCallback((msg: string, type = "success") => {
    setToast({ msg, type });
    setTimeout(() => setToast(null), 3000);
  }, []);

  const sendMessage = useCallback((text: string) => {
    try {
      app?.sendMessage({ text });
      showToast("Message sent");
    } catch {
      showToast(text, "success");
    }
  }, [app, showToast]);

  const handleDeploy = useCallback((e: React.MouseEvent, t: Template) => {
    e.stopPropagation();
    sendMessage(`Deploy template ${t.id} to my n8n instance`);
  }, [sendMessage]);

  const filtered = useMemo(() => {
    let list = [...TEMPLATES];

    // Search
    if (search.trim()) {
      const q = search.toLowerCase();
      list = list.filter(
        (t) =>
          t.name.toLowerCase().includes(q) ||
          t.description.toLowerCase().includes(q) ||
          t.nodes.some((n) => n.toLowerCase().includes(q)) ||
          t.author.toLowerCase().includes(q)
      );
    }

    // Task
    if (activeTask !== "All") {
      list = list.filter((t) => t.task === activeTask);
    }

    // Complexity
    if (activeComplexity !== "All") {
      list = list.filter((t) => t.complexity === activeComplexity);
    }

    // Sort
    switch (sortMode) {
      case "popular":
        list.sort((a, b) => b.views - a.views);
        break;
      case "newest":
        list.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
        break;
      case "most-nodes":
        list.sort((a, b) => b.nodes.length - a.nodes.length);
        break;
    }

    return list;
  }, [search, activeTask, activeComplexity, sortMode]);

  const formatViews = (n: number) => n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);

  return (
    <>
      <style>{css}</style>
      <div className="page">
        {/* Header */}
        <div className="page-header">
          <div>
            <div className="page-title">
              <span className="icon">📦</span>
              n8n Template Gallery
            </div>
            <div className="page-subtitle">2,700+ ready-to-use workflows</div>
          </div>
          <div className="stats-row" style={{ marginBottom: 0 }}>
            <div className="stat-card accent" style={{ minWidth: 100 }}>
              <div className="stat-value">{TEMPLATES.length}</div>
              <div className="stat-label">Featured</div>
            </div>
            <div className="stat-card" style={{ minWidth: 100 }}>
              <div className="stat-value">{filtered.length}</div>
              <div className="stat-label">Showing</div>
            </div>
          </div>
        </div>

        {/* Search */}
        <div className="tg-search-bar search-box">
          <span className="search-icon">🔍</span>
          <input
            className="input"
            placeholder="Search templates... e.g., 'slack notification', 'email automation'"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
          />
        </div>

        {/* Filters */}
        <div className="tg-filters">
          <div className="tg-filter-section">
            <div className="tg-filter-label">Task Category</div>
            <div className="tg-pills">
              {ALL_TASKS.map((t) => (
                <button
                  key={t}
                  className={`tg-pill${activeTask === t ? " active" : ""}`}
                  onClick={() => setActiveTask(t)}
                >
                  <span>{TASK_ICONS[t] || "📋"}</span> {t}
                </button>
              ))}
            </div>
          </div>
          <div className="tg-filter-section">
            <div className="tg-filter-label">Complexity</div>
            <div className="tg-pills">
              {["All", "Beginner", "Intermediate", "Advanced"].map((c) => (
                <button
                  key={c}
                  className={`tg-pill${activeComplexity === c ? " active" : ""}`}
                  onClick={() => setActiveComplexity(c)}
                >
                  {c === "All" ? "🎯" : c === "Beginner" ? "🟢" : c === "Intermediate" ? "🟡" : "🔴"} {c}
                </button>
              ))}
            </div>
          </div>
        </div>

        {/* Toolbar */}
        <div className="tg-toolbar">
          <span className="tg-result-count">
            {filtered.length} template{filtered.length !== 1 ? "s" : ""} found
          </span>
          <div className="tg-sort">
            {([["popular", "🔥 Popular"], ["newest", "🆕 Newest"], ["most-nodes", "🔗 Most Nodes"]] as const).map(
              ([key, label]) => (
                <button
                  key={key}
                  className={`btn btn-sm ${sortMode === key ? "btn-primary" : "btn-ghost"}`}
                  onClick={() => setSortMode(key)}
                >
                  {label}
                </button>
              )
            )}
          </div>
        </div>

        {/* Grid */}
        {filtered.length === 0 ? (
          <div className="empty-state">
            <div className="icon">🔍</div>
            <h3>No templates found</h3>
            <p>Try adjusting your search query or filters.</p>
          </div>
        ) : (
          <div className="grid grid-3">
            {filtered.map((t) => {
              const isExpanded = expandedId === t.id;
              return (
                <div
                  key={t.id}
                  className="card tg-card"
                  onClick={() => setExpandedId(isExpanded ? null : t.id)}
                  style={isExpanded ? { borderColor: "var(--color-n8n)", gridColumn: "1 / -1" } : undefined}
                >
                  <div className="tg-card-top">
                    <div className="tg-card-name">{t.name}</div>
                    {!isExpanded && <div className="tg-card-desc">{t.description}</div>}
                    <div className="tg-card-author">
                      by <strong>{t.author}</strong>
                    </div>

                    {/* Node badges */}
                    <div className="tg-nodes">
                      {(isExpanded ? t.nodes : t.nodes.slice(0, 4)).map((n) => (
                        <span key={n} className="tg-node-badge">
                          {NODE_ICONS[n] || "⬡"} {n}
                        </span>
                      ))}
                      {!isExpanded && t.nodes.length > 4 && (
                        <span className="tg-node-badge">+{t.nodes.length - 4}</span>
                      )}
                    </div>

                    {/* Meta */}
                    <div className="tg-card-meta">
                      <span
                        className="tg-complexity"
                        style={{
                          background: `${COMPLEXITY_COLORS[t.complexity]}20`,
                          color: COMPLEXITY_COLORS[t.complexity],
                        }}
                      >
                        {t.complexity}
                      </span>
                      <span className="tg-views">👁 {formatViews(t.views)}</span>
                    </div>

                    {/* Expanded details */}
                    {isExpanded && (
                      <div className="tg-expanded">
                        <div className="tg-expanded-title">Description</div>
                        <div className="tg-expanded-desc">{t.fullDescription}</div>

                        <div className="tg-expanded-title">Nodes Used ({t.nodes.length})</div>
                        <div className="tg-expanded-nodes">
                          {t.nodes.map((n) => (
                            <span key={n} className="tg-expanded-node">
                              {NODE_ICONS[n] || "⬡"} {n}
                            </span>
                          ))}
                        </div>

                        <div className="tg-expanded-title">Setup Instructions</div>
                        {t.setupSteps.map((step, i) => (
                          <div key={i} className="tg-setup-step">
                            <span className="tg-step-num">{i + 1}</span>
                            <span>{step}</span>
                          </div>
                        ))}
                      </div>
                    )}
                  </div>

                  {/* Footer */}
                  <div className="tg-card-footer">
                    <button
                      className="btn btn-primary btn-sm"
                      onClick={(e) => handleDeploy(e, t)}
                    >
                      🚀 Deploy
                    </button>
                  </div>
                </div>
              );
            })}
          </div>
        )}
      </div>

      {/* Toast */}
      {toast && <div className={`toast ${toast.type}`}>{toast.msg}</div>}
    </>
  );
}

/* ──────────────────────────── mount ──────────────────────────── */
const root = createRoot(document.getElementById("root")!);
root.render(<TemplateGallery />);
