"use strict";
/**
 * React Frontend Generator
 * Generates React components with TypeScript from workflow definitions
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReactGenerator = void 0;
const path = __importStar(require("path"));
class ReactGenerator {
    id = 'react-frontend';
    name = 'React Frontend Generator';
    description = 'Generates React components with TypeScript';
    framework = 'react';
    generate(context) {
        const { workflow, extendedWorkflow, outputDirectory } = context;
        const files = [];
        // Use extended workflow if available
        if (extendedWorkflow) {
            return this.generateFromExtendedWorkflow(extendedWorkflow, outputDirectory);
        }
        // Legacy generation from stack components
        // Find all frontend components in the stack
        const frontendComponents = this.getFrontendComponents(workflow);
        if (frontendComponents.length === 0) {
            return { files: [] };
        }
        // Generate each frontend stack component
        for (const [componentName, component] of frontendComponents) {
            // Generate UI components
            if (component.components) {
                for (const uiComponent of component.components) {
                    files.push(...this.generateUiComponent(uiComponent, componentName, outputDirectory));
                }
            }
            // Generate API client
            files.push(this.generateApiClient(componentName, workflow, outputDirectory));
            // Generate package.json for the frontend
            files.push(this.generatePackageJson(componentName, outputDirectory));
            // Generate tsconfig.json
            files.push(this.generateTsConfig(componentName, outputDirectory));
            // Generate tsconfig.node.json (for Vite build tooling)
            files.push(this.generateTsConfigNode(componentName, outputDirectory));
            // Generate vite.config.ts (will be enhanced by test generator if tests are defined)
            files.push(this.generateViteConfig(componentName, workflow, outputDirectory));
            // Generate vite-env.d.ts (Vite type definitions)
            files.push(this.generateViteEnvTypes(componentName, outputDirectory));
            // Generate index.html
            files.push(this.generateIndexHtml(componentName, workflow, outputDirectory));
            // Generate main App component
            files.push(this.generateAppComponent(componentName, component, outputDirectory));
        }
        return { files };
    }
    /**
     * Generate frontend from extended workflow schema
     * Creates full React app with pages, routing, theme, and API client
     */
    generateFromExtendedWorkflow(workflow, outputDir) {
        const files = [];
        const componentName = 'frontend'; // Default component name
        if (!workflow.models || Object.keys(workflow.models).length === 0) {
            return { files: [], warnings: ['No models defined - skipping frontend generation'] };
        }
        // Always generate theme configuration (Tailwind CSS + PostCSS)
        // Custom theme from workflow.design.theme can be added later
        files.push(this.generateThemeConfig(workflow.design?.theme, componentName, outputDir));
        files.push(this.generateTailwindConfig(workflow.design?.theme, componentName, outputDir));
        files.push(this.generatePostCssConfig(componentName, outputDir));
        // Generate CRUD pages for each model
        for (const [modelName, modelDef] of Object.entries(workflow.models)) {
            files.push(...this.generateModelPages(modelName, modelDef, workflow, outputDir));
        }
        // Generate custom pages from ui.pages
        if (workflow.ui?.pages) {
            for (const page of workflow.ui.pages) {
                files.push(this.generateCustomPage(page, workflow, outputDir));
            }
        }
        // Generate forms from ui.forms
        if (workflow.ui?.forms) {
            for (const form of workflow.ui.forms) {
                files.push(this.generateFormComponent(form, workflow, outputDir));
            }
        }
        // Generate enhanced API client with all CRUD operations
        files.push(this.generateEnhancedApiClient(workflow, componentName, outputDir));
        // Generate ProtectedRoute component if auth is used
        const hasAuthRoutes = workflow.ui?.pages?.some((p) => p.auth === 'required') ||
            workflow.ui?.navigation?.some((n) => n.auth === 'required');
        if (hasAuthRoutes) {
            files.push(this.generateProtectedRoute(componentName, outputDir));
        }
        // Generate main App with routing
        files.push(this.generateEnhancedApp(workflow, componentName, outputDir));
        // Generate infrastructure files
        files.push(this.generatePackageJsonWithRouter(componentName, workflow, outputDir));
        files.push(this.generateTsConfig(componentName, outputDir));
        files.push(this.generateTsConfigNode(componentName, outputDir));
        files.push(this.generateViteConfigExtended(componentName, workflow, outputDir));
        files.push(this.generateViteEnvTypes(componentName, outputDir));
        files.push(this.generateIndexHtmlExtended(componentName, workflow, outputDir));
        return { files };
    }
    getFrontendComponents(workflow) {
        const components = [];
        for (const [name, component] of Object.entries(workflow.stack)) {
            const comp = component;
            if (comp.type === 'frontend' && comp.technology === 'react') {
                components.push([name, comp]);
            }
        }
        return components;
    }
    generateUiComponent(component, stackName, outputDir) {
        const files = [];
        const componentName = component.name;
        // Generate component TypeScript file
        files.push({
            path: path.join(outputDir, stackName, 'src', 'components', `${componentName}.tsx`),
            content: this.generateComponentTsx(component),
            overwrite: true,
        });
        // Generate CSS module if styling is specified
        if (component.generate?.styling === 'css-modules') {
            files.push({
                path: path.join(outputDir, stackName, 'src', 'components', `${componentName}.module.css`),
                content: this.generateComponentCss(component),
                overwrite: true,
            });
        }
        // Generate types file
        files.push({
            path: path.join(outputDir, stackName, 'src', 'components', `${componentName}.types.ts`),
            content: this.generateComponentTypes(component),
            overwrite: true,
        });
        return files;
    }
    generateComponentTsx(component) {
        const componentName = component.name;
        const hasDetailedSteps = component.actions?.some((action) => action.steps?.length > 0);
        let code = `import React, { useState } from 'react';\n`;
        // Only import apiClient if actions have API call steps
        if (hasDetailedSteps) {
            code += `import { apiClient } from '../api/client';\n`;
        }
        code += `import { ${componentName}Props } from './${componentName}.types';\n\n`;
        // Add generated marker and description
        code += `/**\n`;
        code += ` * ${componentName} Component\n`;
        if (component.description) {
            code += ` * ${component.description}\n`;
        }
        code += ` *\n`;
        code += ` * @generated by Bangstack - Infrastructure and scaffolding\n`;
        if (!hasDetailedSteps) {
            code += ` * TODO: Implement component logic and UI\n`;
        }
        code += ` */\n`;
        // Generate component
        code += `export const ${componentName}: React.FC<${componentName}Props> = (props) => {\n`;
        // Generate state hooks from YAML (Tier 2: Scaffolding)
        if (component.state) {
            code += `  // State management (from YAML schema)\n`;
            for (const state of component.state) {
                const initial = this.formatInitialValue(state.initial, state.type);
                code += `  const [${state.name}, set${this.capitalize(state.name)}] = useState<${state.type}>(${initial});\n`;
            }
            code += '\n';
        }
        // Generate event handlers
        if (component.actions) {
            for (const action of component.actions) {
                // Check if THIS action has detailed steps (not just any action in the component)
                if (action.steps && action.steps.length > 0) {
                    // Tier 3: Generate full implementation from explicit YAML steps
                    code += this.generateActionHandler(action, component);
                }
                else {
                    // Tier 2: Generate scaffold only
                    code += this.generateActionScaffold(action);
                }
            }
        }
        // Generate minimal JSX scaffold
        code += '  return (\n';
        code += `    <div>\n`;
        code += `      <h2>${componentName}</h2>\n`;
        if (!hasDetailedSteps) {
            code += `      {/* TODO: Implement your UI here */}\n`;
            code += `      <p>Component scaffolding generated from workflow YAML</p>\n`;
        }
        else {
            // If detailed steps provided, generate basic form structure
            if (component.ui) {
                code += this.generateFormJsx(component.ui, false);
            }
        }
        code += '    </div>\n';
        code += '  );\n';
        code += '};\n';
        return code;
    }
    /**
     * Generate action scaffold (Tier 2: empty stub with TODO)
     */
    generateActionScaffold(action) {
        const handlerName = this.toCamelCase(action.id);
        let code = `  // TODO: Implement ${action.id}\n`;
        code += `  const ${handlerName} = async (e: React.FormEvent) => {\n`;
        code += `    e.preventDefault();\n`;
        code += `    // TODO: Add your implementation here\n`;
        code += `    console.warn('${handlerName} not implemented');\n`;
        code += `  };\n\n`;
        return code;
    }
    /**
     * Generate full action handler implementation (Tier 3: only when explicit steps in YAML)
     */
    generateActionHandler(action, component) {
        const handlerName = this.toCamelCase(action.id);
        let code = `  // Implementation from YAML steps for ${action.id}\n`;
        code += `  const ${handlerName} = async (e: React.FormEvent) => {\n`;
        code += '    e.preventDefault();\n\n';
        // Process each step from YAML
        if (action.steps && action.steps.length > 0) {
            for (let i = 0; i < action.steps.length; i++) {
                const step = action.steps[i];
                code += `    // Step ${i + 1}: ${step.id || step.action} (from YAML)\n`;
                switch (step.action) {
                    case 'validate-field':
                        code += this.generateValidationStep(step);
                        break;
                    case 'api-call':
                        code += this.generateApiCallStep(step);
                        break;
                    case 'store-local':
                        code += this.generateStoreLocalStep(step);
                        break;
                    case 'navigate':
                        code += this.generateNavigateStep(step);
                        break;
                    default:
                        code += `    // TODO: Implement ${step.action}\n`;
                }
                code += '\n';
            }
        }
        else {
            // Fallback to old behavior for backward compatibility
            code += this.generateLegacyActionHandler(action, component);
        }
        code += '  };\n\n';
        return code;
    }
    generateValidationStep(step) {
        let code = '';
        if (step.pattern) {
            code += `    if (!/${step.pattern}/.test(${step.field})) {\n`;
            code += `      setError('${step.error || 'Validation failed'}');\n`;
            code += `      return;\n`;
            code += `    }\n`;
        }
        return code;
    }
    generateApiCallStep(step) {
        let code = '';
        // Handle onStart actions (execute before the try block)
        if (step.onStart) {
            code += this.generateInlineAction(step.onStart, 'success');
            code += '\n';
        }
        code += '    try {\n';
        // Determine endpoint from YAML
        const endpointRef = step.api_workflow || step.endpoint;
        const endpointId = typeof endpointRef === 'string' && endpointRef.includes('.')
            ? endpointRef.split('.')[1]
            : endpointRef;
        code += `      const response = await apiClient.${this.toCamelCase(endpointId)}({\n`;
        // Generate payload from YAML
        if (step.payload) {
            for (const [key, value] of Object.entries(step.payload)) {
                const val = this.parseTemplateValue(value);
                code += `        ${key}: ${val},\n`;
            }
        }
        code += '      });\n';
        // Handle success actions
        if (step.onSuccess) {
            for (const successAction of step.onSuccess) {
                code += this.generateInlineAction(successAction, 'success');
            }
        }
        code += '    } catch (error: any) {\n';
        // Handle error actions
        if (step.onError) {
            for (const errorAction of step.onError) {
                code += this.generateInlineAction(errorAction, 'error');
            }
        }
        else {
            code += `      setError(error.message || 'Request failed');\n`;
        }
        code += '    }\n';
        return code;
    }
    generateStoreLocalStep(step) {
        const value = this.parseTemplateValue(step.value);
        return `    localStorage.setItem('${step.key}', ${value});\n`;
    }
    generateNavigateStep(step) {
        return `    window.location.href = '${step.path}';\n`;
    }
    /**
     * Legacy action handler for backward compatibility
     */
    generateLegacyActionHandler(action, component) {
        let code = '';
        // Add client-side validation for required fields
        const requiredFields = component.ui?.fields?.filter((f) => f.validation?.some((v) => v.required)) || [];
        if (requiredFields.length > 0) {
            code += '    // Validate required fields\n';
            for (const field of requiredFields) {
                const stateVar = this.getFieldStateVar(field);
                code += `    if (!${stateVar}) {\n`;
                code += `      setError('Please fill in all required fields');\n`;
                code += `      return;\n`;
                code += `    }\n`;
            }
            code += '\n';
        }
        return code;
    }
    generateInlineAction(action, context = 'success') {
        switch (action.action) {
            case 'set-state': {
                const value = this.parseTemplateValue(action.value, context);
                return `      set${this.capitalize(action.target)}(${value});\n`;
            }
            case 'store-auth-token':
            case 'store-local': {
                // Support both naming conventions
                const tokenValue = this.parseTemplateValue(action.value, context);
                return `      localStorage.setItem('${action.key}', ${tokenValue});\n`;
            }
            case 'redirect':
            case 'navigate':
                // Support both naming conventions
                return `      window.location.href = '${action.path}';\n`;
            default:
                return `      // TODO: Implement ${action.action}\n`;
        }
    }
    generateFormJsx(ui, useCssModules) {
        let jsx = '';
        if (ui.fields) {
            // Add noValidate to allow custom validation logic to run
            jsx += `      <form onSubmit={handleSubmit} noValidate>\n`;
            for (const field of ui.fields) {
                const styleClass = useCssModules ? `styles.${field.type}` : '""';
                if (field.type === 'email' || field.type === 'password') {
                    jsx += `        <div className={${styleClass}}>\n`;
                    jsx += `          <label htmlFor="${field.id}">${field.label}</label>\n`;
                    jsx += `          <input\n`;
                    jsx += `            id="${field.id}"\n`;
                    jsx += `            type="${field.type}"\n`;
                    jsx += `            placeholder="${field.placeholder}"\n`;
                    jsx += `            value={${this.getFieldStateVar(field)}}\n`;
                    jsx += `            onChange={(e) => set${this.capitalize(this.getFieldStateVar(field))}(e.target.value)}\n`;
                    if (field.validation?.some((v) => v.required)) {
                        jsx += `            required\n`;
                    }
                    jsx += `          />\n`;
                    jsx += `        </div>\n`;
                }
                else if (field.type === 'button') {
                    jsx += `        {error && <div className={${useCssModules ? 'styles.error' : '""'}}>{error}</div>}\n`;
                    jsx += `        {isLoading && <div className={${useCssModules ? 'styles.loading' : '""'}}>Loading...</div>}\n`;
                    jsx += `        <button type="submit" className={${styleClass}} disabled={isLoading}>\n`;
                    jsx += `          ${field.label}\n`;
                    jsx += `        </button>\n`;
                }
            }
            jsx += '      </form>\n';
        }
        return jsx;
    }
    generateComponentCss(component) {
        let css = `.container {\n`;
        css += `  max-width: 400px;\n`;
        css += `  margin: 0 auto;\n`;
        css += `  padding: 2rem;\n`;
        css += `}\n\n`;
        if (component.ui?.layout === 'centered-card') {
            css += `.container {\n`;
            css += `  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n`;
            css += `  border-radius: 8px;\n`;
            css += `  background: white;\n`;
            css += `}\n\n`;
        }
        css += `.email, .password {\n`;
        css += `  margin-bottom: 1rem;\n`;
        css += `}\n\n`;
        css += `.email label, .password label {\n`;
        css += `  display: block;\n`;
        css += `  margin-bottom: 0.5rem;\n`;
        css += `  font-weight: 500;\n`;
        css += `}\n\n`;
        css += `.email input, .password input {\n`;
        css += `  width: 100%;\n`;
        css += `  padding: 0.5rem;\n`;
        css += `  border: 1px solid #ddd;\n`;
        css += `  border-radius: 4px;\n`;
        css += `  font-size: 1rem;\n`;
        css += `}\n\n`;
        css += `.button {\n`;
        css += `  width: 100%;\n`;
        css += `  padding: 0.75rem;\n`;
        css += `  background: #007bff;\n`;
        css += `  color: white;\n`;
        css += `  border: none;\n`;
        css += `  border-radius: 4px;\n`;
        css += `  font-size: 1rem;\n`;
        css += `  cursor: pointer;\n`;
        css += `}\n\n`;
        css += `.button:hover {\n`;
        css += `  background: #0056b3;\n`;
        css += `}\n\n`;
        css += `.error {\n`;
        css += `  color: #dc3545;\n`;
        css += `  margin-top: 1rem;\n`;
        css += `  padding: 0.5rem;\n`;
        css += `  background: #f8d7da;\n`;
        css += `  border-radius: 4px;\n`;
        css += `}\n\n`;
        css += `.loading {\n`;
        css += `  text-align: center;\n`;
        css += `  margin-top: 1rem;\n`;
        css += `  color: #6c757d;\n`;
        css += `}\n`;
        return css;
    }
    generateComponentTypes(component) {
        let code = `export interface ${component.name}Props {\n`;
        code += `  // Add props as needed\n`;
        code += `}\n\n`;
        // Generate state interface
        if (component.state) {
            code += `export interface ${component.name}State {\n`;
            for (const state of component.state) {
                code += `  ${state.name}: ${state.type};\n`;
            }
            code += `}\n`;
        }
        return code;
    }
    generateApiClient(stackName, workflow, outputDir) {
        let code = `/**\n * API Client\n * Generated from workflow definition\n */\n\n`;
        // Find the backend API in the stack
        const apiComponent = Object.entries(workflow.stack).find(([_, comp]) => comp.type === 'backend');
        if (apiComponent) {
            const [_apiName, api] = apiComponent;
            code += `const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000';\n\n`;
            code += `class ApiClient {\n`;
            // Generate method for each endpoint
            if (api.endpoints) {
                for (const endpoint of api.endpoints) {
                    const methodName = this.toCamelCase(endpoint.id);
                    const requestBodyType = this.generateRequestType(endpoint);
                    const responseBodyType = this.generateResponseType(endpoint);
                    code += `  async ${methodName}(data: ${requestBodyType}): Promise<${responseBodyType}> {\n`;
                    code += `    const response = await fetch(\`\${API_BASE_URL}${endpoint.path}\`, {\n`;
                    code += `      method: '${endpoint.method}',\n`;
                    code += `      headers: {\n`;
                    code += `        'Content-Type': 'application/json',\n`;
                    code += `      },\n`;
                    code += `      body: JSON.stringify(data),\n`;
                    code += `    });\n\n`;
                    code += `    if (!response.ok) {\n`;
                    code += `      const error = await response.json();\n`;
                    code += `      throw new Error(error.message || 'Request failed');\n`;
                    code += `    }\n\n`;
                    code += `    return response.json();\n`;
                    code += `  }\n\n`;
                }
            }
            code += `}\n\n`;
            code += `export const apiClient = new ApiClient();\n`;
        }
        else {
            code += `export const apiClient = {};\n`;
        }
        return {
            path: path.join(outputDir, stackName, 'src', 'api', 'client.ts'),
            content: code,
            overwrite: true,
        };
    }
    generateRequestType(endpoint) {
        if (!endpoint.request?.body) {
            return 'void';
        }
        let type = '{\n';
        if (endpoint.request.body.properties) {
            for (const [key, prop] of Object.entries(endpoint.request.body.properties)) {
                const p = prop;
                const optional = !endpoint.request.body.required?.includes(key);
                type += `    ${key}${optional ? '?' : ''}: ${this.mapTypeToTs(p.type)};\n`;
            }
        }
        type += '  }';
        return type;
    }
    generateResponseType(endpoint) {
        const successResponse = endpoint.responses?.find((r) => r.status === 200);
        if (!successResponse?.body) {
            return 'void';
        }
        let type = '{\n';
        if (successResponse.body.properties) {
            for (const [key, prop] of Object.entries(successResponse.body.properties)) {
                const p = prop;
                if (p.type === 'object') {
                    type += `    ${key}: any;\n`; // Simplified for now
                }
                else {
                    type += `    ${key}: ${this.mapTypeToTs(p.type)};\n`;
                }
            }
        }
        type += '  }';
        return type;
    }
    generatePackageJson(stackName, outputDir) {
        // Tier 1: Infrastructure - always generated
        // Note: Package manager is inferred from workflow config and technology
        const pkg = {
            name: stackName,
            version: '1.0.0',
            type: 'module',
            description: 'Generated by Bangstack',
            scripts: {
                dev: 'vite',
                build: 'tsc && vite build',
                preview: 'vite preview',
                lint: 'eslint . --ext ts,tsx',
                test: 'vitest',
                'test:ui': 'vitest --ui',
                'test:coverage': 'vitest --coverage',
                'e2e': 'playwright test',
                'e2e:ui': 'playwright test --ui',
            },
            dependencies: {
                react: '^18.2.0',
                'react-dom': '^18.2.0',
            },
            devDependencies: {
                '@types/react': '^18.2.0',
                '@types/react-dom': '^18.2.0',
                '@vitejs/plugin-react': '^4.0.0',
                typescript: '^5.0.0',
                vite: '^4.3.0',
                eslint: '^8.42.0',
                // Testing dependencies
                vitest: '^1.0.0',
                '@testing-library/react': '^14.1.0',
                '@testing-library/jest-dom': '^6.1.0',
                '@testing-library/user-event': '^14.5.0',
                'jsdom': '^23.0.0',
                // E2E testing
                '@playwright/test': '^1.40.0',
            },
        };
        return {
            path: path.join(outputDir, stackName, 'package.json'),
            content: JSON.stringify(pkg, null, 2),
            overwrite: false, // Don't overwrite existing package.json (preserves user changes)
        };
    }
    generateTsConfig(stackName, outputDir) {
        const config = {
            compilerOptions: {
                target: 'ES2020',
                useDefineForClassFields: true,
                lib: ['ES2020', 'DOM', 'DOM.Iterable'],
                module: 'ESNext',
                skipLibCheck: true,
                moduleResolution: 'bundler',
                allowImportingTsExtensions: true,
                resolveJsonModule: true,
                isolatedModules: true,
                noEmit: true,
                jsx: 'react-jsx',
                strict: true,
                noUnusedLocals: true,
                noUnusedParameters: true,
                noFallthroughCasesInSwitch: true,
            },
            include: ['src'],
            exclude: ['src/**/*.test.tsx', 'src/**/*.test.ts', 'e2e'],
            references: [{ path: './tsconfig.node.json' }],
        };
        return {
            path: path.join(outputDir, stackName, 'tsconfig.json'),
            content: JSON.stringify(config, null, 2),
            overwrite: false,
        };
    }
    generateTsConfigNode(stackName, outputDir) {
        const config = {
            compilerOptions: {
                composite: true,
                skipLibCheck: true,
                module: 'ESNext',
                moduleResolution: 'bundler',
                allowSyntheticDefaultImports: true,
            },
            include: ['vite.config.ts', 'vitest.config.ts'],
        };
        return {
            path: path.join(outputDir, stackName, 'tsconfig.node.json'),
            content: JSON.stringify(config, null, 2),
            overwrite: false,
        };
    }
    generateViteConfig(stackName, workflow, outputDir) {
        // Check if testing is configured
        const hasTests = workflow.testing?.unit_tests || workflow.testing?.e2e_tests;
        const config = `import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    port: 5173,
    host: true,
  },${hasTests ? `
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: ['./src/test/setup.ts'],
    exclude: ['**/e2e/**', '**/node_modules/**'],
    coverage: {
      reporter: ['text', 'json', 'html'],
      exclude: [
        'node_modules/',
        'src/test/',
      ],
    },
  },` : ''}
});
`;
        return {
            path: path.join(outputDir, stackName, 'vite.config.ts'),
            content: config,
            overwrite: false,
        };
    }
    generateViteEnvTypes(stackName, outputDir) {
        const types = `/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_API_URL: string
  // Add more env variables as needed
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}
`;
        return {
            path: path.join(outputDir, stackName, 'src', 'vite-env.d.ts'),
            content: types,
            overwrite: false,
        };
    }
    generateIndexHtml(stackName, workflow, outputDir) {
        const html = `<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>${workflow.workflow.name}</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>
`;
        return {
            path: path.join(outputDir, stackName, 'index.html'),
            content: html,
            overwrite: false,
        };
    }
    generateAppComponent(stackName, component, outputDir) {
        let code = `import React from 'react';\nimport ReactDOM from 'react-dom/client';\n`;
        // Import all UI components
        if (component.components) {
            for (const uiComp of component.components) {
                code += `import { ${uiComp.name} } from './components/${uiComp.name}';\n`;
            }
        }
        code += `\nconst App: React.FC = () => {\n`;
        code += `  return (\n`;
        code += `    <div>\n`;
        // Render UI components
        if (component.components) {
            for (const uiComp of component.components) {
                code += `      <${uiComp.name} />\n`;
            }
        }
        code += `    </div>\n`;
        code += `  );\n`;
        code += `};\n\n`;
        code += `ReactDOM.createRoot(document.getElementById('root')!).render(\n`;
        code += `  <React.StrictMode>\n`;
        code += `    <App />\n`;
        code += `  </React.StrictMode>\n`;
        code += `);\n`;
        return {
            path: path.join(outputDir, stackName, 'src', 'main.tsx'),
            content: code,
            overwrite: true,
        };
    }
    // Helper methods
    capitalize(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    }
    toCamelCase(str) {
        return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
    }
    formatInitialValue(value, type) {
        // Handle explicit null value
        if (value === null || value === 'null') {
            return 'null';
        }
        if (type.includes('string')) {
            return `"${value}"`;
        }
        if (type.includes('null') && value === undefined) {
            return 'null';
        }
        return String(value);
    }
    parseTemplateValue(template, context = 'success') {
        // Handle non-string values
        if (typeof template === 'boolean') {
            return String(template);
        }
        if (typeof template === 'number') {
            return String(template);
        }
        if (typeof template !== 'string') {
            return String(template);
        }
        // Parse {{ state.email }} -> email
        // Parse {{ response.token }} -> response.token (success) or error.token (error)
        const match = template.match(/\{\{\s*([^}]+)\s*\}\}/);
        if (match) {
            const path = match[1].trim();
            if (path.startsWith('state.')) {
                return path.replace('state.', '');
            }
            if (path.startsWith('response.')) {
                // In error context, map response.* to error.*
                if (context === 'error') {
                    return path.replace('response.', 'error.');
                }
                return path.replace('response.', 'response.');
            }
            return path;
        }
        return `"${template}"`;
    }
    getFieldStateVar(field) {
        // Map field-id to state variable name
        // email-input -> email
        // password-input -> password
        return field.id.replace('-input', '').replace('-', '');
    }
    mapTypeToTs(type) {
        switch (type) {
            case 'string':
                return 'string';
            case 'number':
            case 'integer':
                return 'number';
            case 'boolean':
                return 'boolean';
            case 'array':
                return 'any[]';
            case 'object':
                return 'any';
            default:
                return 'any';
        }
    }
    /**
     * Extended workflow methods
     */
    generateModelPages(modelName, modelDef, workflow, outputDir) {
        const files = [];
        const _modelLower = modelName.toLowerCase();
        // Generate List Page
        files.push({
            path: path.join(outputDir, 'frontend', 'src', 'pages', modelName, `${modelName}List.tsx`),
            content: this.generateModelListPage(modelName, modelDef, workflow),
            overwrite: true,
        });
        // Generate Detail Page
        files.push({
            path: path.join(outputDir, 'frontend', 'src', 'pages', modelName, `${modelName}Detail.tsx`),
            content: this.generateModelDetailPage(modelName, modelDef, workflow),
            overwrite: true,
        });
        // Generate Create/Edit Form Page
        files.push({
            path: path.join(outputDir, 'frontend', 'src', 'pages', modelName, `${modelName}Form.tsx`),
            content: this.generateModelFormPage(modelName, modelDef, workflow),
            overwrite: true,
        });
        return files;
    }
    generateModelListPage(modelName, modelDef, _workflow) {
        const modelVar = this.toCamelCase(modelName);
        const modelPlural = `${modelVar}s`;
        let code = `import React, { useState, useEffect } from 'react';\n`;
        code += `import { Link } from 'react-router-dom';\n`;
        code += `import { api } from '../../api/client';\n\n`;
        code += `export const ${modelName}List: React.FC = () => {\n`;
        code += `  const [${modelPlural}, set${this.capitalize(modelPlural)}] = useState<any[]>([]);\n`;
        code += `  const [loading, setLoading] = useState(true);\n\n`;
        code += `  useEffect(() => {\n`;
        code += `    const load${this.capitalize(modelPlural)} = async () => {\n`;
        code += `      try {\n`;
        code += `        const data = await api.${modelVar}.list();\n`;
        code += `        set${this.capitalize(modelPlural)}(data);\n`;
        code += `      } catch (error) {\n`;
        code += `        console.error('Failed to load ${modelPlural}:', error);\n`;
        code += `      } finally {\n`;
        code += `        setLoading(false);\n`;
        code += `      }\n`;
        code += `    };\n`;
        code += `    load${this.capitalize(modelPlural)}();\n`;
        code += `  }, []);\n\n`;
        code += `  if (loading) return <div>Loading...</div>;\n\n`;
        code += `  return (\n`;
        code += `    <div className="container mx-auto p-4">\n`;
        code += `      <div className="flex justify-between items-center mb-4">\n`;
        code += `        <h1 className="text-2xl font-bold">${modelName}s</h1>\n`;
        code += `        <Link to="/${modelVar}s/new" className="btn btn-primary">Create ${modelName}</Link>\n`;
        code += `      </div>\n`;
        code += `      <div className="space-y-2">\n`;
        code += `        {${modelPlural}.map((${modelVar}: any) => (\n`;
        code += `          <div key={${modelVar}.id} className="card p-4">\n`;
        code += `            <Link to={\`/${modelVar}s/\${${modelVar}.id}\`} className="text-blue-600 hover:underline">\n`;
        code += `              ${this.getModelDisplayField(modelDef, modelVar)}\n`;
        code += `            </Link>\n`;
        code += `          </div>\n`;
        code += `        ))}\n`;
        code += `      </div>\n`;
        code += `    </div>\n`;
        code += `  );\n`;
        code += `};\n`;
        return code;
    }
    generateModelDetailPage(modelName, modelDef, _workflow) {
        const modelVar = this.toCamelCase(modelName);
        let code = `import React, { useState, useEffect } from 'react';\n`;
        code += `import { useParams, Link } from 'react-router-dom';\n`;
        code += `import { api } from '../../api/client';\n\n`;
        code += `export const ${modelName}Detail: React.FC = () => {\n`;
        code += `  const { id } = useParams<{ id: string }>();\n`;
        code += `  const [${modelVar}, set${modelName}] = useState<any>(null);\n`;
        code += `  const [loading, setLoading] = useState(true);\n\n`;
        code += `  useEffect(() => {\n`;
        code += `    const load${modelName} = async () => {\n`;
        code += `      if (!id) return;\n`;
        code += `      try {\n`;
        code += `        const data = await api.${modelVar}.get(id);\n`;
        code += `        set${modelName}(data);\n`;
        code += `      } catch (error) {\n`;
        code += `        console.error('Failed to load ${modelVar}:', error);\n`;
        code += `      } finally {\n`;
        code += `        setLoading(false);\n`;
        code += `      }\n`;
        code += `    };\n`;
        code += `    load${modelName}();\n`;
        code += `  }, [id]);\n\n`;
        code += `  if (loading) return <div>Loading...</div>;\n`;
        code += `  if (!${modelVar}) return <div>Not found</div>;\n\n`;
        code += `  return (\n`;
        code += `    <div className="container mx-auto p-4">\n`;
        code += `      <div className="flex justify-between items-center mb-4">\n`;
        code += `        <h1 className="text-2xl font-bold">${modelName} Details</h1>\n`;
        code += `        <Link to={\`/${modelVar}s/\${id}/edit\`} className="btn btn-primary">Edit</Link>\n`;
        code += `      </div>\n`;
        code += `      <div className="card p-6">\n`;
        // Generate fields display
        for (const [fieldName, _fieldDef] of Object.entries(modelDef.fields)) {
            code += `        <div className="mb-2">\n`;
            code += `          <strong>${fieldName}:</strong> {${modelVar}.${fieldName}}\n`;
            code += `        </div>\n`;
        }
        code += `      </div>\n`;
        code += `    </div>\n`;
        code += `  );\n`;
        code += `};\n`;
        return code;
    }
    generateModelFormPage(modelName, modelDef, _workflow) {
        const modelVar = this.toCamelCase(modelName);
        let code = `import React, { useState, useEffect } from 'react';\n`;
        code += `import { useParams, useNavigate } from 'react-router-dom';\n`;
        code += `import { api } from '../../api/client';\n\n`;
        code += `export const ${modelName}Form: React.FC = () => {\n`;
        code += `  const { id } = useParams<{ id: string }>();\n`;
        code += `  const navigate = useNavigate();\n`;
        code += `  const isEdit = !!id;\n\n`;
        // Generate state for each field
        code += `  // Form state\n`;
        for (const [fieldName, fieldDef] of Object.entries(modelDef.fields)) {
            if (fieldDef.system_field || fieldName === 'id')
                continue;
            const defaultValue = this.getFieldDefaultValue(fieldDef);
            code += `  const [${fieldName}, set${this.capitalize(fieldName)}] = useState(${defaultValue});\n`;
        }
        code += `  const [loading, setLoading] = useState(false);\n`;
        code += `  const [error, setError] = useState('');\n\n`;
        // Load existing data if editing
        code += `  useEffect(() => {\n`;
        code += `    if (isEdit && id) {\n`;
        code += `      const load${modelName} = async () => {\n`;
        code += `        try {\n`;
        code += `          const data = await api.${modelVar}.get(id);\n`;
        for (const [fieldName, fieldDef] of Object.entries(modelDef.fields)) {
            if (fieldDef.system_field || fieldName === 'id')
                continue;
            code += `          set${this.capitalize(fieldName)}(data.${fieldName} || ${this.getFieldDefaultValue(fieldDef)});\n`;
        }
        code += `        } catch (error) {\n`;
        code += `          console.error('Failed to load ${modelVar}:', error);\n`;
        code += `        }\n`;
        code += `      };\n`;
        code += `      load${modelName}();\n`;
        code += `    }\n`;
        code += `  }, [isEdit, id]);\n\n`;
        // Handle submit
        code += `  const handleSubmit = async (e: React.FormEvent) => {\n`;
        code += `    e.preventDefault();\n`;
        code += `    setLoading(true);\n`;
        code += `    setError('');\n\n`;
        code += `    try {\n`;
        code += `      const data = {\n`;
        for (const [fieldName, fieldDef] of Object.entries(modelDef.fields)) {
            if (fieldDef.system_field || fieldName === 'id')
                continue;
            code += `        ${fieldName},\n`;
        }
        code += `      };\n\n`;
        code += `      if (isEdit && id) {\n`;
        code += `        await api.${modelVar}.update(id, data);\n`;
        code += `      } else {\n`;
        code += `        await api.${modelVar}.create(data);\n`;
        code += `      }\n`;
        code += `      navigate('/${modelVar}s');\n`;
        code += `    } catch (error: any) {\n`;
        code += `      setError(error.message || 'Failed to save');\n`;
        code += `    } finally {\n`;
        code += `      setLoading(false);\n`;
        code += `    }\n`;
        code += `  };\n\n`;
        // Render form
        code += `  return (\n`;
        code += `    <div className="container mx-auto p-4">\n`;
        code += `      <h1 className="text-2xl font-bold mb-4">{isEdit ? 'Edit' : 'Create'} ${modelName}</h1>\n`;
        code += `      {error && <div className="alert alert-error mb-4">{error}</div>}\n`;
        code += `      <form onSubmit={handleSubmit} className="card p-6 space-y-4">\n`;
        // Generate form fields
        for (const [fieldName, fieldDef] of Object.entries(modelDef.fields)) {
            if (fieldDef.system_field || fieldName === 'id')
                continue;
            code += `        <div>\n`;
            code += `          <label className="label">${this.capitalize(fieldName)}</label>\n`;
            code += this.generateFormField(fieldName, fieldDef);
            code += `        </div>\n`;
        }
        code += `        <div className="flex gap-2">\n`;
        code += `          <button type="submit" className="btn btn-primary" disabled={loading}>\n`;
        code += `            {loading ? 'Saving...' : 'Save'}\n`;
        code += `          </button>\n`;
        code += `          <button type="button" onClick={() => navigate('/${modelVar}s')} className="btn btn-secondary">\n`;
        code += `            Cancel\n`;
        code += `          </button>\n`;
        code += `        </div>\n`;
        code += `      </form>\n`;
        code += `    </div>\n`;
        code += `  );\n`;
        code += `};\n`;
        return code;
    }
    generateEnhancedApiClient(workflow, stackName, outputDir) {
        let code = `/**\n * API Client\n * Generated from workflow models\n * \n * Auto-configured based on workflow:\n * - Backend: ${workflow.technology?.backend?.framework || 'express'}\n * - Auth: ${workflow.technology?.auth?.provider || 'token-based'}\n */\n\n`;
        code += `import { workflowConfig, getTechnologyProvider } from '../config/workflow';\n\n`;
        code += `const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000/api';\n\n`;
        code += `class ApiClient {\n`;
        code += `  private async request(endpoint: string, options?: RequestInit) {\n`;
        code += `    // Get auth provider from workflow config\n`;
        code += `    const authProvider = getTechnologyProvider('auth', 'provider');\n`;
        code += `    const token = localStorage.getItem('auth_token');\n\n`;
        code += `    const response = await fetch(\`\${API_BASE_URL}\${endpoint}\`, {\n`;
        code += `      ...options,\n`;
        code += `      headers: {\n`;
        code += `        'Content-Type': 'application/json',\n`;
        code += `        // Add auth header based on configured provider\n`;
        code += `        ...(token && { Authorization: \`Bearer \${token}\` }),\n`;
        code += `        ...options?.headers,\n`;
        code += `      },\n`;
        code += `    });\n\n`;
        code += `    if (!response.ok) {\n`;
        code += `      const error = await response.json();\n`;
        code += `      throw new Error(error.message || 'Request failed');\n`;
        code += `    }\n\n`;
        code += `    return response.json();\n`;
        code += `  }\n\n`;
        // Generate CRUD methods for each model
        for (const modelName of Object.keys(workflow.models)) {
            const modelVar = this.toCamelCase(modelName);
            const modelPath = `/${modelVar}s`;
            code += `  ${modelVar} = {\n`;
            code += `    list: () => this.request('${modelPath}'),\n`;
            code += `    get: (id: string) => this.request(\`${modelPath}/\${id}\`),\n`;
            code += `    create: (data: any) => this.request('${modelPath}', {\n`;
            code += `      method: 'POST',\n`;
            code += `      body: JSON.stringify(data),\n`;
            code += `    }),\n`;
            code += `    update: (id: string, data: any) => this.request(\`${modelPath}/\${id}\`, {\n`;
            code += `      method: 'PUT',\n`;
            code += `      body: JSON.stringify(data),\n`;
            code += `    }),\n`;
            code += `    delete: (id: string) => this.request(\`${modelPath}/\${id}\`, {\n`;
            code += `      method: 'DELETE',\n`;
            code += `    }),\n`;
            code += `  };\n\n`;
        }
        code += `}\n\n`;
        code += `export const api = new ApiClient();\n`;
        return {
            path: path.join(outputDir, stackName, 'src', 'api', 'client.ts'),
            content: code,
            overwrite: true,
        };
    }
    generateEnhancedApp(workflow, stackName, outputDir) {
        let code = `import React from 'react';\n`;
        code += `import ReactDOM from 'react-dom/client';\n`;
        code += `import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';\n`;
        code += `import './index.css';\n\n`;
        // Import all model pages
        for (const modelName of Object.keys(workflow.models)) {
            code += `import { ${modelName}List } from './pages/${modelName}/${modelName}List';\n`;
            code += `import { ${modelName}Detail } from './pages/${modelName}/${modelName}Detail';\n`;
            code += `import { ${modelName}Form } from './pages/${modelName}/${modelName}Form';\n`;
        }
        // Import custom pages if defined
        if (workflow.ui?.pages) {
            for (const page of workflow.ui.pages) {
                const pageName = this.toComponentName(page.title);
                code += `import { ${pageName} } from './pages/${pageName}';\n`;
            }
        }
        // Import ProtectedRoute if auth is used
        const hasAuthRoutes = workflow.ui?.pages?.some((p) => p.auth === 'required') ||
            workflow.ui?.navigation?.some((n) => n.auth === 'required');
        if (hasAuthRoutes) {
            code += `import { ProtectedRoute } from './components/ProtectedRoute';\n`;
        }
        code += `\nconst App: React.FC = () => {\n`;
        code += `  return (\n`;
        code += `    <BrowserRouter>\n`;
        code += `      <div className="min-h-screen bg-gray-50">\n`;
        code += `        <nav className="bg-white shadow-sm">\n`;
        code += `          <div className="container mx-auto px-4 py-4">\n`;
        code += `            <div className="flex gap-4">\n`;
        // Check if custom navigation is defined in workflow.ui.navigation
        if (workflow.ui?.navigation && workflow.ui.navigation.length > 0) {
            // Use custom navigation from schema
            for (const navItem of workflow.ui.navigation) {
                const icon = navItem.icon ? `<span className="mr-1">${navItem.icon}</span>` : '';
                code += `              <Link to="${navItem.path}" className="text-blue-600 hover:underline flex items-center">\n`;
                if (icon) {
                    code += `                ${icon}\n`;
                }
                code += `                ${navItem.label}\n`;
                code += `              </Link>\n`;
            }
        }
        else {
            // Fall back to auto-generated navigation from models
            code += `              <Link to="/" className="text-blue-600 hover:underline">Home</Link>\n`;
            for (const modelName of Object.keys(workflow.models)) {
                const modelVar = this.toCamelCase(modelName);
                code += `              <Link to="/${modelVar}s" className="text-blue-600 hover:underline">${modelName}s</Link>\n`;
            }
        }
        code += `            </div>\n`;
        code += `          </div>\n`;
        code += `        </nav>\n`;
        code += `        <Routes>\n`;
        code += `          <Route path="/" element={<div className="container mx-auto p-4"><h1 className="text-3xl font-bold">${workflow.workflow?.name || 'Welcome'}</h1></div>} />\n`;
        // Generate routes for custom pages first
        if (workflow.ui?.pages) {
            for (const page of workflow.ui.pages) {
                const pageName = this.toComponentName(page.title);
                const element = page.auth === 'required'
                    ? `<ProtectedRoute><${pageName} /></ProtectedRoute>`
                    : `<${pageName} />`;
                code += `          <Route path="${page.path}" element={${element}} />\n`;
            }
        }
        // Generate routes for each model
        for (const modelName of Object.keys(workflow.models)) {
            const modelVar = this.toCamelCase(modelName);
            code += `          <Route path="/${modelVar}s" element={<${modelName}List />} />\n`;
            code += `          <Route path="/${modelVar}s/new" element={<${modelName}Form />} />\n`;
            code += `          <Route path="/${modelVar}s/:id" element={<${modelName}Detail />} />\n`;
            code += `          <Route path="/${modelVar}s/:id/edit" element={<${modelName}Form />} />\n`;
        }
        code += `        </Routes>\n`;
        code += `      </div>\n`;
        code += `    </BrowserRouter>\n`;
        code += `  );\n`;
        code += `};\n\n`;
        code += `ReactDOM.createRoot(document.getElementById('root')!).render(\n`;
        code += `  <React.StrictMode>\n`;
        code += `    <App />\n`;
        code += `  </React.StrictMode>\n`;
        code += `);\n`;
        return {
            path: path.join(outputDir, stackName, 'src', 'main.tsx'),
            content: code,
            overwrite: true,
        };
    }
    generateThemeConfig(theme, stackName, outputDir) {
        const css = `@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .btn {
    @apply px-4 py-2 rounded font-medium transition-colors;
  }
  .btn-primary {
    @apply bg-blue-600 text-white hover:bg-blue-700;
  }
  .btn-secondary {
    @apply bg-gray-200 text-gray-800 hover:bg-gray-300;
  }
  .card {
    @apply bg-white rounded-lg shadow;
  }
  .label {
    @apply block text-sm font-medium text-gray-700 mb-1;
  }
  .input {
    @apply w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500;
  }
  .alert {
    @apply p-4 rounded-md;
  }
  .alert-error {
    @apply bg-red-50 text-red-800 border border-red-200;
  }
}
`;
        return {
            path: path.join(outputDir, stackName, 'src', 'index.css'),
            content: css,
            overwrite: true,
        };
    }
    generateTailwindConfig(theme, stackName, outputDir) {
        const config = `/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
`;
        return {
            path: path.join(outputDir, stackName, 'tailwind.config.js'),
            content: config,
            overwrite: false,
        };
    }
    generatePostCssConfig(stackName, outputDir) {
        const config = `export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}
`;
        return {
            path: path.join(outputDir, stackName, 'postcss.config.js'),
            content: config,
            overwrite: false,
        };
    }
    generatePackageJsonWithRouter(stackName, workflow, outputDir) {
        const pkg = {
            name: stackName,
            version: '1.0.0',
            type: 'module',
            scripts: {
                dev: 'vite',
                build: 'tsc && vite build',
                preview: 'vite preview',
            },
            dependencies: {
                react: '^18.2.0',
                'react-dom': '^18.2.0',
                'react-router-dom': '^6.20.0',
            },
            devDependencies: {
                '@types/react': '^18.2.0',
                '@types/react-dom': '^18.2.0',
                '@vitejs/plugin-react': '^4.0.0',
                typescript: '^5.0.0',
                vite: '^4.3.0',
                tailwindcss: '^3.3.0',
                autoprefixer: '^10.4.16',
                postcss: '^8.4.32',
            },
        };
        return {
            path: path.join(outputDir, stackName, 'package.json'),
            content: JSON.stringify(pkg, null, 2),
            overwrite: false,
        };
    }
    generateViteConfigExtended(stackName, workflow, outputDir) {
        const config = `import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  server: {
    port: 5173,
    host: true,
  },
});
`;
        return {
            path: path.join(outputDir, stackName, 'vite.config.ts'),
            content: config,
            overwrite: false,
        };
    }
    generateIndexHtmlExtended(stackName, workflow, outputDir) {
        const html = `<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>${workflow.workflow?.name || 'App'}</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>
`;
        return {
            path: path.join(outputDir, stackName, 'index.html'),
            content: html,
            overwrite: false,
        };
    }
    generateCustomPage(page, workflow, outputDir) {
        const pageName = this.toComponentName(page.title);
        let code = `import React from 'react';\n`;
        // Import data fetching hooks if data_source is specified
        if (page.data_source) {
            code += `import { useState, useEffect } from 'react';\n`;
            code += `import { api } from '../api/client';\n`;
        }
        // Import Link for navigation
        code += `import { Link } from 'react-router-dom';\n\n`;
        code += `/**\n`;
        code += ` * ${pageName}\n`;
        code += ` * Custom page generated from extended workflow schema\n`;
        code += ` * Path: ${page.path}\n`;
        if (page.auth)
            code += ` * Auth: ${page.auth}\n`;
        code += ` */\n`;
        code += `export const ${pageName}: React.FC = () => {\n`;
        // Add state management if data_source exists
        if (page.data_source) {
            code += `  const [data, setData] = useState<any>(null);\n`;
            code += `  const [loading, setLoading] = useState(true);\n`;
            code += `  const [error, setError] = useState('');\n\n`;
            code += `  useEffect(() => {\n`;
            code += `    const fetchData = async () => {\n`;
            code += `      try {\n`;
            code += `        // TODO: Implement API call to ${page.data_source}\n`;
            code += `        // const response = await api.customEndpoint();\n`;
            code += `        // setData(response);\n`;
            code += `      } catch (err: any) {\n`;
            code += `        setError(err.message || 'Failed to fetch data');\n`;
            code += `      } finally {\n`;
            code += `        setLoading(false);\n`;
            code += `      }\n`;
            code += `    };\n`;
            code += `    fetchData();\n`;
            code += `  }, []);\n\n`;
            code += `  if (loading) return <div className="container mx-auto p-4">Loading...</div>;\n`;
            code += `  if (error) return <div className="container mx-auto p-4 text-red-600">{error}</div>;\n\n`;
        }
        code += `  return (\n`;
        // Generate layout based on page.layout
        switch (page.layout) {
            case 'grid':
                code += `    <div className="container mx-auto p-4">\n`;
                code += `      <h1 className="text-3xl font-bold mb-6">${page.title}</h1>\n`;
                code += `      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">\n`;
                code += `        {/* TODO: Add grid items */}\n`;
                if (page.data_source) {
                    code += `        {data && Array.isArray(data) && data.map((item: any, index: number) => (\n`;
                    code += `          <div key={index} className="card p-4">\n`;
                    code += `            {JSON.stringify(item)}\n`;
                    code += `          </div>\n`;
                    code += `        ))}\n`;
                }
                code += `      </div>\n`;
                code += `    </div>\n`;
                break;
            case 'list':
                code += `    <div className="container mx-auto p-4">\n`;
                code += `      <h1 className="text-3xl font-bold mb-6">${page.title}</h1>\n`;
                code += `      <div className="space-y-2">\n`;
                code += `        {/* TODO: Add list items */}\n`;
                if (page.data_source) {
                    code += `        {data && Array.isArray(data) && data.map((item: any, index: number) => (\n`;
                    code += `          <div key={index} className="card p-4">\n`;
                    code += `            {JSON.stringify(item)}\n`;
                    code += `          </div>\n`;
                    code += `        ))}\n`;
                }
                code += `      </div>\n`;
                code += `    </div>\n`;
                break;
            case 'dashboard':
                code += `    <div className="container mx-auto p-4">\n`;
                code += `      <h1 className="text-3xl font-bold mb-6">${page.title}</h1>\n`;
                code += `      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">\n`;
                code += `        {/* TODO: Add dashboard stats */}\n`;
                code += `        <div className="card p-6">\n`;
                code += `          <h3 className="text-lg font-semibold mb-2">Metric 1</h3>\n`;
                code += `          <p className="text-3xl font-bold">0</p>\n`;
                code += `        </div>\n`;
                code += `      </div>\n`;
                code += `      <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">\n`;
                code += `        {/* TODO: Add dashboard charts/tables */}\n`;
                code += `        <div className="card p-6">\n`;
                code += `          <h3 className="text-lg font-semibold mb-4">Chart 1</h3>\n`;
                code += `        </div>\n`;
                code += `      </div>\n`;
                code += `    </div>\n`;
                break;
            case 'detail':
                code += `    <div className="container mx-auto p-4">\n`;
                code += `      <div className="flex justify-between items-center mb-6">\n`;
                code += `        <h1 className="text-3xl font-bold">${page.title}</h1>\n`;
                code += `        <Link to="/" className="text-blue-600 hover:underline">Back</Link>\n`;
                code += `      </div>\n`;
                code += `      <div className="card p-6">\n`;
                code += `        {/* TODO: Add detail view */}\n`;
                if (page.data_source) {
                    code += `        {data && (\n`;
                    code += `          <div className="space-y-2">\n`;
                    code += `            {Object.entries(data).map(([key, value]) => (\n`;
                    code += `              <div key={key}>\n`;
                    code += `                <strong className="text-gray-700">{key}:</strong>{' '}\n`;
                    code += `                <span>{String(value)}</span>\n`;
                    code += `              </div>\n`;
                    code += `            ))}\n`;
                    code += `          </div>\n`;
                    code += `        )}\n`;
                }
                code += `      </div>\n`;
                code += `    </div>\n`;
                break;
            default:
                // Default: simple centered layout
                code += `    <div className="container mx-auto p-4">\n`;
                code += `      <h1 className="text-3xl font-bold mb-6">${page.title}</h1>\n`;
                code += `      <div className="card p-6">\n`;
                code += `        {/* TODO: Add page content */}\n`;
                code += `        <p>This is a custom page. Implement your content here.</p>\n`;
                if (page.components && page.components.length > 0) {
                    code += `        {/* Components to include: ${page.components.join(', ')} */}\n`;
                }
                if (page.data_source) {
                    code += `        {data && <pre className="mt-4 p-4 bg-gray-100 rounded overflow-auto">{JSON.stringify(data, null, 2)}</pre>}\n`;
                }
                code += `      </div>\n`;
                code += `    </div>\n`;
        }
        code += `  );\n`;
        code += `};\n`;
        return {
            path: path.join(outputDir, 'frontend', 'src', 'pages', `${pageName}.tsx`),
            content: code,
            overwrite: true,
        };
    }
    generateFormComponent(form, workflow, outputDir) {
        const formName = this.toComponentName(form.id);
        let code = `import React, { useState } from 'react';\n\n`;
        code += `/**\n`;
        code += ` * ${formName}\n`;
        if (form.title)
            code += ` * ${form.title}\n`;
        if (form.model)
            code += ` * Associated model: ${form.model}\n`;
        code += ` * Generated from extended workflow schema\n`;
        code += ` */\n`;
        // Generate interface for form props
        code += `export interface ${formName}Props {\n`;
        code += `  onSubmit?: (data: any) => void | Promise<void>;\n`;
        code += `  initialValues?: Record<string, any>;\n`;
        code += `  submitLabel?: string;\n`;
        code += `}\n\n`;
        code += `export const ${formName}: React.FC<${formName}Props> = ({\n`;
        code += `  onSubmit,\n`;
        code += `  initialValues = {},\n`;
        code += `  submitLabel = '${form.submit_label || 'Submit'}',\n`;
        code += `}) => {\n`;
        // Generate state for each field
        code += `  // Form field state\n`;
        for (const field of form.fields) {
            const defaultValue = this.getFormFieldDefaultValue(field);
            code += `  const [${field.name}, set${this.capitalize(field.name)}] = useState(initialValues.${field.name} ?? ${defaultValue});\n`;
        }
        code += `  const [submitting, setSubmitting] = useState(false);\n`;
        code += `  const [error, setError] = useState('');\n\n`;
        // Generate submit handler
        code += `  const handleSubmit = async (e: React.FormEvent) => {\n`;
        code += `    e.preventDefault();\n`;
        code += `    setError('');\n`;
        code += `    setSubmitting(true);\n\n`;
        // Add validation logic
        code += `    // Validation\n`;
        for (const field of form.fields) {
            if (field.validation && field.validation.includes('required')) {
                code += `    if (!${field.name}) {\n`;
                code += `      setError('${field.label} is required');\n`;
                code += `      setSubmitting(false);\n`;
                code += `      return;\n`;
                code += `    }\n`;
            }
        }
        code += `\n`;
        code += `    try {\n`;
        code += `      const formData = {\n`;
        for (const field of form.fields) {
            code += `        ${field.name},\n`;
        }
        code += `      };\n\n`;
        code += `      if (onSubmit) {\n`;
        code += `        await onSubmit(formData);\n`;
        code += `      }\n`;
        if (form.on_submit) {
            code += `      // TODO: Emit event: ${form.on_submit}\n`;
        }
        code += `    } catch (err: any) {\n`;
        code += `      setError(err.message || 'Failed to submit form');\n`;
        code += `    } finally {\n`;
        code += `      setSubmitting(false);\n`;
        code += `    }\n`;
        code += `  };\n\n`;
        // Generate JSX
        code += `  return (\n`;
        code += `    <form onSubmit={handleSubmit} className="space-y-4">\n`;
        if (form.title) {
            code += `      <h2 className="text-2xl font-bold mb-4">${form.title}</h2>\n`;
        }
        code += `      {error && (\n`;
        code += `        <div className="alert alert-error">{error}</div>\n`;
        code += `      )}\n\n`;
        // Generate form fields
        for (const field of form.fields) {
            code += `      {/* Field: ${field.name} */}\n`;
            code += `      <div>\n`;
            code += `        <label htmlFor="${field.name}" className="label">\n`;
            code += `          ${field.label}\n`;
            code += `        </label>\n`;
            switch (field.type) {
                case 'textarea':
                    code += `        <textarea\n`;
                    code += `          id="${field.name}"\n`;
                    code += `          className="input"\n`;
                    code += `          value={${field.name}}\n`;
                    code += `          onChange={(e) => set${this.capitalize(field.name)}(e.target.value)}\n`;
                    if (field.placeholder)
                        code += `          placeholder="${field.placeholder}"\n`;
                    if (field.validation && field.validation.includes('required'))
                        code += `          required\n`;
                    code += `          rows={4}\n`;
                    code += `        />\n`;
                    break;
                case 'select':
                    code += `        <select\n`;
                    code += `          id="${field.name}"\n`;
                    code += `          className="input"\n`;
                    code += `          value={${field.name}}\n`;
                    code += `          onChange={(e) => set${this.capitalize(field.name)}(e.target.value)}\n`;
                    if (field.validation && field.validation.includes('required'))
                        code += `          required\n`;
                    code += `        >\n`;
                    code += `          <option value="">Select ${field.label}</option>\n`;
                    if (field.options) {
                        for (const option of field.options) {
                            code += `          <option value="${option.value}">${option.label}</option>\n`;
                        }
                    }
                    code += `        </select>\n`;
                    break;
                case 'checkbox':
                    code += `        <input\n`;
                    code += `          id="${field.name}"\n`;
                    code += `          type="checkbox"\n`;
                    code += `          checked={${field.name}}\n`;
                    code += `          onChange={(e) => set${this.capitalize(field.name)}(e.target.checked)}\n`;
                    code += `          className="mr-2"\n`;
                    code += `        />\n`;
                    break;
                case 'file':
                    code += `        <input\n`;
                    code += `          id="${field.name}"\n`;
                    code += `          type="file"\n`;
                    code += `          className="input"\n`;
                    code += `          onChange={(e) => {\n`;
                    code += `            const file = e.target.files?.[0];\n`;
                    code += `            if (file) set${this.capitalize(field.name)}(file);\n`;
                    code += `          }}\n`;
                    if (field.validation && field.validation.includes('required'))
                        code += `          required\n`;
                    code += `        />\n`;
                    break;
                default:
                    // text, email, password, number, date
                    code += `        <input\n`;
                    code += `          id="${field.name}"\n`;
                    code += `          type="${field.type}"\n`;
                    code += `          className="input"\n`;
                    code += `          value={${field.name}}\n`;
                    code += `          onChange={(e) => set${this.capitalize(field.name)}(e.target.value)}\n`;
                    if (field.placeholder)
                        code += `          placeholder="${field.placeholder}"\n`;
                    if (field.validation && field.validation.includes('required'))
                        code += `          required\n`;
                    // Add min/max for number fields
                    if (field.type === 'number' && field.validation) {
                        const minMatch = field.validation.match(/min:(\d+)/);
                        const maxMatch = field.validation.match(/max:(\d+)/);
                        if (minMatch)
                            code += `          min="${minMatch[1]}"\n`;
                        if (maxMatch)
                            code += `          max="${maxMatch[1]}"\n`;
                    }
                    // Add pattern for text fields
                    if (field.validation && field.validation.includes('email')) {
                        code += `          pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$"\n`;
                    }
                    code += `        />\n`;
            }
            code += `      </div>\n\n`;
        }
        // Submit button
        code += `      <div className="flex gap-2">\n`;
        code += `        <button\n`;
        code += `          type="submit"\n`;
        code += `          className="btn btn-primary"\n`;
        code += `          disabled={submitting}\n`;
        code += `        >\n`;
        code += `          {submitting ? 'Submitting...' : submitLabel}\n`;
        code += `        </button>\n`;
        code += `      </div>\n`;
        code += `    </form>\n`;
        code += `  );\n`;
        code += `};\n`;
        return {
            path: path.join(outputDir, 'frontend', 'src', 'components', 'forms', `${formName}.tsx`),
            content: code,
            overwrite: true,
        };
    }
    generateProtectedRoute(stackName, outputDir) {
        const code = `import React from 'react';
import { Navigate } from 'react-router-dom';
import { workflowConfig, getTechnologyProvider } from '../config/workflow';

/**
 * ProtectedRoute Component
 * Wraps routes that require authentication
 *
 * This component uses workflow configuration to determine the auth strategy.
 * Check workflowConfig.technology.auth.provider at runtime.
 *
 * Usage:
 * <Route path="/protected" element={<ProtectedRoute><ProtectedPage /></ProtectedRoute>} />
 */
export interface ProtectedRouteProps {
  children: React.ReactNode;
  redirectTo?: string;
}

export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({
  children,
  redirectTo = '/login',
}) => {
  // Get auth provider from workflow config
  const authProvider = getTechnologyProvider('auth', 'provider');

  // Check if user is authenticated based on configured auth provider
  const isAuthenticated = () => {
    switch (authProvider) {
      case 'clerk':
        // Clerk auth check (will be integrated when Clerk provider is used)
        // For now, fall back to token-based auth
        return !!localStorage.getItem('auth_token');

      case 'auth0':
        // Auth0 check (future integration)
        return !!localStorage.getItem('auth_token');

      default:
        // Default: token-based authentication
        return !!localStorage.getItem('auth_token');
    }
  };

  if (!isAuthenticated()) {
    // Redirect to login page if not authenticated
    return <Navigate to={redirectTo} replace />;
  }

  return <>{children}</>;
};
`;
        return {
            path: path.join(outputDir, stackName, 'src', 'components', 'ProtectedRoute.tsx'),
            content: code,
            overwrite: true,
        };
    }
    getModelDisplayField(modelDef, modelVar) {
        // Try to find a good display field (name, title, email, etc.)
        const displayFields = ['title', 'name', 'email'];
        for (const field of displayFields) {
            if (modelDef.fields[field]) {
                return `{${modelVar}.${field}}`;
            }
        }
        return `{${modelVar}.id}`;
    }
    getFieldDefaultValue(fieldDef) {
        if (fieldDef.default !== undefined) {
            if (typeof fieldDef.default === 'string')
                return `'${fieldDef.default}'`;
            return String(fieldDef.default);
        }
        if (fieldDef.type === 'string' || fieldDef.type === 'text' || fieldDef.type === 'enum')
            return "''";
        if (fieldDef.type === 'number' || fieldDef.type === 'integer')
            return '0';
        if (fieldDef.type === 'boolean')
            return 'false';
        return "''";
    }
    generateFormField(fieldName, fieldDef) {
        const setter = `set${this.capitalize(fieldName)}`;
        if (fieldDef.type === 'text') {
            return `          <textarea\n` +
                `            className="input"\n` +
                `            value={${fieldName}}\n` +
                `            onChange={(e) => ${setter}(e.target.value)}\n` +
                `            ${fieldDef.required ? 'required' : ''}\n` +
                `          />\n`;
        }
        if (fieldDef.type === 'enum') {
            let code = `          <select\n`;
            code += `            className="input"\n`;
            code += `            value={${fieldName}}\n`;
            code += `            onChange={(e) => ${setter}(e.target.value)}\n`;
            code += `            ${fieldDef.required ? 'required' : ''}\n`;
            code += `          >\n`;
            if (fieldDef.enum_values) {
                for (const value of fieldDef.enum_values) {
                    code += `            <option value="${value}">${value}</option>\n`;
                }
            }
            code += `          </select>\n`;
            return code;
        }
        const inputType = this.mapFieldTypeToInputType(fieldDef.type);
        return `          <input\n` +
            `            type="${inputType}"\n` +
            `            className="input"\n` +
            `            value={${fieldName}}\n` +
            `            onChange={(e) => ${setter}(e.target.value)}\n` +
            `            ${fieldDef.required ? 'required' : ''}\n` +
            `          />\n`;
    }
    mapFieldTypeToInputType(type) {
        const typeMap = {
            string: 'text',
            email: 'email',
            password: 'password',
            integer: 'number',
            decimal: 'number',
            date: 'date',
            timestamp: 'datetime-local',
            boolean: 'checkbox',
        };
        return typeMap[type] || 'text';
    }
    /**
     * Convert title/id to valid React component name
     * "Contact Us" -> "ContactUs"
     * "user-profile" -> "UserProfile"
     */
    toComponentName(title) {
        return title
            .replace(/[^a-zA-Z0-9\s-]/g, '') // Remove special chars
            .split(/[\s-]+/) // Split on spaces or hyphens
            .map(word => this.capitalize(word))
            .join('');
    }
    /**
     * Get default value for form field based on field type
     */
    getFormFieldDefaultValue(field) {
        if (field.default !== undefined) {
            if (typeof field.default === 'string')
                return `'${field.default}'`;
            if (typeof field.default === 'boolean')
                return String(field.default);
            return String(field.default);
        }
        switch (field.type) {
            case 'text':
            case 'email':
            case 'password':
            case 'select':
                return "''";
            case 'number':
                return '0';
            case 'checkbox':
                return 'false';
            case 'file':
                return 'null';
            case 'date':
                return "''";
            default:
                return "''";
        }
    }
    /**
     * Generate a React component
     * Implements the recommended FrontendGenerator.generateComponent method
     */
    generateComponent(component, context) {
        // Delegate to existing generateUiComponent method
        const stackName = 'frontend';
        return this.generateUiComponent(component, stackName, context.outputDirectory);
    }
}
exports.ReactGenerator = ReactGenerator;
//# sourceMappingURL=react-generator.js.map