import { Router, Response } from 'express';
import { z } from 'zod';
import db from '../db';
import { AuthRequest } from '../middleware/auth';
import { checkLimiter } from '../middleware/rateLimiter';
import { MonitorFrequency, Monitor } from '../types';
import { checkMonitor, scheduleMonitor, unscheduleMonitor, rescheduleMonitor } from '../services/monitor';

const router = Router();

const createMonitorSchema = z.object({
  url: z.string().url(),
  name: z.string().optional(),
  frequency: z.number().int().positive(),
  elementSelector: z.string().optional(),
  ignoreRules: z
    .array(
      z.object({
        type: z.enum(['css', 'regex', 'text']),
        value: z.string(),
      })
    )
    .optional(),
  keywordRules: z
    .array(
      z.object({
        keyword: z.string(),
        type: z.enum(['appears', 'disappears', 'count']),
        threshold: z.number().optional(),
        caseSensitive: z.boolean().optional(),
      })
    )
    .optional(),
});

const updateMonitorSchema = z.object({
  name: z.string().optional(),
  frequency: z.number().int().positive().optional(),
  status: z.enum(['active', 'paused', 'error']).optional(),
  elementSelector: z.string().optional(),
  ignoreRules: z
    .array(
      z.object({
        type: z.enum(['css', 'regex', 'text']),
        value: z.string(),
      })
    )
    .optional(),
  keywordRules: z
    .array(
      z.object({
        keyword: z.string(),
        type: z.enum(['appears', 'disappears', 'count']),
        threshold: z.number().optional(),
        caseSensitive: z.boolean().optional(),
      })
    )
    .optional(),
});

// Get plan limits
function getPlanLimits(plan: string) {
  const limits = {
    free: {
      maxMonitors: parseInt(process.env.MAX_MONITORS_FREE || '5'),
      minFrequency: parseInt(process.env.MIN_FREQUENCY_FREE || '60'),
    },
    pro: {
      maxMonitors: parseInt(process.env.MAX_MONITORS_PRO || '50'),
      minFrequency: parseInt(process.env.MIN_FREQUENCY_PRO || '5'),
    },
    business: {
      maxMonitors: parseInt(process.env.MAX_MONITORS_BUSINESS || '200'),
      minFrequency: parseInt(process.env.MIN_FREQUENCY_BUSINESS || '1'),
    },
    enterprise: {
      maxMonitors: 999999,
      minFrequency: 1,
    },
  };

  return limits[plan as keyof typeof limits] || limits.free;
}

// List monitors
router.get('/', async (req: AuthRequest, res: Response): Promise<void> => {
  try {
    if (!req.user) {
      res.status(401).json({ error: 'unauthorized', message: 'Not authenticated' });
      return;
    }

    const monitors = await db.monitors.findByUserId(req.user.userId);

    // Attach recent snapshots to each monitor for sparklines
    const monitorsWithSnapshots = await Promise.all(monitors.map(async (monitor) => {
      // Get last 20 snapshots for sparkline
      const recentSnapshots = await db.snapshots.findByMonitorId(monitor.id, 20);
      return {
        ...monitor,
        recentSnapshots
      };
    }));

    res.json({ monitors: monitorsWithSnapshots });
  } catch (error) {
    console.error('List monitors error:', error);
    res.status(500).json({ error: 'server_error', message: 'Failed to list monitors' });
  }
});

// Get monitor by ID
router.get('/:id', async (req: AuthRequest, res: Response): Promise<void> => {
  try {
    if (!req.user) {
      res.status(401).json({ error: 'unauthorized', message: 'Not authenticated' });
      return;
    }

    const monitor = await db.monitors.findById(req.params.id);

    if (!monitor) {
      res.status(404).json({ error: 'not_found', message: 'Monitor not found' });
      return;
    }

    if (monitor.userId !== req.user.userId) {
      res.status(403).json({ error: 'forbidden', message: 'Access denied' });
      return;
    }

    res.json({ monitor });
  } catch (error) {
    console.error('Get monitor error:', error);
    res.status(500).json({ error: 'server_error', message: 'Failed to get monitor' });
  }
});

