#!/usr/bin/env node
// file: bin/glide-ui.js
import { Command, InvalidArgumentError } from "commander";
import path from "node:path";
import process from "node:process";
import { fileURLToPath, pathToFileURL } from "node:url";
import { promises as fs } from "node:fs";
import pc from "picocolors";
import {
  discoverAllComponents,
  getComponentInfo,
} from "../dist/cli/component-discovery.js";
import { loadDesignEngineer } from "../dist/cli/component-discovery.js";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Read package.json version dynamically
const packageJsonPath = path.join(__dirname, "..", "package.json");
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
const version = packageJson.version;

// --- Configuration -----------------------------------------------------------
// Templates are now discovered dynamically from template files

// --- Utilities ---------------------------------------------------------------
async function discoverTemplates() {
  const templatesDir = path.join(__dirname, "templates");

  try {
    const files = await fs.readdir(templatesDir);
    const templateFiles = files.filter((file) => file.endsWith("-template.js"));

    const templates = {};

    for (const file of templateFiles) {
      const templateType = file.replace("-template.js", "");
      const filePath = path.join(templatesDir, file);

      try {
        const mod = await import(pathToFileURL(filePath).href);

        if (mod.metadata) {
          // Template has metadata
          // Look for the actual template export in the module
          let templateKey = null;
          for (const key of Object.keys(mod)) {
            if (key.includes("Template") && typeof mod[key] === "string") {
              templateKey = key;
              break;
            }
          }

          if (templateKey) {
            templates[templateType] = {
              exportKey: templateKey,
              type: templateType, // Add the type derived from filename
              ...mod.metadata,
            };
          } else {
            console.warn(`Warning: Could not find template export in ${file}`);
          }
        }
      } catch (err) {
        console.warn(
          `Warning: Could not load template ${file}: ${err.message}`,
        );
      }
    }

    return templates;
  } catch (err) {
    throw new Error(`Failed to discover templates: ${err.message}`);
  }
}

function resolveTemplateFile(templateType) {
  return path.join(__dirname, "templates", `${templateType}-template.js`);
}

async function loadTemplateSource(templateType, templates) {
  const file = resolveTemplateFile(templateType);
  try {
    const mod = await import(pathToFileURL(file).href);
    const key = templates[templateType].exportKey;
    const src = mod?.[key];
    if (!src || typeof src !== "string") {
      throw new Error(
        `Missing export "${key}" from ${path.relative(process.cwd(), file)}`,
      );
    }
    return src;
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    throw new Error(`Failed to load template "${templateType}": ${msg}`);
  }
}

async function loadDefaultTemplateSource() {
  const file = path.join(__dirname, "templates", "default-screen-template.js");
  try {
    const mod = await import(pathToFileURL(file).href);
    const src = mod?.defaultTemplate;
    if (!src || typeof src !== "string") {
      throw new Error(
        `Missing export "defaultTemplate" from ${path.relative(process.cwd(), file)}`,
      );
    }
    return src;
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    throw new Error(`Failed to load default template: ${msg}`);
  }
}

function validateTemplate(value) {
  // Don't throw error for unknown templates - we'll handle them with default template
  return value;
}

function isKnownTemplate(templateType, templates) {
  return Object.keys(templates).includes(templateType);
}

// --- CLI ---------------------------------------------------------------------
const program = new Command()
  .name("glide-ui")
  .description("Glide UI CLI")
  .version(version);

program
  .command("create:screen")
  .description("Create a new screen from a prompt")
  .option(
    "-p, --prompt <prompt>",
    "The prompt describing the screen to generate",
  )
  .option("-s, --schema <schema>", "Schema for the screen's data (optional)")
  .option(
    "--model <model>",
    "Model to use for code generation (claude-haiku-4-5, gpt-5, v0-1.5-md, etc)",
    "claude-haiku-4-5",
  )
  .option(
    "--planning-model <model>",
    "Model to use for planning step (claude-haiku-4-5, gpt-5, v0-1.5-md, etc)",
    "claude-haiku-4-5",
  )
  .action(async (opts) => {
    if (!opts.prompt) {
      console.error(
        pc.red("❌ Error: Prompt is required. Use -p or --prompt to specify."),
      );
      process.exitCode = 1;
      return;
    }

    const model = opts.model || "claude-haiku-4-5";
    const planningModel = opts.planningModel || "claude-haiku-4-5";

    const { screenGenerator } = await import("../dist/cli/generate-screen.js");
    const result = await screenGenerator({
      prompt: opts.prompt,
      schema: opts.schema,
      model: model,
      planningModel: planningModel,
    });
    console.log(JSON.stringify(result, null, 2));
  });

