import { Response, NextFunction } from 'express';
import { z } from 'zod';
import {
  mobileService,
  CreateSessionDto,
  RegisterPushTokenDto,
  AddToQueueDto,
  SessionFilters,
  QueueFilters,
} from './services/index.js';
import { ApiResponse, AuthenticatedRequest, ValidationError } from '../../shared/types/index.js';

// ===== Zod Validation Schemas =====

const createSessionSchema = z.object({
  user_id: z.string().uuid().optional(),
  userId: z.string().uuid().optional(),
  device_id: z.string().uuid().optional(),
  deviceId: z.string().uuid().optional(),
  branch_id: z.string().uuid().optional(),
  branchId: z.string().uuid().optional(),
  active_profile_id: z.string().uuid().optional(),
  activeProfileId: z.string().uuid().optional(),
  active_profile_code: z.string().max(10).optional(),
  activeProfileCode: z.string().max(10).optional(),
  app_version: z.string().max(20).optional(),
  appVersion: z.string().max(20).optional(),
  platform: z.enum(['ios', 'android']).optional(),
  os_version: z.string().max(20).optional(),
  osVersion: z.string().max(20).optional(),
  expires_at: z.string().datetime().optional(),
  expiresAt: z.string().datetime().optional(),
});

const sessionQuerySchema = z.object({
  user_id: z.string().uuid().optional(),
  userId: z.string().uuid().optional(),
  device_id: z.string().uuid().optional(),
  deviceId: z.string().uuid().optional(),
  status: z.enum(['active', 'paused', 'expired', 'terminated']).optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(20),
});

const registerPushTokenSchema = z.object({
  user_id: z.string().uuid().optional(),
  userId: z.string().uuid().optional(),
  device_id: z.string().uuid().optional(),
  deviceId: z.string().uuid().optional(),
  token: z.string().min(1, 'El token es requerido'),
  platform: z.enum(['ios', 'android']),
  provider: z.enum(['firebase', 'apns', 'fcm']).default('firebase'),
  subscribed_topics: z.array(z.string()).optional(),
  subscribedTopics: z.array(z.string()).optional(),
});

const addToQueueSchema = z.object({
  user_id: z.string().uuid().optional(),
  userId: z.string().uuid().optional(),
  device_id: z.string().uuid().optional(),
  deviceId: z.string().uuid().optional(),
  session_id: z.string().uuid().optional(),
  sessionId: z.string().uuid().optional(),
  entity_type: z.string().min(1).max(50).optional(),
  entityType: z.string().min(1).max(50).optional(),
  entity_id: z.string().uuid().optional(),
  entityId: z.string().uuid().optional(),
  operation: z.enum(['create', 'update', 'delete']),
  payload: z.record(z.any()),
  metadata: z.record(z.any()).optional(),
  sequence_number: z.coerce.number().int().optional(),
  sequenceNumber: z.coerce.number().int().optional(),
  depends_on: z.string().uuid().optional(),
  dependsOn: z.string().uuid().optional(),
  max_retries: z.coerce.number().int().positive().optional(),
  maxRetries: z.coerce.number().int().positive().optional(),
});

const queueQuerySchema = z.object({
  user_id: z.string().uuid().optional(),
  userId: z.string().uuid().optional(),
  device_id: z.string().uuid().optional(),
  deviceId: z.string().uuid().optional(),
  status: z.enum(['pending', 'processing', 'completed', 'failed', 'conflict']).optional(),
  entity_type: z.string().optional(),
  entityType: z.string().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(20),
});

// ===== MobileController Class =====

class MobileController {

  // ===== Session Endpoints =====

  /**
   * POST /api/mobile/sessions - Create a new mobile session
   */
  async createSession(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createSessionSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de sesion invalidos', parseResult.error.errors);
      }

      const data = parseResult.data;
      const tenantId = req.user!.tenantId;
      const currentUserId = req.user!.userId;

      const dto: CreateSessionDto = {
        userId: data.userId || data.user_id || currentUserId,
        deviceId: (data.deviceId || data.device_id)!,
        branchId: data.branchId || data.branch_id,
        activeProfileId: data.activeProfileId || data.active_profile_id,
        activeProfileCode: data.activeProfileCode || data.active_profile_code,
        appVersion: data.appVersion || data.app_version,
        platform: data.platform,
        osVersion: data.osVersion || data.os_version,
        expiresAt: (data.expiresAt || data.expires_at)
          ? new Date((data.expiresAt || data.expires_at)!)
          : undefined,
      };

      if (!dto.deviceId) {
        throw new ValidationError('El deviceId es requerido');
      }

      const session = await mobileService.createSession(tenantId, dto);

      const response: ApiResponse = {
        success: true,
        data: session,
        message: 'Sesion movil creada exitosamente',
      };

      res.status(201).json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /api/mobile/sessions - List active sessions
   */
  async listSessions(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = sessionQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parametros de consulta invalidos', queryResult.error.errors);
      }

      const data = queryResult.data;
      const tenantId = req.user!.tenantId;