// Create monitor
router.post('/', async (req: AuthRequest, res: Response): Promise<void> => {
  try {
    if (!req.user) {
      res.status(401).json({ error: 'unauthorized', message: 'Not authenticated' });
      return;
    }

    const input = createMonitorSchema.parse(req.body);

    // Check plan limits (fetch fresh user data)
    const currentUser = await db.users.findById(req.user.userId);
    const plan = currentUser?.plan || req.user.plan;
    const limits = getPlanLimits(plan);
    const currentCount = await db.monitors.countByUserId(req.user.userId);

    if (currentCount >= limits.maxMonitors) {
      res.status(403).json({
        error: 'limit_exceeded',
        message: `Your ${plan} plan allows max ${limits.maxMonitors} monitors`,
      });
      return;
    }

    if (input.frequency < limits.minFrequency) {
      res.status(403).json({
        error: 'invalid_frequency',
        message: `Your ${plan} plan requires minimum ${limits.minFrequency} minute frequency`,
      });
      return;
    }

    // Extract domain from URL for name if not provided
    const name = input.name || new URL(input.url).hostname;

    const monitor = await db.monitors.create({
      userId: req.user.userId,
      url: input.url,
      name,
      frequency: input.frequency as MonitorFrequency,
      status: 'active',
      elementSelector: input.elementSelector,
      ignoreRules: input.ignoreRules,
      keywordRules: input.keywordRules,
    });

    // Schedule recurring checks
    try {
      await scheduleMonitor(monitor);
      console.log(`Monitor ${monitor.id} scheduled successfully`);
    } catch (err) {
      console.error('Failed to schedule monitor:', err);
    }

    // Perform first check immediately
    checkMonitor(monitor.id).catch((err) =>
      console.error('Initial check failed:', err)
    );

    res.status(201).json({ monitor });
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({
        error: 'validation_error',
        message: 'Invalid input',
        details: error.errors,
      });
      return;
    }

    console.error('Create monitor error:', error);
    res.status(500).json({ error: 'server_error', message: 'Failed to create monitor' });
  }
});

// Update monitor
router.put('/:id', async (req: AuthRequest, res: Response): Promise<void> => {
  try {
    if (!req.user) {
      res.status(401).json({ error: 'unauthorized', message: 'Not authenticated' });
      return;
    }

    const monitor = await db.monitors.findById(req.params.id);

    if (!monitor) {
      res.status(404).json({ error: 'not_found', message: 'Monitor not found' });
      return;
    }

    if (monitor.userId !== req.user.userId) {
      res.status(403).json({ error: 'forbidden', message: 'Access denied' });
      return;
    }

    const input = updateMonitorSchema.parse(req.body);

    // Check frequency limit if being updated
    if (input.frequency) {
      // Fetch fresh user data to get current plan
      const currentUser = await db.users.findById(req.user.userId);
      const plan = currentUser?.plan || req.user.plan;
      const limits = getPlanLimits(plan);

      if (input.frequency < limits.minFrequency) {
        res.status(403).json({
          error: 'invalid_frequency',
          message: `Your ${plan} plan requires minimum ${limits.minFrequency} minute frequency`,
        });
        return;
      }
    }

    const updateData: Partial<Monitor> = {
      ...input,
      frequency: input.frequency as MonitorFrequency | undefined,
    };
    const updated = await db.monitors.update(req.params.id, updateData);

    if (!updated) {
      res.status(500).json({ error: 'update_failed', message: 'Failed to update monitor' });
      return;
    }

    // Reschedule if frequency changed or status changed to/from active
    const needsRescheduling =
      input.frequency !== undefined ||
      (input.status && (input.status === 'active' || monitor.status === 'active'));

    if (needsRescheduling) {
      try {
        if (updated.status === 'active') {
          await rescheduleMonitor(updated);
          console.log(`Monitor ${updated.id} rescheduled`);
        } else {
          await unscheduleMonitor(updated.id);
          console.log(`Monitor ${updated.id} unscheduled (status: ${updated.status})`);
        }
      } catch (err) {
        console.error('Failed to reschedule monitor:', err);
      }
    }

    res.json({ monitor: updated });
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({
        error: 'validation_error',
        message: 'Invalid input',
        details: error.errors,
      });
      return;
    }

    console.error('Update monitor error:', error);
    res.status(500).json({ error: 'server_error', message: 'Failed to update monitor' });
  }
});

// Delete monitor
router.delete('/:id', async (req: AuthRequest, res: Response): Promise<void> => {
  try {
    if (!req.user) {
      res.status(401).json({ error: 'unauthorized', message: 'Not authenticated' });
      return;
    }

    const monitor = await db.monitors.findById(req.params.id);

    if (!monitor) {
      res.status(404).json({ error: 'not_found', message: 'Monitor not found' });
      return;
    }

    if (monitor.userId !== req.user.userId) {
      res.status(403).json({ error: 'forbidden', message: 'Access denied' });
      return;
    }

    // Unschedule before deleting
    try {
      await unscheduleMonitor(req.params.id);
      console.log(`Monitor ${req.params.id} unscheduled before deletion`);
    } catch (err) {
      console.error('Failed to unschedule monitor:', err);
    }

    await db.monitors.delete(req.params.id);

    res.json({ message: 'Monitor deleted successfully' });
  } catch (error) {
    console.error('Delete monitor error:', error);
    res.status(500).json({ error: 'server_error', message: 'Failed to delete monitor' });
  }
});

