/**
 * Usage Controller
 * API endpoints for usage tracking and events
 *
 * @module BillingUsage
 * @prefix /api/v1/billing/usage
 */

import { Router, Request, Response, NextFunction } from 'express';
import {
  UsageTrackingService,
  UpdateUsageTrackingDto,
} from '../services/usage-tracking.service';
import {
  UsageEventService,
  CreateUsageEventDto,
} from '../services/usage-event.service';

const router = Router();
const trackingService = new UsageTrackingService();
const eventService = new UsageEventService();

/**
 * GET /api/v1/billing/usage/current
 * Get current period usage
 */
router.get('/current', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const usage = await trackingService.getCurrentPeriod({ tenantId });
    if (!usage) {
      return res.status(404).json({ error: 'No usage data for current period' });
    }

    return res.json({ success: true, data: usage });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/usage/history
 * Get usage history
 */
router.get('/history', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const months = parseInt(req.query.months as string) || 12;
    const history = await trackingService.getUsageHistory({ tenantId }, months);

    return res.json({
      success: true,
      data: history,
      count: history.length,
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/usage/summary
 * Get usage summary for a date range
 */
router.get('/summary', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { startDate, endDate } = req.query;

    if (!startDate || !endDate) {
      return res.status(400).json({ error: 'startDate and endDate query parameters required' });
    }

    const summary = await trackingService.getUsageSummary(
      { tenantId },
      new Date(startDate as string),
      new Date(endDate as string)
    );

    return res.json({ success: true, data: summary });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/usage/trend/:metric
 * Get usage trend for a specific metric
 */
router.get('/trend/:metric', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { metric } = req.params;
    const periods = parseInt(req.query.periods as string) || 6;

    const validMetrics = [
      'activeUsers',
      'activeBranches',
      'storageUsedGb',
      'apiCalls',
      'salesCount',
      'salesAmount',
      'documentsCount',
      'mobileSessions',
    ];

    if (!validMetrics.includes(metric)) {
      return res.status(400).json({ error: `Invalid metric. Valid metrics: ${validMetrics.join(', ')}` });
    }

    const trend = await trackingService.getUsageTrend({ tenantId }, metric as any, periods);

    return res.json({ success: true, data: trend });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/usage/initialize
 * Initialize a new usage period
 */
router.post('/initialize', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { periodStart, periodEnd } = req.body;

    if (!periodStart || !periodEnd) {
      return res.status(400).json({ error: 'periodStart and periodEnd are required' });
    }

    const usage = await trackingService.initializePeriod(
      { tenantId },
      new Date(periodStart),
      new Date(periodEnd)
    );

    return res.status(201).json({ success: true, data: usage });
  } catch (error) {
    return next(error);
  }
});

/**
 * PATCH /api/v1/billing/usage/current
 * Update current period usage
 */
router.patch('/current', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const data: UpdateUsageTrackingDto = req.body;
    const usage = await trackingService.updateCurrentPeriod({ tenantId }, data);

    if (!usage) {
      return res.status(404).json({ error: 'No usage data for current period' });
    }

    return res.json({ success: true, data: usage });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/usage/increment
 * Increment a usage metric
 */
router.post('/increment', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { metric, value = 1 } = req.body;

    if (!metric) {
      return res.status(400).json({ error: 'metric is required' });
    }

    const usage = await trackingService.incrementMetric({ tenantId }, metric, value);

    if (!usage) {
      return res.status(404).json({ error: 'No usage data for current period' });
    }

    return res.json({ success: true, data: usage });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/usage/aggregate
 * Aggregate usage from events
 */
router.post('/aggregate', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { periodStart, periodEnd } = req.body;

    if (!periodStart || !periodEnd) {
      return res.status(400).json({ error: 'periodStart and periodEnd are required' });
    }

    const usage = await trackingService.aggregateFromEvents(
      { tenantId },
      new Date(periodStart),
      new Date(periodEnd)
    );

    if (!usage) {
      return res.status(404).json({ error: 'Usage tracking record not found' });
    }

    return res.json({ success: true, data: usage });
  } catch (error) {
    return next(error);
  }
});

// Usage Events

/**
 * GET /api/v1/billing/usage/events
 * Get usage events
 */
router.get('/events', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const {
      eventType,
      eventCategory,
      userId,
      branchId,
      platform,
      startDate,
      endDate,
      limit = '100',
      offset = '0',
    } = req.query;

    const result = await eventService.findByFilters(
      { tenantId },
      {
        eventType: eventType as string,
        eventCategory: eventCategory as any,
        userId: userId as string,
        branchId: branchId as string,
        platform: platform as string,
        startDate: startDate ? new Date(startDate as string) : undefined,
        endDate: endDate ? new Date(endDate as string) : undefined,
      },
      parseInt(limit as string),
      parseInt(offset as string)
    );

    return res.json({
      success: true,
      data: result.events,
      total: result.total,
      limit: parseInt(limit as string),
      offset: parseInt(offset as string),
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/usage/events/recent
 * Get recent events
 */
router.get('/events/recent', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const limit = parseInt(req.query.limit as string) || 50;
    const events = await eventService.getRecentEvents({ tenantId }, limit);

    return res.json({
      success: true,
      data: events,
      count: events.length,
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/usage/events/by-category
 * Get event counts by category
 */
router.get('/events/by-category', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { startDate, endDate } = req.query;

    if (!startDate || !endDate) {
      return res.status(400).json({ error: 'startDate and endDate query parameters required' });
    }

    const counts = await eventService.countByCategory(
      { tenantId },
      new Date(startDate as string),
      new Date(endDate as string)
    );

    return res.json({ success: true, data: counts });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/usage/events/by-type
 * Get event counts by type
 */
router.get('/events/by-type', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { startDate, endDate } = req.query;

    if (!startDate || !endDate) {
      return res.status(400).json({ error: 'startDate and endDate query parameters required' });
    }

    const counts = await eventService.countByEventType(
      { tenantId },
      new Date(startDate as string),
      new Date(endDate as string)
    );

    return res.json({ success: true, data: counts });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/usage/api
 * Get API usage statistics
 */
router.get('/api', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { startDate, endDate } = req.query;

    if (!startDate || !endDate) {
      return res.status(400).json({ error: 'startDate and endDate query parameters required' });
    }

    const apiUsage = await eventService.getApiUsage(
      { tenantId },
      new Date(startDate as string),
      new Date(endDate as string)
    );

    return res.json({ success: true, data: apiUsage });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/usage/storage
 * Get storage usage statistics
 */
router.get('/storage', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { startDate, endDate } = req.query;

    if (!startDate || !endDate) {
      return res.status(400).json({ error: 'startDate and endDate query parameters required' });
    }

    const storageUsage = await eventService.getStorageUsage(
      { tenantId },
      new Date(startDate as string),
      new Date(endDate as string)
    );

    return res.json({ success: true, data: storageUsage });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/usage/users
 * Get user activity statistics
 */
router.get('/users', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { startDate, endDate } = req.query;

    if (!startDate || !endDate) {
      return res.status(400).json({ error: 'startDate and endDate query parameters required' });
    }

    const userActivity = await eventService.getUserActivity(
      { tenantId },
      new Date(startDate as string),
      new Date(endDate as string)
    );

    return res.json({ success: true, data: userActivity });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/usage/events
 * Record a usage event
 */
router.post('/events', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const data: CreateUsageEventDto = req.body;

    if (!data.eventType || !data.eventCategory) {
      return res.status(400).json({ error: 'eventType and eventCategory are required' });
    }

    const event = await eventService.record({ tenantId }, data);
    return res.status(201).json({ success: true, data: event });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/usage/events/batch
 * Record multiple usage events
 */
router.post('/events/batch', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { events } = req.body;

    if (!events || !Array.isArray(events)) {
      return res.status(400).json({ error: 'events array is required' });
    }

    const savedEvents = await eventService.recordBatch({ tenantId }, events);
    return res.status(201).json({
      success: true,
      data: savedEvents,
      count: savedEvents.length,
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/usage/events/login
 * Record login event
 */
router.post('/events/login', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { userId, profileCode, platform, metadata } = req.body;

    if (!userId || !profileCode || !platform) {
      return res.status(400).json({ error: 'userId, profileCode, and platform are required' });
    }

    const event = await eventService.recordLogin(
      { tenantId },
      userId,
      profileCode,
      platform,
      metadata
    );

    return res.status(201).json({ success: true, data: event });
  } catch (error) {
    return next(error);
  }
});

export default router;
