/**
 * Extended Workflow Schema
 * Extends event-driven schema with complete specifications for code generation
 *
 * This schema includes everything needed to generate:
 * - Database schema and migrations
 * - Backend API with all endpoints
 * - Frontend application with pages and components
 * - Tests for all layers
 * - Complete documentation
 */

import { z } from 'zod';
import { EventDrivenWorkflow as _EventDrivenWorkflow } from './event-driven-schema';

/**
 * Event Definition Schema
 * Defines events that can be emitted and consumed throughout the application
 */
const EventDefinitionSchema = z.object({
  description: z.string().min(1),
  payload: z.record(z.string(), z.enum(['string', 'number', 'boolean', 'array', 'object', 'date'])),
});

/**
 * Stack Component Event Config
 * Defines which events a stack component emits or consumes
 */
const ComponentEventConfigSchema = z.object({
  emits: z.array(z.string()).default([]), // Events this component emits
  consumes: z.array(z.string()).default([]), // Events this component consumes
});

/**
 * Field Type Schema
 * Defines database field types in a database-agnostic way
 */
const FieldTypeSchema = z.enum([
  'uuid',
  'string',
  'text',
  'integer',
  'decimal',
  'boolean',
  'timestamp',
  'date',
  'time',
  'json',
  'array',
  'enum',
]);

/**
 * API Access Control Schema
 * Controls who can read/write fields via API
 */
const APIAccessControlSchema = z.object({
  read_roles: z.array(z.string()).optional(), // Roles that can read this field/relation
  write_roles: z.array(z.string()).optional(), // Roles that can write this field/relation
  include_by_default: z.boolean().default(false), // Include in API responses by default
  queryable: z.boolean().default(true), // Can be included via ?include=field_name
});

/**
 * Field Definition Schema
 * Complete specification for a data model field
 */
const FieldDefinitionSchema = z.object({
  type: FieldTypeSchema,
  required: z.boolean().default(false),
  unique: z.boolean().default(false),
  default: z.any().optional(),
  max_length: z.number().optional(), // For string types
  min_length: z.number().optional(),
  precision: z.number().optional(), // For decimal types
  scale: z.number().optional(),
  enum_values: z.array(z.string()).optional(), // For enum types
  foreign_key: z.string().optional(), // References another model
  description: z.string().optional(),

  // Form and UI control
  system_field: z.boolean().default(false), // System-managed fields (created_at, id, etc.) - excluded from forms
  exclude_from_forms: z.boolean().default(false), // Explicitly exclude from generated forms
  form_widget: z.string().optional(), // Custom form widget (e.g., "rich-text-editor", "image-upload")

  // API access control for individual fields
  api_access: APIAccessControlSchema.optional(), // Control who can read/write this field via API
});

/**
 * Index Schema
 * Database indexes for performance
 */
const IndexSchema = z.object({
  fields: z.array(z.string()).min(1),
  unique: z.boolean().default(false),
  name: z.string().optional(),
});

/**
 * Data Model Schema
 * Defines database tables/collections
 */
const DataModelSchema = z.object({
  description: z.string().optional(),
  fields: z.record(z.string(), FieldDefinitionSchema),
  indexes: z.array(IndexSchema).optional(),
  timestamps: z.boolean().default(true), // Auto-add created_at/updated_at
  soft_delete: z.boolean().default(false), // Add deleted_at for soft deletes
});

/**
 * Relationship Schema
 * Defines relationships between models
 */
const RelationshipSchema = z.object({
  type: z.enum(['one_to_one', 'one_to_many', 'many_to_many']),
  from: z.string(), // Model name
  to: z.string(), // Model name
  through: z.string().optional(), // For many_to_many join table
  foreign_key: z.string().optional(), // Custom foreign key name
  inverse: z.boolean().default(true), // Create inverse relationship (e.g., User.tasks from Task.user_id)
  api_access: APIAccessControlSchema.optional(), // Control API exposure
  description: z.string().optional(),
});

/**
 * API Endpoint Schema
 * Explicit API endpoint definitions (optional - can be inferred from workflows)
 */