// Trigger manual check
router.post('/:id/check', checkLimiter, async (req: AuthRequest, res: Response): Promise<void> => {
  try {
    if (!req.user) {
      res.status(401).json({ error: 'unauthorized', message: 'Not authenticated' });
      return;
    }

    const monitor = await db.monitors.findById(req.params.id);

    if (!monitor) {
      res.status(404).json({ error: 'not_found', message: 'Monitor not found' });
      return;
    }

    if (monitor.userId !== req.user.userId) {
      res.status(403).json({ error: 'forbidden', message: 'Access denied' });
      return;
    }

    // Await the check so user gets immediate feedback
    try {
      await checkMonitor(monitor.id);

      // Get the latest snapshot to return to the user
      const latestSnapshot = await db.snapshots.findLatestByMonitorId(monitor.id);
      const updatedMonitor = await db.monitors.findById(monitor.id);

      res.json({
        message: 'Check completed successfully',
        monitor: updatedMonitor,
        snapshot: latestSnapshot ? {
          id: latestSnapshot.id,
          changed: latestSnapshot.changed,
          changePercentage: latestSnapshot.changePercentage,
          httpStatus: latestSnapshot.httpStatus,
          responseTime: latestSnapshot.responseTime,
          createdAt: latestSnapshot.createdAt,
          errorMessage: latestSnapshot.errorMessage,
        } : null,
      });
    } catch (checkError: any) {
      console.error('Check failed:', checkError);
      res.status(500).json({
        error: 'check_failed',
        message: checkError.message || 'Failed to check monitor'
      });
    }
  } catch (error) {
    console.error('Trigger check error:', error);
    res.status(500).json({ error: 'server_error', message: 'Failed to trigger check' });
  }
});

// Get monitor history (snapshots)
router.get('/:id/history', async (req: AuthRequest, res: Response): Promise<void> => {
  try {
    if (!req.user) {
      res.status(401).json({ error: 'unauthorized', message: 'Not authenticated' });
      return;
    }

    const monitor = await db.monitors.findById(req.params.id);

    if (!monitor) {
      res.status(404).json({ error: 'not_found', message: 'Monitor not found' });
      return;
    }

    if (monitor.userId !== req.user.userId) {
      res.status(403).json({ error: 'forbidden', message: 'Access denied' });
      return;
    }

    const limit = Math.min(parseInt(req.query.limit as string) || 50, 100);
    const snapshots = await db.snapshots.findByMonitorId(req.params.id, limit);

    res.json({ snapshots });
  } catch (error) {
    console.error('Get history error:', error);
    res.status(500).json({ error: 'server_error', message: 'Failed to get history' });
  }
});

// Get specific snapshot
router.get(
  '/:id/history/:snapshotId',
  async (req: AuthRequest, res: Response): Promise<void> => {
    try {
      if (!req.user) {
        res.status(401).json({ error: 'unauthorized', message: 'Not authenticated' });
        return;
      }

      const monitor = await db.monitors.findById(req.params.id);

      if (!monitor) {
        res.status(404).json({ error: 'not_found', message: 'Monitor not found' });
        return;
      }

      if (monitor.userId !== req.user.userId) {
        res.status(403).json({ error: 'forbidden', message: 'Access denied' });
        return;
      }

      const snapshot = await db.snapshots.findById(req.params.snapshotId);

      if (!snapshot || snapshot.monitorId !== req.params.id) {
        res.status(404).json({ error: 'not_found', message: 'Snapshot not found' });
        return;
      }

      res.json({ snapshot });
    } catch (error) {
      console.error('Get snapshot error:', error);
      res.status(500).json({ error: 'server_error', message: 'Failed to get snapshot' });
    }
  }
);