program
  .command("screens:generate")
  .description("Generate a new screen from a template (DEPRECATED)")
  .option(
    "--type <type>",
    "Template type (run 'glide-ui templates:list' to see available templates)",
    validateTemplate,
    "collection-default",
  )
  .option(
    "--output <path>",
    "Full output file path (e.g. src/screens/office_locations.tsx)",
    "src/screens/items.tsx",
  )
  .addHelpText(
    "after",
    `
⚠️  DEPRECATION WARNING: This command is deprecated and will be removed in a future version.
    Please use 'screens:generate-ai' for AI-powered screen generation instead.

Examples:
  $ glide-ui screens:generate --type collection-default --output src/screens/products.tsx

Note: Unknown template types will create a default page structure that you can customize.
`,
  )
  .action(async (opts) => {
    console.log(
      pc.yellow(
        "⚠️  DEPRECATION WARNING: The 'screens:generate' command is deprecated and will be removed in a future version.",
      ),
    );
    console.log(
      pc.yellow(
        "    Please use 'screens:generate-ai' for AI-powered screen generation instead.\n",
      ),
    );

    const templateType = String(opts.type);
    const outputPath = String(opts.output);

    // Resolve the full file path
    const filePath = path.resolve(outputPath);
    const fileDir = path.dirname(filePath);

    try {
      // Discover available templates
      const templates = await discoverTemplates();
      // Ensure destination directory exists
      await fs.mkdir(fileDir, { recursive: true });

      // Refuse to overwrite
      try {
        await fs.access(filePath);
        // File exists
        console.log(pc.yellow(`⚠ Template already exists at: ${outputPath}`));
        console.log(pc.dim("No changes made."));
        return;
      } catch {
        // File doesn't exist, we can proceed
      }

      // Load and render
      let templateSource;
      let label;
      let isUnknownTemplate = false;

      if (isKnownTemplate(templateType, templates)) {
        templateSource = await loadTemplateSource(templateType, templates);
        label = templates[templateType].title;
      } else {
        templateSource = await loadDefaultTemplateSource();
        label = "Default Page";
        isUnknownTemplate = true;
        console.log(
          pc.yellow(
            `⚠ Template "${templateType}" not recognized. Creating default template instead.`,
          ),
        );
      }

      const rendered = templateSource; // Templates no longer use dynamic component names

      // Write file
      await fs.writeFile(filePath, rendered, "utf8");

      console.log(
        pc.green(`✅ Created new ${label} screen file at: ${outputPath}`),
      );
    } catch (error) {
      console.error(pc.red(`❌ Error creating template: ${error.message}`));
    }
  });

program
  .command("templates:list")
  .description(
    "List available templates with descriptions and use cases (DEPRECATED)",
  )
  .addHelpText(
    "after",
    `
⚠️  DEPRECATION WARNING: This command is deprecated and will be removed in a future version.
    Please use 'screens:generate-ai' for AI-powered screen generation instead.

Examples:
  $ glide-ui templates:list

This command provides detailed information about each template including:
- Template type (for use with --type flag)
- Description and capabilities
- Common use cases and applications

Output is in markdown format for easy reading by both humans and LLMs.
`,
  )
  .action(async () => {
    console.log(
      pc.yellow(
        "⚠️  DEPRECATION WARNING: The 'templates:list' command is deprecated and will be removed in a future version.",
      ),
    );
    console.log(
      pc.yellow(
        "    Please use 'screens:generate-ai' for AI-powered screen generation instead.\n",
      ),
    );

    try {
      const allTemplates = await discoverTemplates();

      console.log("# Glide UI Screen Templates\n");
      console.log("Available templates for creating screens:\n");

      Object.entries(allTemplates).forEach(([type, config]) => {
        console.log(`## ${config.title}\n`);
        console.log(`Type: \`${type}\`\n`);
        console.log(`${config.description}\n`);
      });
    } catch (error) {
      console.error(pc.red(`❌ Error listing templates: ${error.message}`));
    }
  });