const APIEndpointSchema = z.object({
  path: z.string().min(1),
  method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
  component: z.string().min(1), // Must reference a component
  description: z.string().optional(),

  // Authentication and authorization
  auth: z.enum(['required', 'none']).default('required'),
  roles: z.array(z.string()).optional(), // REQUIRED if auth: required (e.g., ['admin', 'user'])
  exclude_roles: z.array(z.string()).optional(), // Roles that cannot access (e.g., ['guest'])
  ownership: z.object({
    enabled: z.boolean(),
    field: z.string().optional(), // Field to check ownership (default: user_id or id)
    override_roles: z.array(z.string()).optional(), // Roles that bypass ownership check (e.g., ['admin', 'superadmin'])
  }).optional(),

  params: z.record(z.string(), z.string()).optional(), // Path/query params
  body: z.string().optional(), // Model name for request body
  response: z.string().optional(), // Model name or type for response
  emits: z.string().optional(), // Event emitted on success
}).superRefine((data, ctx) => {
  // Validate: if auth is required, roles must be specified
  if (data.auth === 'required' && !data.roles && !data.ownership) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: `Endpoint '${data.method} ${data.path}' has auth: required but no roles specified. Must explicitly define roles (can be empty array []).`,
      path: ['roles'],
    });
  }
});

/**
 * Form Field Schema
 * UI form field specifications
 */
const FormFieldSchema = z.object({
  name: z.string().min(1),
  type: z.enum(['text', 'email', 'password', 'number', 'select', 'checkbox', 'textarea', 'date', 'file']),
  label: z.string().min(1),
  placeholder: z.string().optional(),
  validation: z.string().optional(), // e.g., "required|email|min:8"
  options: z.array(z.object({ value: z.string(), label: z.string() })).optional(), // For select fields
  default: z.any().optional(),
});

/**
 * UI Form Schema
 * Form definitions for data entry
 */
const UIFormSchema = z.object({
  id: z.string().min(1),
  title: z.string().optional(),
  model: z.string().optional(), // Associated data model
  fields: z.array(FormFieldSchema),
  submit_label: z.string().default('Submit'),
  on_submit: z.string().optional(), // Event to emit
});

/**
 * Custom Layout Schema
 * For complex layout structures beyond presets
 */
const CustomLayoutSchema = z.object({
  type: z.enum(['grid', 'flex', 'masonry', 'custom']),
  config: z.record(z.string(), z.any()).optional(), // Layout-specific config (columns, gap, etc.)
  responsive: z.boolean().default(true),
  breakpoints: z.record(z.string(), z.any()).optional(), // Responsive breakpoints
});

/**
 * UI Page Schema
 * Page definitions (optional - can be inferred from workflows)
 */
const UIPageSchema = z.object({
  path: z.string().min(1),
  title: z.string().min(1),
  layout: z.enum(['grid', 'list', 'detail', 'form', 'dashboard', 'custom']).optional(),
  custom_layout: CustomLayoutSchema.optional(), // For complex layouts like masonry
  auth: z.enum(['required', 'optional', 'none']).default('required'),
  components: z.array(z.string()).optional(), // List of component IDs to include
  data_source: z.string().optional(), // API endpoint to fetch data from
});

/**
 * Color Scale Schema (Tailwind/Radix style)
 * Full color palette with shades 50-950
 */
const ColorScaleSchema = z.object({
  50: z.string(),
  100: z.string(),
  200: z.string(),
  300: z.string(),
  400: z.string(),
  500: z.string(), // Base shade
  600: z.string(),
  700: z.string(),
  800: z.string(),
  900: z.string(),
  950: z.string(),
});

/**
 * Typography Scale Schema
 * Define heading and text styles
 */