      const filters: SessionFilters = {
        userId: data.userId || data.user_id,
        deviceId: data.deviceId || data.device_id,
        status: data.status,
        page: data.page,
        limit: data.limit,
      };

      const result = await mobileService.getActiveSessions(tenantId, filters);

      const response: ApiResponse = {
        success: true,
        data: result.data,
        meta: {
          total: result.total,
          page: filters.page || 1,
          limit: filters.limit || 20,
          totalPages: Math.ceil(result.total / (filters.limit || 20)),
        },
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * DELETE /api/mobile/sessions/:id - Revoke a session
   */
  async revokeSession(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const tenantId = req.user!.tenantId;

      const session = await mobileService.revokeSession(tenantId, id);

      const response: ApiResponse = {
        success: true,
        data: session,
        message: 'Sesion movil revocada exitosamente',
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  // ===== Push Token Endpoints =====

  /**
   * POST /api/mobile/push-tokens - Register a push token
   */
  async registerPushToken(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = registerPushTokenSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de token invalidos', parseResult.error.errors);
      }

      const data = parseResult.data;
      const tenantId = req.user!.tenantId;
      const currentUserId = req.user!.userId;

      const dto: RegisterPushTokenDto = {
        userId: data.userId || data.user_id || currentUserId,
        deviceId: (data.deviceId || data.device_id)!,
        token: data.token,
        platform: data.platform,
        provider: data.provider,
        subscribedTopics: data.subscribedTopics || data.subscribed_topics,
      };

      if (!dto.deviceId) {
        throw new ValidationError('El deviceId es requerido');
      }

      const pushToken = await mobileService.registerPushToken(tenantId, dto);

      const response: ApiResponse = {
        success: true,
        data: pushToken,
        message: 'Token de push registrado exitosamente',
      };

      res.status(201).json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * DELETE /api/mobile/push-tokens/:id - Remove a push token
   */
  async removePushToken(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const tenantId = req.user!.tenantId;

      await mobileService.removePushToken(tenantId, id);

      const response: ApiResponse = {
        success: true,
        message: 'Token de push removido exitosamente',
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  // ===== Sync Queue Endpoints =====

  /**
   * GET /api/mobile/sync-queue - Get sync queue items
   */
  async getQueueItems(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = queueQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parametros de consulta invalidos', queryResult.error.errors);
      }

      const data = queryResult.data;
      const tenantId = req.user!.tenantId;

      const filters: QueueFilters = {
        userId: data.userId || data.user_id,
        deviceId: data.deviceId || data.device_id,
        status: data.status,
        entityType: data.entityType || data.entity_type,
        page: data.page,
        limit: data.limit,
      };

      const result = await mobileService.getQueueItems(tenantId, filters);

      const response: ApiResponse = {
        success: true,
        data: result.data,
        meta: {
          total: result.total,
          page: filters.page || 1,
          limit: filters.limit || 20,
          totalPages: Math.ceil(result.total / (filters.limit || 20)),
        },
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /api/mobile/sync-queue - Add item to sync queue
   */
  async addToQueue(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = addToQueueSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de cola de sincronizacion invalidos', parseResult.error.errors);
      }

      const data = parseResult.data;
      const tenantId = req.user!.tenantId;
      const currentUserId = req.user!.userId;

      const entityType = data.entityType || data.entity_type;
      const sequenceNumber = data.sequenceNumber ?? data.sequence_number;

      if (!entityType) {
        throw new ValidationError('El entityType es requerido');
      }

      if (sequenceNumber === undefined || sequenceNumber === null) {
        throw new ValidationError('El sequenceNumber es requerido');
      }

      const dto: AddToQueueDto = {
        userId: data.userId || data.user_id || currentUserId,
        deviceId: (data.deviceId || data.device_id)!,
        sessionId: data.sessionId || data.session_id,
        entityType,
        entityId: data.entityId || data.entity_id,
        operation: data.operation,
        payload: data.payload,
        metadata: data.metadata,
        sequenceNumber,
        dependsOn: data.dependsOn || data.depends_on,
        maxRetries: data.maxRetries ?? data.max_retries,
      };

      if (!dto.deviceId) {
        throw new ValidationError('El deviceId es requerido');
      }

      const queueItem = await mobileService.addToQueue(tenantId, dto);

      const response: ApiResponse = {
        success: true,
        data: queueItem,
        message: 'Elemento agregado a la cola de sincronizacion',
      };

      res.status(201).json(response);
    } catch (error) {
      next(error);
    }
  }

  /**
   * PUT /api/mobile/sync-queue/:id/process - Process a sync queue item
   */
  async processQueueItem(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const tenantId = req.user!.tenantId;

      const queueItem = await mobileService.processQueueItem(tenantId, id);

      const response: ApiResponse = {
        success: true,
        data: queueItem,
        message: 'Elemento de sincronizacion procesado exitosamente',
      };

      res.json(response);
    } catch (error) {
      next(error);
    }
  }
}

export const mobileController = new MobileController();