// Get Design Engineer command
program
  .command("design-engineer")
  .description("Get the Design Engineer prompt")
  .action(() => {
    try {
      const designEngineer = loadDesignEngineer();
      console.log(designEngineer);
    } catch (error) {
      console.error(
        pc.red(`❌ Error loading design engineer prompt: ${error.message}`),
      );
    }
  });

// Component commands
program
  .command("components:list")
  .description("List all available components")
  .option("--json", "Output as JSON for LLMs")
  .option("--category <category>", "Filter by category")
  .action((opts) => {
    try {
      const components = discoverAllComponents();

      // Filter by category if specified
      let filtered = components;
      if (opts.category) {
        filtered = components.filter((c) => c.category === opts.category);
      }

      if (opts.json) {
        // JSON output for LLMs
        const output = {
          total: filtered.length,
          categories: [...new Set(filtered.map((c) => c.category))].sort(),
          components: filtered,
        };
        console.log(JSON.stringify(output, null, 2));
      } else {
        // Markdown output (default)
        console.log("# Glide UI Components\n");

        // Group by category
        const grouped = {};
        filtered.forEach((comp) => {
          if (!grouped[comp.category]) {
            grouped[comp.category] = [];
          }
          grouped[comp.category].push(comp);
        });

        Object.entries(grouped)
          .sort()
          .forEach(([category, comps]) => {
            console.log(
              `\n## ${category.charAt(0).toUpperCase() + category.slice(1)}\n`,
            );
            comps.forEach((comp) => {
              const displayName = comp.name;
              const description =
                comp.description || "No description available";
              console.log(`- **${displayName}** (${comp.id}): ${description}`);
            });
          });

        console.log(`\n**Total:** ${filtered.length} components\n`);
      }
    } catch (err) {
      console.error(pc.red(`❌ Error: ${err.message}`));
      process.exitCode = 1;
    }
  });

// MCP Server command
program
  .command("mcp")
  .description("Start the Glide UI MCP (Model Context Protocol) server")
  .addHelpText(
    "after",
    `
This starts an MCP server that allows AI assistants like Claude to discover
and understand Glide UI components programmatically.

The server provides tools for:
- Discovering all available components and their documentation
- Getting detailed component information including props and usage examples
- Understanding component categories and relationships

Example:
  $ npx @glide/glide-ui mcp

For more information about MCP, visit: https://modelcontextprotocol.io
`,
  )
  .action(async () => {
    try {
      // Import and start the MCP server
      const { GlideUIMCPServer } = await import("../dist/mcp/server.js");
      const server = new GlideUIMCPServer();
      await server.start();
    } catch (error) {
      console.error(pc.red(`❌ Failed to start MCP server: ${error.message}`));
      process.exit(1);
    }
  });