const TypographyScaleSchema = z.object({
  h1: z.object({
    size: z.string(),
    weight: z.string(),
    line_height: z.string().optional(),
    letter_spacing: z.string().optional(),
  }),
  h2: z.object({
    size: z.string(),
    weight: z.string(),
    line_height: z.string().optional(),
    letter_spacing: z.string().optional(),
  }),
  h3: z.object({
    size: z.string(),
    weight: z.string(),
    line_height: z.string().optional(),
    letter_spacing: z.string().optional(),
  }),
  h4: z.object({
    size: z.string(),
    weight: z.string(),
    line_height: z.string().optional(),
    letter_spacing: z.string().optional(),
  }),
  h5: z.object({
    size: z.string(),
    weight: z.string(),
    line_height: z.string().optional(),
    letter_spacing: z.string().optional(),
  }),
  h6: z.object({
    size: z.string(),
    weight: z.string(),
    line_height: z.string().optional(),
    letter_spacing: z.string().optional(),
  }),
  body: z.object({
    size: z.string(),
    weight: z.string(),
    line_height: z.string().optional(),
  }),
  body_bold: z.object({
    size: z.string(),
    weight: z.string(),
    line_height: z.string().optional(),
  }).optional(),
  small: z.object({
    size: z.string(),
    weight: z.string(),
    line_height: z.string().optional(),
  }),
  // Custom typography variants (optional)
  custom: z.record(z.string(), z.object({
    size: z.string(),
    weight: z.string(),
    line_height: z.string().optional(),
    letter_spacing: z.string().optional(),
  })).optional(),
});

/**
 * Component Style Schema
 * Per-component styling (radius, spacing, shadows, etc.)
 */
const ComponentStyleSchema = z.object({
  radius: z.string().optional(), // e.g., "8px", "0.5rem", "md"
  padding: z.string().optional(),
  margin: z.string().optional(),
  shadow: z.string().optional(), // e.g., "sm", "md", "lg", or custom shadow
  border: z.string().optional(),
  // Component can define custom properties
  custom: z.record(z.string(), z.string()).optional(),
});

/**
 * Design Theme Schema
 * Complete design system configuration
 */
const DesignThemeSchema = z.object({
  // Full color palette (Tailwind/Radix style)
  colors: z.object({
    // Semantic colors (map to palette)
    brand: z.string(), // e.g., "blue-600" or specific hex
    primary: z.string(), // e.g., "blue-600"
    secondary: z.string().optional(),
    accent: z.string().optional(),
    success: z.string(), // e.g., "green-600"
    warning: z.string(), // e.g., "amber-600"
    error: z.string(), // e.g., "red-600"
    info: z.string().optional(),

    // Neutral colors
    background: z.string(), // e.g., "gray-50"
    foreground: z.string(), // e.g., "gray-950"
    muted: z.string().optional(), // e.g., "gray-100"
    muted_foreground: z.string().optional(), // e.g., "gray-500"

    // Full color palette (all colors with shades 50-950)
    palette: z.object({
      gray: ColorScaleSchema.optional(),
      red: ColorScaleSchema.optional(),
      orange: ColorScaleSchema.optional(),
      amber: ColorScaleSchema.optional(),
      yellow: ColorScaleSchema.optional(),
      lime: ColorScaleSchema.optional(),
      green: ColorScaleSchema.optional(),
      emerald: ColorScaleSchema.optional(),
      teal: ColorScaleSchema.optional(),
      cyan: ColorScaleSchema.optional(),
      sky: ColorScaleSchema.optional(),
      blue: ColorScaleSchema.optional(),
      indigo: ColorScaleSchema.optional(),
      violet: ColorScaleSchema.optional(),
      purple: ColorScaleSchema.optional(),
      fuchsia: ColorScaleSchema.optional(),
      pink: ColorScaleSchema.optional(),
      rose: ColorScaleSchema.optional(),
      // Allow custom color scales
      custom: z.record(z.string(), ColorScaleSchema).optional(),
    }).optional(),
  }),

  // Typography system
  typography: z.object({
    font_family: z.string(), // Base font family
    heading_font: z.string().optional(), // Optional separate font for headings
    mono_font: z.string().optional(), // Monospace font for code
    scale: TypographyScaleSchema,
  }),

  // Spacing scale (optional - uses Tailwind defaults if not specified)
  spacing: z.object({
    xs: z.string().optional(), // e.g., "4px"
    sm: z.string().optional(), // e.g., "8px"
    md: z.string().optional(), // e.g., "16px"
    lg: z.string().optional(), // e.g., "24px"
    xl: z.string().optional(), // e.g., "32px"
    custom: z.record(z.string(), z.string()).optional(),
  }).optional(),

  // Border radius scale
  radius: z.object({
    none: z.string().optional(),
    sm: z.string().optional(),
    md: z.string().optional(),
    lg: z.string().optional(),
    xl: z.string().optional(),
    full: z.string().optional(),
    custom: z.record(z.string(), z.string()).optional(),
  }).optional(),

  // Shadow scale
  shadows: z.object({
    sm: z.string().optional(),
    md: z.string().optional(),
    lg: z.string().optional(),
    xl: z.string().optional(),
    custom: z.record(z.string(), z.string()).optional(),
  }).optional(),

  // Per-component styling overrides
  components: z.record(z.string(), ComponentStyleSchema).optional(),

  // Icon library
  icons: z.object({
    library: z.enum(['lucide', 'heroicons', 'feather', 'phosphor', 'tabler', 'custom']),
    custom_path: z.string().optional(), // For custom icon libraries
  }).optional(),
});

