import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import { randomUUID } from 'node:crypto';

registerTool({
  name: 'dev.generateTelemetryDataset',
  description:
    'Generate synthetic telemetry dataset for UX Lab testing and development. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    shape: z
      .enum(['spike', 'gradual-increase', 'sawtooth', 'random-walk', 'realistic'])
      .describe('Pattern shape for the data'),
    size: z
      .number()
      .int()
      .min(10)
      .max(10000)
      .default(1000)
      .describe('Number of events to generate'),
    seed: z.number().int().optional().describe('Random seed for reproducible datasets'),
    productId: z.string().min(1).default('chronomind').describe('Product ID for the events'),
    timeWindow: z
      .object({
        start: z.string().datetime().describe('Start time (ISO datetime)'),
        end: z.string().datetime().describe('End time (ISO datetime)'),
      })
      .optional()
      .describe('Time window for events (defaults to last 24 hours)'),
    errorRate: z.number().min(0).max(1).default(0.05).describe('Error event rate (0.0 to 1.0)'),
    platforms: z
      .array(z.string())
      .default(['web', 'ios', 'android'])
      .describe('Platforms to generate events for'),
    eventTypes: z
      .array(z.string())
      .default(['timer_started', 'timer_completed', 'timer_cancelled'])
      .describe('Event types to generate'),
  }),
  async execute(args, req) {
    const { shape, size, seed, productId, timeWindow, errorRate, platforms, eventTypes } = args;
    const runId = `dataset-${shape}-${Date.now()}`;

    // Use seed for reproducibility
    const rng = seed
      ? (() => {
          let state = seed;
          return () => {
            state = (state * 9301 + 49297) % 233280;
            return state / 233280;
          };
        })()
      : Math.random;

    const now = new Date();
    const start = timeWindow?.start
      ? new Date(timeWindow.start)
      : new Date(now.getTime() - 24 * 60 * 60 * 1000);
    const end = timeWindow?.end ? new Date(timeWindow.end) : now;
    const timeRange = end.getTime() - start.getTime();

    const generateEvents = () => {
      const events = [];

      for (let i = 0; i < size; i++) {
        const isError = rng() < errorRate;
        const platform = platforms[Math.floor(rng() * platforms.length)];
        const eventType = isError
          ? 'error_occurred'
          : eventTypes[Math.floor(rng() * eventTypes.length)];

        let timestamp;
        switch (shape) {
          case 'spike': {
            // Concentrate events around a spike point
            const spikeCenter = start.getTime() + timeRange * 0.7;
            const spikeSpread = timeRange * 0.1;
            timestamp = new Date(spikeCenter + (rng() - 0.5) * spikeSpread * 2);
            break;
          }

          case 'gradual-increase': {
            // Linear increase over time
            const progress = i / size;
            timestamp = new Date(start.getTime() + timeRange * progress);
            break;
          }

          case 'sawtooth': {
            // Multiple peaks and valleys
            const waveProgress = (i / size) * 4; // 4 complete waves
            const waveValue = Math.abs((waveProgress % 2) - 1); // Triangle wave 0-1
            timestamp = new Date(start.getTime() + timeRange * waveValue);
            break;
          }

          case 'random-walk': {
            // Random walk with momentum
            const randomProgress = i / size;
            const randomOffset = (rng() - 0.5) * timeRange * 0.3;
            timestamp = new Date(start.getTime() + timeRange * randomProgress + randomOffset);
            break;
          }

          case 'realistic':
          default: {
            // More natural distribution - fewer events at night
            const hour = new Date(start.getTime() + (i / size) * timeRange).getHours();
            const nightPenalty = hour >= 22 || hour <= 6 ? 0.3 : 1.0;
            const realisticProgress = rng() * rng() * nightPenalty; // Square for more clustering
            timestamp = new Date(start.getTime() + timeRange * realisticProgress);
            break;
          }
        }

        // Ensure timestamp is within bounds
        if (timestamp < start) timestamp = new Date(start);
        if (timestamp > end) timestamp = new Date(end);

        const event = {
          id: randomUUID(),
          productId,
          platform,
          eventType,
          timestamp: timestamp.toISOString(),
          userId: `user_${Math.floor(rng() * 1000)
            .toString()
            .padStart(4, '0')}`,
          sessionId: `session_${Math.floor(rng() * 100)
            .toString()
            .padStart(3, '0')}`,
          version: `1.${Math.floor(rng() * 5)}.${Math.floor(rng() * 10)}`,
          metadata: {
            userAgent: `${platform}-client/${Math.random()}`,
            buildNumber: Math.floor(rng() * 1000).toString(),
            ...(isError && {
              error: {
                message: [
                  'Network timeout',
                  'Validation failed',
                  'Database error',
                  'Auth token expired',
                ][Math.floor(rng() * 4)],
                stack: `Error: Synthetic error ${i}`,
                code: [400, 401, 500, 503][Math.floor(rng() * 4)],
              },
            }),
            ...(!isError && {
              duration: Math.floor(rng() * 5000) + 100, // 100-5100ms
              success: true,
            }),
          },
        };

        events.push(event);
      }

      // Sort by timestamp
      events.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
      return events;
    };

    const events = generateEvents();
    const errorCount = events.filter(e => e.eventType === 'error_occurred').length;
    const platformCounts = platforms.reduce(
      (acc: Record<string, number>, platform: string) => {
        acc[platform] = events.filter(e => e.platform === platform).length;
        return acc;
      },
      {} as Record<string, number>
    );

    const dataset = {
      runId,
      shape,
      size: events.length,
      seed,
      productId,
      timeWindow: {
        start: start.toISOString(),
        end: end.toISOString(),
      },
      errorRate: errorCount / events.length,
      platforms: platformCounts,
      events,
      generatedAt: new Date().toISOString(),
    };

    req.log.info(
      {
        runId,
        shape,
        size: events.length,
        errorCount,
        errorRate: errorCount / events.length,
        platforms: platformCounts,
      },
      'Generated synthetic telemetry dataset'
    );

    return {
      ...dataset,
      summary: `Generated ${events.length} telemetry events with ${errorCount} errors (${((errorCount / events.length) * 100).toFixed(1)}%) across ${platforms.length} platforms`,
    };
  },
});