program
  .command("components:info")
  .description("Get detailed information about components")
  .option(
    "-t, --targets <ids>",
    "Comma-separated list of component ids",
    (value) => value.split(",").map((s) => s.trim()),
  )
  .option("--json", "Output as JSON for LLMs")
  .option(
    "--verbose-props",
    "Show all props individually (includes all event handlers, aria-*, data-*)",
  )
  .action((opts) => {
    try {
      const targets = opts.targets || [];

      if (targets.length === 0) {
        console.error(
          pc.red("❌ Please specify component names using -t or --targets"),
        );
        console.log(
          pc.dim('\nExample: glide-ui components:info -t "button,chrome,list"'),
        );
        console.log(
          pc.dim(
            'Run "glide-ui components:list" to see all available components',
          ),
        );
        process.exitCode = 1;
        return;
      }

      const results = [];
      const notFound = [];

      for (const name of targets) {
        const info = getComponentInfo(name, {
          verbose: opts.verboseProps,
        });
        if (info) {
          results.push(info);
        } else {
          notFound.push(name);
        }
      }

      if (notFound.length > 0) {
        console.error(
          pc.red(`❌ Components not found: ${notFound.join(", ")}`),
        );
        console.log(
          pc.dim(
            '\nRun "glide-ui components:list" to see all available components',
          ),
        );
      }

      if (results.length === 0) {
        process.exitCode = 1;
        return;
      }

      if (opts.json) {
        // JSON output for LLMs
        console.log(JSON.stringify(results, null, 2));
      } else {
        // Markdown output (default)
        console.log("# Component Information\n");

        results.forEach((info, index) => {
          if (index > 0) console.log("\n---\n");

          if (info.type === "group") {
            // Markdown output for grouped components
            console.log(`## ${info.name} Components\n`);
            console.log(`**Import from:** \`${info.importPath}\`\n`);
            console.log("### Available components:\n");

            // Handle components as dictionary
            const componentsArray = Object.entries(info.components).map(
              ([id, comp]) => ({ id, ...comp }),
            );

            componentsArray.forEach((comp) => {
              console.log(`- **${comp.name}** (\`${comp.id}\`)`);
              console.log(`  - Import: \`${comp.import}\``);
              if (comp.description) {
                console.log(
                  `  - Description: ${comp.description.substring(0, 80)}...`,
                );
              }
            });

            console.log(
              `\n**Total:** ${componentsArray.length} components in ${info.name}`,
            );

            // Show full README if available
            if (info.info) {
              console.log("\n---\n");
              console.log(info.info);
            }
          } else {
            // Markdown output for single components
            console.log(`## ${info.name}\n`);
            console.log(`**Category:** ${info.category}`);
            console.log(`**Import:** \`${info.importPath}\`\n`);
            console.log("### Usage:\n");
            console.log("```typescript");
            console.log(info.import);
            console.log("```\n");

            if (info.description) {
              console.log("### Description:\n");
              console.log(info.description + "\n");
            }

            // Show full README if available
            if (info.info) {
              console.log("### Full Documentation:\n");
              console.log(info.info + "\n");
            }

            if (info.props && Object.keys(info.props).length > 0) {
              console.log("### Props:\n");
              console.log("| Prop | Type | Required | Description |");
              console.log("|------|------|----------|-------------|");

              Object.entries(info.props).forEach(([propName, propInfo]) => {
                const type = propInfo.type || "any";
                const required = propInfo.required ? "Yes" : "No";
                const description = propInfo.description || "-";
                console.log(
                  `| \`${propName}\` | \`${type}\` | ${required} | ${description} |`,
                );
              });
              console.log();
            }

            if (info.example) {
              console.log("### Example:\n");
              console.log("```tsx");
              console.log(info.example);
              console.log("```");
            }
          }
        });

        console.log();
      }
    } catch (err) {
      console.error(pc.red(`❌ Error: ${err.message}`));
      process.exitCode = 1;
    }
  });

// Generate command for natural language component composition
program
  .command("components:generate")
  .description("Generate components using natural language")
  .option("-p, --prompt <prompt>", "The prompt describing what to generate")
  .option(
    "--provider <provider>",
    "LLM provider (anthropic|openai|mock)",
    "anthropic",
  )
  .option("--no-cache", "Skip cache lookup")
  .option("--json", "Output as JSON for LLMs")
  .option("--max-tokens <number>", "Max tokens for response", parseInt, 2000)
  .option(
    "--smart-selection",
    "Use LLM for component selection (slower but potentially more accurate)",
  )
  .action(async (opts) => {
    if (!opts.prompt) {
      console.error(
        pc.red("❌ Error: Prompt is required. Use -p or --prompt to specify."),
      );
      console.log(
        pc.dim(
          '\nExample: npx glide-ui components:generate -p "button with icon"',
        ),
      );
      process.exitCode = 1;
      return;
    }

    try {
      const { ComponentComposer } = await import("../dist/cli/composer.js");
      const composer = new ComponentComposer();
      const result = await composer.compose(opts.prompt, opts);

      if (opts.json) {
        console.log(JSON.stringify(result, null, 2));
      } else {
        console.log(result.code);
      }
    } catch (error) {
      console.error(pc.red(`❌ Composition failed: ${error.message}`));
      process.exitCode = 1;
    }
  });