/**
 * Development Tools Schema
 * Tools and endpoints for development/testing
 */
const DevelopmentToolsSchema = z.object({
  enabled: z.boolean().default(true),

  // Database reset/seed operations (bypasses API, direct DB access)
  database_operations: z.object({
    reset: z.boolean().default(true), // Enable database reset endpoint
    seed: z.boolean().default(true), // Enable database seed endpoint
    truncate: z.boolean().default(true), // Enable truncate tables endpoint
  }).optional(),

  // Testing helpers
  test_data_generator: z.boolean().default(true), // Generate test fixtures

  // Development-only API endpoints
  dev_endpoints: z.object({
    health_check: z.boolean().default(true),
    database_status: z.boolean().default(true),
    clear_cache: z.boolean().default(true),
  }).optional(),
});

/**
 * Documentation Intent Schema
 * Declarative documentation generation - specify WHAT, not HOW
 */
const DocumentationIntentSchema = z.object({
  type: z.enum(['tutorial', 'methodology', 'troubleshooting', 'api_reference', 'setup_guide', 'best_practices', 'architecture']),
  scope: z.string().optional(),
  topics: z.array(z.string()).optional(),
  includeExamples: z.boolean().optional().default(false),
  includeCommonIssues: z.boolean().optional().default(false),
  audience: z.enum(['user', 'developer', 'both']).optional().default('developer'),
  overwrite: z.enum(['always', 'if-missing', 'never']).optional().default('always'),
  outputPath: z.string().optional(),
});

/**
 * Documentation Schema
 * Supports both intent-based (declarative) and literal (legacy) approaches
 */
const DocumentationSchema = z.object({
  intents: z.array(DocumentationIntentSchema).optional(),
  // Legacy literal documentation (deprecated)
  user_docs: z.array(z.any()).optional(),
  developer_docs: z.array(z.any()).optional(),
  customSections: z.array(z.any()).optional(),
});

/**
 * Technology Stack Schema
 * Specifies concrete technologies to use
 * Made flexible to allow for different technology choices
 */
const TechnologySchema = z.object({
  frontend: z.object({
    framework: z.string().optional(),
    language: z.string().optional(),
    styling: z.string().optional(),
    state_management: z.string().optional(),
  }).optional(),
  backend: z.object({
    framework: z.string().optional(),
    language: z.string().optional(),
    orm: z.string().optional(),
    directory: z.string().optional(), // Custom output directory for backend
  }).optional(),
  database: z.string().optional(), // Simplified to string for flexibility
  cache: z.object({
    provider: z.string(), // e.g., 'upstash-redis', 'redis', 'memcached'
    use: z.enum(['local', 'cloud']).optional(), // Deployment location
    options: z.record(z.string(), z.any()).optional(), // Provider-specific options
  }).optional(),
  testing: z.object({
    unit: z.string().optional(),
    e2e: z.string().optional(),
  }).optional(),

  // Development tools (optional - enabled by default in local/dev stages)
  development: DevelopmentToolsSchema.optional(),
}).passthrough(); // Allow additional properties for future extensibility

/**
 * Extended Workflow Schema
 * Defines extended workflow with additional fields for code generation
 * Note: This is a permissive schema that allows EventDriven fields + extended fields
 */