registerTool({
  name: 'dev.generateUserProfiles',
  description:
    'Generate synthetic user profiles for testing user segmentation and targeting. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    count: z
      .number()
      .int()
      .min(1)
      .max(1000)
      .default(100)
      .describe('Number of user profiles to generate'),
    productId: z.string().min(1).default('chronomind').describe('Product ID'),
    segments: z
      .array(z.string())
      .default(['free', 'premium', 'beta', 'churned'])
      .describe('User segments to distribute across'),
    countries: z
      .array(z.string())
      .default(['US', 'CA', 'GB', 'DE', 'FR', 'JP', 'AU'])
      .describe('Country distribution'),
    seed: z.number().int().optional().describe('Random seed for reproducible results'),
  }),
  async execute(args, req) {
    const { count, productId, segments, countries, seed } = args;
    const runId = `profiles-${Date.now()}`;

    const rng = seed
      ? (() => {
          let state = seed;
          return () => {
            state = (state * 9301 + 49297) % 233280;
            return state / 233280;
          };
        })()
      : Math.random;

    const generateProfiles = () => {
      const profiles = [];

      for (let i = 0; i < count; i++) {
        const segment = segments[Math.floor(rng() * segments.length)];
        const country = countries[Math.floor(rng() * countries.length)];

        // Segment-specific characteristics
        let subscriptionTier = 'free';
        let engagementScore: number;

        // Segment-specific characteristics
        switch (segment) {
          case 'premium':
            subscriptionTier = 'premium';
            engagementScore = 50 + rng() * 50; // 50-100
            break;
          case 'beta':
            subscriptionTier = rng() > 0.5 ? 'premium' : 'free';
            engagementScore = 70 + rng() * 30; // 70-100
            break;
          case 'churned': {
            engagementScore = rng() * 20; // 0-20
            break;
          }
          default: // free
            engagementScore = rng() * 60; // 0-60
        }

        const createdAt = new Date(Date.now() - rng() * 365 * 24 * 60 * 60 * 1000); // Random date in last year
        const lastActiveAt =
          segment === 'churned'
            ? new Date(Date.now() - rng() * 90 * 24 * 60 * 60 * 1000)
            : new Date(createdAt.getTime() + rng() * (Date.now() - createdAt.getTime()));

        const profile = {
          userId: `user_${(i + 1).toString().padStart(4, '0')}`,
          email: `user${i + 1}@example.com`,
          productId,
          segment,
          subscriptionTier,
          country,
          createdAt: createdAt.toISOString(),
          lastActiveAt: lastActiveAt.toISOString(),
          engagementScore: Math.round(engagementScore),
          properties: {
            platform: ['web', 'ios', 'android'][Math.floor(rng() * 3)],
            version: `1.${Math.floor(rng() * 5)}.${Math.floor(rng() * 10)}`,
            totalSessions: Math.floor(rng() * 1000),
            totalValue: rng() > 0.8 ? Math.floor(rng() * 10000) : undefined, // 20% have monetary value
            preferences: {
              notifications: rng() > 0.3,
              darkMode: rng() > 0.5,
              language: ['en', 'es', 'fr', 'de', 'ja'][Math.floor(rng() * 5)],
            },
          },
        };

        profiles.push(profile);
      }

      return profiles;
    };

    const profiles = generateProfiles();
    const segmentCounts = segments.reduce(
      (acc: Record<string, number>, segment: string) => {
        acc[segment] = profiles.filter(p => p.segment === segment).length;
        return acc;
      },
      {} as Record<string, number>
    );
    const countryCounts = countries.reduce(
      (acc: Record<string, number>, country: string) => {
        acc[country] = profiles.filter(p => p.country === country).length;
        return acc;
      },
      {} as Record<string, number>
    );

    req.log.info(
      {
        runId,
        count: profiles.length,
        segments: segmentCounts,
        countries: countryCounts,
      },
      'Generated synthetic user profiles'
    );

    return {
      runId,
      productId,
      profiles,
      count: profiles.length,
      segments: segmentCounts,
      countries: countryCounts,
      generatedAt: new Date().toISOString(),
      summary: `Generated ${profiles.length} user profiles across ${segments.length} segments and ${countries.length} countries`,
    };
  },
});