// Export monitor audit trail (JSON or CSV)
router.get('/:id/export', async (req: AuthRequest, res: Response): Promise<void> => {
  try {
    if (!req.user) {
      res.status(401).json({ error: 'unauthorized', message: 'Not authenticated' });
      return;
    }

    // Check if user has export feature (PRO+)
    const user = await db.users.findById(req.user.userId);
    if (!user) {
      res.status(404).json({ error: 'not_found', message: 'User not found' });
      return;
    }

    // Allow export for all users for now, but in production check plan
    // if (!hasFeature(user.plan, 'audit_export')) {
    //   res.status(403).json({ error: 'forbidden', message: 'Export feature requires Pro plan' });
    //   return;
    // }

    const monitor = await db.monitors.findById(req.params.id);

    if (!monitor) {
      res.status(404).json({ error: 'not_found', message: 'Monitor not found' });
      return;
    }

    if (monitor.userId !== req.user.userId) {
      res.status(403).json({ error: 'forbidden', message: 'Access denied' });
      return;
    }

    const format = (req.query.format as string)?.toLowerCase() || 'json';
    const fromDate = req.query.from ? new Date(req.query.from as string) : undefined;
    const toDate = req.query.to ? new Date(req.query.to as string) : undefined;

    // Get all snapshots (up to 1000)
    let snapshots = await db.snapshots.findByMonitorId(monitor.id, 1000);

    // Filter by date range if provided
    if (fromDate) {
      snapshots = snapshots.filter(s => new Date(s.createdAt) >= fromDate);
    }
    if (toDate) {
      snapshots = snapshots.filter(s => new Date(s.createdAt) <= toDate);
    }

    // Get alerts for this monitor
    const allAlerts = await db.alerts.findByUserId(req.user.userId, 1000);
    const monitorAlerts = allAlerts.filter(a => a.monitorId === monitor.id);

    // Filter alerts by date range if provided
    let filteredAlerts = monitorAlerts;
    if (fromDate) {
      filteredAlerts = filteredAlerts.filter(a => new Date(a.createdAt) >= fromDate);
    }
    if (toDate) {
      filteredAlerts = filteredAlerts.filter(a => new Date(a.createdAt) <= toDate);
    }

    const exportData = {
      monitor: {
        id: monitor.id,
        name: monitor.name,
        url: monitor.url,
        frequency: monitor.frequency,
        status: monitor.status,
        createdAt: monitor.createdAt,
      },
      exportedAt: new Date().toISOString(),
      dateRange: {
        from: fromDate?.toISOString() || 'start',
        to: toDate?.toISOString() || 'now',
      },
      summary: {
        totalChecks: snapshots.length,
        changesDetected: snapshots.filter(s => s.changed).length,
        errorsDetected: snapshots.filter(s => s.errorMessage).length,
        totalAlerts: filteredAlerts.length,
      },
      checks: snapshots.map(s => ({
        id: s.id,
        timestamp: s.createdAt,
        changed: s.changed,
        changePercentage: s.changePercentage,
        httpStatus: s.httpStatus,
        responseTime: s.responseTime,
        errorMessage: s.errorMessage,
      })),
      alerts: filteredAlerts.map(a => ({
        id: a.id,
        type: a.type,
        title: a.title,
        summary: a.summary,
        channels: a.channels,
        createdAt: a.createdAt,
        deliveredAt: a.deliveredAt,
      })),
    };

    if (format === 'csv') {
      // Generate CSV
      const csvLines: string[] = [];

      // Header
      csvLines.push('Type,Timestamp,Changed,Change %,HTTP Status,Response Time (ms),Error,Alert Type,Alert Title');

      // Checks
      for (const check of exportData.checks) {
        csvLines.push([
          'check',
          check.timestamp,
          check.changed ? 'true' : 'false',
          check.changePercentage?.toFixed(2) || '',
          check.httpStatus,
          check.responseTime,
          `"${(check.errorMessage || '').replace(/"/g, '""')}"`,
          '',
          '',
        ].join(','));
      }

      // Alerts
      for (const alert of exportData.alerts) {
        csvLines.push([
          'alert',
          alert.createdAt,
          '',
          '',
          '',
          '',
          '',
          alert.type,
          `"${(alert.title || '').replace(/"/g, '""')}"`,
        ].join(','));
      }

      const csv = csvLines.join('\n');
      const filename = `${monitor.name.replace(/[^a-zA-Z0-9]/g, '_')}_audit_${new Date().toISOString().split('T')[0]}.csv`;

      res.setHeader('Content-Type', 'text/csv');
      res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
      res.send(csv);
    } else {
      // JSON format
      const filename = `${monitor.name.replace(/[^a-zA-Z0-9]/g, '_')}_audit_${new Date().toISOString().split('T')[0]}.json`;

      res.setHeader('Content-Type', 'application/json');
      res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
      res.json(exportData);
    }
  } catch (error) {
    console.error('Export error:', error);
    res.status(500).json({ error: 'server_error', message: 'Failed to export audit trail' });
  }
});

export default router;