export const ExtendedWorkflowSchema = z.object({
  // Technology choices (OPTIONAL - but recommended)
  technology: TechnologySchema.optional(),

  // Events (OPTIONAL - enables event-driven architecture)
  events: z.record(z.string(), EventDefinitionSchema).optional(),

  // Data models (OPTIONAL - required for database generation)
  models: z.record(z.string(), DataModelSchema).optional(),

  // Relationships between models (OPTIONAL - helps with complex relations)
  relationships: z.array(RelationshipSchema).optional(),

  // Design system (OPTIONAL - uses defaults if not provided)
  design: z.object({
    theme: DesignThemeSchema.optional(),
    component_library: z.string().optional(), // e.g., "shadcn/ui", "headlessui"
  }).optional(),

  // API endpoints (OPTIONAL - inferred from workflows if not provided)
  api: z.object({
    base_path: z.string().default('/api'),
    endpoints: z.array(APIEndpointSchema).optional(),
  }).optional(),

  // UI specifications (OPTIONAL - inferred from workflows if not provided)
  ui: z.object({
    pages: z.array(UIPageSchema).optional(),
    forms: z.array(UIFormSchema).optional(),
    navigation: z.array(z.object({
      label: z.string(),
      path: z.string(),
      icon: z.string().optional(),
      auth: z.enum(['required', 'optional', 'none']).default('required'),
    })).optional(),
  }).optional(),

  // Authentication (OPTIONAL)
  auth: z.object({
    enabled: z.boolean().default(true),
    provider: z.enum(['local', 'auth0', 'firebase', 'supabase', 'clerk']).optional(),
    strategies: z.array(z.enum(['email', 'google', 'github', 'microsoft'])).optional(),
  }).optional(),

  // Documentation (OPTIONAL - intent-based declarative documentation)
  documentation: DocumentationSchema.optional(),
}).passthrough().superRefine((data: any, ctx: z.RefinementCtx) => {
  // Additional validation beyond the base schema

  // Type helpers for validation
  type RelationshipType = z.infer<typeof RelationshipSchema>;
  type DataModelType = z.infer<typeof DataModelSchema>;
  type FieldDefinitionType = z.infer<typeof FieldDefinitionSchema>;
  type APIEndpointType = z.infer<typeof APIEndpointSchema>;
  type UIFormType = z.infer<typeof UIFormSchema>;

  // Validate model references in relationships
  if (data.relationships && data.models) {
    const modelNames = new Set(Object.keys(data.models));
    (data.relationships as RelationshipType[]).forEach((rel: RelationshipType, index: number) => {
      if (!modelNames.has(rel.from)) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: `Relationship references undefined model '${rel.from}'`,
          path: ['relationships', index, 'from'],
        });
      }
      if (!modelNames.has(rel.to)) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: `Relationship references undefined model '${rel.to}'`,
          path: ['relationships', index, 'to'],
        });
      }
    });
  }

  // Validate foreign key references in models
  if (data.models) {
    Object.entries(data.models as Record<string, DataModelType>).forEach(([modelName, model]: [string, DataModelType]) => {
      Object.entries(model.fields as Record<string, FieldDefinitionType>).forEach(([fieldName, field]: [string, FieldDefinitionType]) => {
        if (field.foreign_key) {
          if (!Object.keys(data.models).includes(field.foreign_key)) {
            ctx.addIssue({
              code: z.ZodIssueCode.custom,
              message: `Field '${fieldName}' in model '${modelName}' references undefined model '${field.foreign_key}'`,
              path: ['models', modelName, 'fields', fieldName, 'foreign_key'],
            });
          }
        }
      });
    });
  }

  // Validate API endpoint components exist
  if (data.api?.endpoints) {
    const componentNames = new Set(data.components ? Object.keys(data.components) : []);

    // Get valid roles from User model
    const userModel = data.models?.User as DataModelType | undefined;
    const validRoles = new Set<string>();
    if (userModel) {
      const roleField = userModel.fields.role as FieldDefinitionType | undefined;
      if (roleField && roleField.type === 'enum' && roleField.enum_values) {
        roleField.enum_values.forEach((role: string) => validRoles.add(role));
      }
    }

    (data.api.endpoints as APIEndpointType[]).forEach((endpoint: APIEndpointType, index: number) => {
      // Validate component exists (only if components are defined)
      if (componentNames.size > 0 && !componentNames.has(endpoint.component)) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: `API endpoint '${endpoint.method} ${endpoint.path}' references undefined component '${endpoint.component}'`,
          path: ['api', 'endpoints', index, 'component'],
        });
      }

      // Validate roles exist in User model
      if (validRoles.size > 0) {
        endpoint.roles?.forEach((role: string) => {
          if (!validRoles.has(role)) {
            ctx.addIssue({
              code: z.ZodIssueCode.custom,
              message: `API endpoint '${endpoint.method} ${endpoint.path}' uses role '${role}' which is not defined in User model's role field. Valid roles: ${Array.from(validRoles).join(', ')}`,
              path: ['api', 'endpoints', index, 'roles'],
            });
          }
        });

        endpoint.exclude_roles?.forEach((role: string) => {
          if (!validRoles.has(role)) {
            ctx.addIssue({
              code: z.ZodIssueCode.custom,
              message: `API endpoint '${endpoint.method} ${endpoint.path}' excludes role '${role}' which is not defined in User model's role field. Valid roles: ${Array.from(validRoles).join(', ')}`,
              path: ['api', 'endpoints', index, 'exclude_roles'],
            });
          }
        });

        endpoint.ownership?.override_roles?.forEach((role: string) => {
          if (!validRoles.has(role)) {
            ctx.addIssue({
              code: z.ZodIssueCode.custom,
              message: `API endpoint '${endpoint.method} ${endpoint.path}' ownership override uses role '${role}' which is not defined in User model's role field. Valid roles: ${Array.from(validRoles).join(', ')}`,
              path: ['api', 'endpoints', index, 'ownership', 'override_roles'],
            });
          }
        });
      }
    });
  }

  // Validate UI form models exist
  if (data.ui?.forms && data.models) {
    const modelNames = new Set(Object.keys(data.models));
    (data.ui.forms as UIFormType[]).forEach((form: UIFormType, index: number) => {
      if (form.model && !modelNames.has(form.model)) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: `Form '${form.id}' references undefined model '${form.model}'`,
          path: ['ui', 'forms', index, 'model'],
        });
      }
    });
  }

  // Validate event references in API endpoints
  if (data.api?.endpoints && data.events) {
    const definedEvents = new Set(Object.keys(data.events));
    (data.api.endpoints as APIEndpointType[]).forEach((endpoint: APIEndpointType, index: number) => {
      if (endpoint.emits && !definedEvents.has(endpoint.emits)) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: `API endpoint '${endpoint.method} ${endpoint.path}' emits undefined event '${endpoint.emits}'. Define this event in the events section.`,
          path: ['api', 'endpoints', index, 'emits'],
        });
      }
    });
  }

  // Warn about events that are never emitted or consumed
  if (data.events) {
    const definedEvents = new Set(Object.keys(data.events));
    const emittedEvents = new Set<string>();
    const consumedEvents = new Set<string>();

    // Collect emitted events from API endpoints
    if (data.api?.endpoints) {
      (data.api.endpoints as APIEndpointType[]).forEach((endpoint: APIEndpointType) => {
        if (endpoint.emits) {
          emittedEvents.add(endpoint.emits);
        }
      });
    }

    // Check for events that are defined but never emitted (informational warning)
    definedEvents.forEach((eventName: string) => {
      if (!emittedEvents.has(eventName) && !consumedEvents.has(eventName)) {
        // This is just a warning, not an error - events might be used in future
        // We'll add this as a console.warn in the parser, not as a validation issue
      }
    });
  }
});

export type ExtendedWorkflow = z.infer<typeof ExtendedWorkflowSchema>;

// Re-export individual schemas for use in generators
export {
  EventDefinitionSchema,
  ComponentEventConfigSchema,
  FieldTypeSchema,
  FieldDefinitionSchema,
  DataModelSchema,
  RelationshipSchema,
  APIEndpointSchema,
  UIPageSchema,
  UIFormSchema,
  DesignThemeSchema,
  TechnologySchema,
  DocumentationSchema,
  DocumentationIntentSchema,
};