// Generate screens using AI (Claude agent)
program
  .command("screens:generate-ai")
  .description("Generate complete UI screens using Claude AI")
  .option(
    "-p, --prompt <prompt>",
    "The prompt describing the screen to generate",
  )
  .option("--json", "Output as JSON for LLMs")
  .option("--max-tokens <number>", "Max tokens for response", parseInt, 4000)
  .option(
    "--model <model>",
    "Model to use (claude-haiku-4-5, claude-sonnet-4-5, claude-opus-4-1)",
    "claude-haiku-4-5",
  )
  .addHelpText(
    "after",
    `
Examples:
  $ glide-ui screens:generate-ai -p "dashboard showing sales metrics with charts"
  $ glide-ui screens:generate-ai -p "product listing page with search and filters" --model claude-sonnet-4-5
  $ glide-ui screens:generate-ai -p "user profile detail page" --model claude-opus-4-1

Note: This command uses Claude AI to generate complete screen components following
Glide's design system principles. The generated code includes React Router loaders,
TypeScript types, and follows all design guidelines from the DESIGN_ENGINEER prompt.

Supported models:
  - claude-haiku-4-5 (default, fastest)
  - claude-sonnet-4-5 (balanced)
  - claude-opus-4-1 (most capable)
`,
  )
  .action(async (opts) => {
    if (!opts.prompt) {
      console.error(
        pc.red("❌ Error: Prompt is required. Use -p or --prompt to specify."),
      );
      console.log(
        pc.dim(
          '\nExample: glide-ui screens:generate-ai -p "dashboard with sales charts"',
        ),
      );
      process.exitCode = 1;
      return;
    }

    const supportedModels = [
      "claude-haiku-4-5",
      "claude-sonnet-4-5",
      "claude-opus-4-1",
    ];
    if (opts.model && !supportedModels.includes(opts.model)) {
      console.error(
        pc.red(
          `❌ Error: Unsupported model "${opts.model}". Supported models are: ${supportedModels.join(", ")}`,
        ),
      );
      process.exitCode = 1;
      return;
    }

    try {
      const { ScreenComposer } = await import("../dist/cli/screen-composer.js");
      const composer = new ScreenComposer();
      const result = await composer.compose(opts.prompt, opts);

      if (opts.json) {
        // Output only JSON to stdout, ensure it's valid JSON
        const jsonOutput = JSON.stringify(result, null, 2);
        process.stdout.write(jsonOutput + "\n");
      } else {
        // Output only the code to stdout
        process.stdout.write(result.code + "\n");
      }
    } catch (error) {
      // Errors go to stderr, not stdout
      console.error(pc.red(`❌ Screen generation failed: ${error.message}`));
      process.exitCode = 1;
    }
  });

// Interactive mode (default when no command)
program.action(() => {
  console.log(pc.bold(pc.cyan("\n🎨 Glide UI CLI\n")));
  console.log("Available commands:\n");
  console.log(
    `  ${pc.white("components:list")}              List all available components`,
  );
  console.log(
    `  ${pc.white("components:info")} -t <names>   Get component details`,
  );
  console.log(
    `  ${pc.white("components:generate")} -p <prompt> Generate components using natural language`,
  );
  console.log(
    `  ${pc.white("screens:generate-ai")} -p <prompt> Generate screens using Claude AI`,
  );
  console.log(
    `  ${pc.dim("screens:generate")}             Create from template ${pc.dim("(deprecated)")}`,
  );
  console.log(
    `  ${pc.dim("templates:list")}               List available templates ${pc.dim("(deprecated)")}`,
  );
  console.log(
    `  ${pc.white("mcp")}                          Start MCP server for AI assistants\n`,
  );
  console.log(pc.dim("Examples:"));
  console.log(pc.dim("  npx glide-ui components:list"));
  console.log(pc.dim("  npx glide-ui components:list --json"));
  console.log(pc.dim('  npx glide-ui components:info -t "button"'));
  console.log(pc.dim('  npx glide-ui components:info -t "button,chrome,list"'));
  console.log(
    pc.dim('  npx glide-ui components:info -t "charts/area-chart" --json'),
  );
  console.log(
    pc.dim('  npx glide-ui components:generate -p "button with icon"'),
  );
  console.log(
    pc.dim('  npx glide-ui screens:generate-ai -p "dashboard with charts"'),
  );
  console.log(pc.dim("  npx glide-ui mcp\n"));
  console.log(pc.dim('Run "glide-ui <command> --help" for more information\n'));
});

await program.parseAsync(process.argv);
