/**
 * Mobile Session Controller
 *
 * REST API endpoints for mobile session management
 *
 * @module Mobile
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import { MobileSessionService } from '../services/mobile-session.service';
import {
  CreateMobileSessionDto,
  UpdateMobileSessionDto,
  UpdateLocationDto,
  MobileSessionFilterDto,
} from '../dto/mobile-session.dto';

interface AuthenticatedRequest extends Request {
  tenantId?: string;
  userId?: string;
}

export class MobileSessionController {
  public router: Router;
  private service: MobileSessionService;

  constructor(dataSource: DataSource) {
    this.router = Router();
    this.service = new MobileSessionService(dataSource);
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Session CRUD
    this.router.get('/', this.findAll.bind(this));
    this.router.get('/active', this.getActiveSessions.bind(this));
    this.router.get('/offline', this.getOfflineSessions.bind(this));
    this.router.get('/device/:deviceId', this.getByDevice.bind(this));
    this.router.get('/user/:userId', this.getByUser.bind(this));
    this.router.get('/:id', this.getById.bind(this));
    this.router.post('/', this.create.bind(this));
    this.router.put('/:id', this.update.bind(this));
    this.router.delete('/:id', this.terminate.bind(this));

    // Session actions
    this.router.post('/:id/activity', this.recordActivity.bind(this));
    this.router.post('/:id/location', this.updateLocation.bind(this));
    this.router.post('/:id/offline', this.setOfflineMode.bind(this));
    this.router.post('/:id/online', this.setOnlineMode.bind(this));
    this.router.post('/:id/sync-status', this.updateSyncStatus.bind(this));

    // Maintenance
    this.router.post('/expire-old', this.expireOldSessions.bind(this));
    this.router.post('/device/:deviceId/terminate-all', this.terminateDeviceSessions.bind(this));
  }

  /**
   * GET /mobile/sessions
   * Get all sessions with filters
   */
  private async findAll(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const filter: MobileSessionFilterDto = {
        userId: req.query.userId as string,
        deviceId: req.query.deviceId as string,
        branchId: req.query.branchId as string,
        status: req.query.status as any,
        isOfflineMode: req.query.isOfflineMode === 'true' ? true : req.query.isOfflineMode === 'false' ? false : undefined,
        limit: req.query.limit ? parseInt(req.query.limit as string, 10) : undefined,
        offset: req.query.offset ? parseInt(req.query.offset as string, 10) : undefined,
      };

      const result = await this.service.findAll({ tenantId: req.tenantId! }, filter);
      res.json({
        data: result.data.map(s => this.service.toResponseDto(s)),
        total: result.total,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /mobile/sessions/active
   * Get active sessions count
   */
  private async getActiveSessions(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const count = await this.service.getActiveSessionsCount({ tenantId: req.tenantId! });
      res.json({ data: { count } });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /mobile/sessions/offline
   * Get sessions in offline mode
   */
  private async getOfflineSessions(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const sessions = await this.service.getOfflineSessions({ tenantId: req.tenantId! });
      res.json({ data: sessions.map(s => this.service.toResponseDto(s)) });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /mobile/sessions/device/:deviceId
   * Get active session for device
   */
  private async getByDevice(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const session = await this.service.findActiveByDevice(
        { tenantId: req.tenantId! },
        req.params.deviceId
      );

      if (!session) {
        res.status(404).json({ error: 'No active session found for device' });
        return;
      }

      res.json({ data: this.service.toResponseDto(session) });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /mobile/sessions/user/:userId
   * Get sessions for user
   */
  private async getByUser(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const sessions = await this.service.findByUser(
        { tenantId: req.tenantId! },
        req.params.userId
      );
      res.json({ data: sessions.map(s => this.service.toResponseDto(s)) });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /mobile/sessions/:id
   * Get session by ID
   */
  private async getById(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const session = await this.service.findById(
        { tenantId: req.tenantId! },
        req.params.id
      );

      if (!session) {
        res.status(404).json({ error: 'Session not found' });
        return;
      }

      res.json({ data: this.service.toResponseDto(session) });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /mobile/sessions
   * Create new session
   */
  private async create(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: CreateMobileSessionDto = req.body;
      const session = await this.service.create({ tenantId: req.tenantId! }, dto);
      res.status(201).json({ data: this.service.toResponseDto(session) });
    } catch (error) {
      next(error);
    }
  }

  /**
   * PUT /mobile/sessions/:id
   * Update session
   */
  private async update(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: UpdateMobileSessionDto = req.body;
      const session = await this.service.update(
        { tenantId: req.tenantId! },
        req.params.id,
        dto
      );
      res.json({ data: this.service.toResponseDto(session) });
    } catch (error) {
      next(error);
    }
  }

  /**
   * DELETE /mobile/sessions/:id
   * Terminate session
   */
  private async terminate(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await this.service.terminate({ tenantId: req.tenantId! }, req.params.id);
      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /mobile/sessions/:id/activity
   * Record activity (heartbeat)
   */
  private async recordActivity(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const session = await this.service.recordActivity(
        { tenantId: req.tenantId! },
        req.params.id
      );
      res.json({ data: this.service.toResponseDto(session) });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /mobile/sessions/:id/location
   * Update location
   */
  private async updateLocation(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: UpdateLocationDto = req.body;
      const session = await this.service.updateLocation(
        { tenantId: req.tenantId! },
        req.params.id,
        dto
      );
      res.json({ data: this.service.toResponseDto(session) });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /mobile/sessions/:id/offline
   * Set offline mode
   */
  private async setOfflineMode(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const session = await this.service.setOfflineMode(
        { tenantId: req.tenantId! },
        req.params.id,
        true
      );
      res.json({ data: this.service.toResponseDto(session) });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /mobile/sessions/:id/online
   * Set online mode
   */
  private async setOnlineMode(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const session = await this.service.setOfflineMode(
        { tenantId: req.tenantId! },
        req.params.id,
        false
      );
      res.json({ data: this.service.toResponseDto(session) });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /mobile/sessions/:id/sync-status
   * Update sync status
   */
  private async updateSyncStatus(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { pendingCount } = req.body;
      const session = await this.service.updateSyncStatus(
        { tenantId: req.tenantId! },
        req.params.id,
        pendingCount
      );
      res.json({ data: this.service.toResponseDto(session) });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /mobile/sessions/expire-old
   * Expire old sessions (maintenance endpoint)
   */
  private async expireOldSessions(_req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const count = await this.service.expireOldSessions();
      res.json({ data: { expired: count } });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /mobile/sessions/device/:deviceId/terminate-all
   * Terminate all sessions for device
   */
  private async terminateDeviceSessions(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const count = await this.service.terminateDeviceSessions(req.params.deviceId);
      res.json({ data: { terminated: count } });
    } catch (error) {
      next(error);
    }
  }
}
