/**
 * @swagger
 * tags:
 *   name: CryptoComOTC
 *   description: Crypto.com OTC 2.0 API Integration for institutional trading
 */

import { Router, Request, Response, NextFunction } from 'express';
import { CryptoComOtcService, createCryptoComOtcService } from './crypto-com-otc.service';
import { RequestQuoteParams, RequestDealParams } from './types';
import { fxService } from '@/core/fx/fx.service';

const router = Router();

// Service instance (initialized on first use)
let otcService: CryptoComOtcService | null = null;

/**
 * Get or create the OTC service instance
 * Returns null if credentials are not configured
 */
async function getOtcService(): Promise<CryptoComOtcService | null> {
  const apiKey = process.env.CRYPTO_COM_API_KEY;
  const apiSecret = process.env.CRYPTO_COM_API_SECRET;

  if (!apiKey || !apiSecret) {
    return null;
  }

  if (!otcService) {
    const environment = (process.env.CRYPTO_COM_ENVIRONMENT || 'production') as 'production' | 'uat';

    otcService = createCryptoComOtcService({
      apiKey,
      apiSecret,
      environment,
    });

    await otcService.initialize();

    // Wire OTC price provider to FX service for market price integration
    const otcPriceProvider = otcService.getPriceProvider();
    fxService.setPriceProvider({
      getPrice: (pair: string, amount?: number) => otcPriceProvider.getPrice(pair, amount),
      isPairSupported: (pair: string) => otcPriceProvider.isPairSupported(pair),
    });
  }

  return otcService;
}

/** Helper: return 503 if OTC not configured */
function requireOtcService(service: CryptoComOtcService | null, res: Response): service is CryptoComOtcService {
  if (!service) {
    res.status(503).json({ success: false, error: 'Crypto.com OTC credentials not configured', timestamp: new Date() });
    return false;
  }
  return true;
}

/**
 * @swagger
 * /api/crypto-com-otc/instruments:
 *   get:
 *     summary: Get available OTC instruments
 *     description: Retrieve list of instruments available for OTC trading
 *     tags: [CryptoComOTC]
 *     responses:
 *       200:
 *         description: List of available instruments
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                 data:
 *                   type: array
 *                   items:
 *                     type: object
 *                     properties:
 *                       instrument_name:
 *                         type: string
 *                       base_currency:
 *                         type: string
 *                       quote_currency:
 *                         type: string
 *       500:
 *         description: Internal server error
 */
router.get('/instruments', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    if (!service) {
      res.status(503).json({
        success: false,
        error: 'Crypto.com OTC API credentials not configured',
        timestamp: new Date(),
      });
      return;
    }
    const instruments = service.getInstruments();

    res.json({
      success: true,
      data: instruments,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/quote-requests:
 *   get:
 *     summary: Get open quote requests
 *     description: Retrieve active quote requests (status = NEW or ACTIVE)
 *     tags: [CryptoComOTC]
 *     parameters:
 *       - in: query
 *         name: cl_quote_req_id
 *         schema:
 *           type: string
 *         description: Client provided ID for quote request
 *       - in: query
 *         name: quote_req_id
 *         schema:
 *           type: string
 *         description: System generated quote request ID
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *           default: 20
 *           maximum: 200
 *         description: Page size
 *     responses:
 *       200:
 *         description: List of open quote requests
 *       500:
 *         description: Internal server error
 */
router.get('/quote-requests', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    if (!requireOtcService(service, res)) return;
    const response = await service.getOpenQuoteRequests({
      cl_quote_req_id: req.query.cl_quote_req_id as string,
      quote_req_id: req.query.quote_req_id as string,
      limit: req.query.limit ? parseInt(req.query.limit as string, 10) : undefined,
    });

    res.json({
      success: response.code === 0,
      data: response.result,
      code: response.code,
      message: response.message,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/quote-requests/history:
 *   get:
 *     summary: Get quote request history
 *     description: Retrieve completed or rejected quote requests
 *     tags: [CryptoComOTC]
 *     parameters:
 *       - in: query
 *         name: cl_quote_req_id
 *         schema:
 *           type: string
 *       - in: query
 *         name: quote_req_id
 *         schema:
 *           type: string
 *       - in: query
 *         name: start_time
 *         schema:
 *           type: integer
 *         description: Start time in Unix epoch (milliseconds)
 *       - in: query
 *         name: end_time
 *         schema:
 *           type: integer
 *         description: End time in Unix epoch (milliseconds)
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *     responses:
 *       200:
 *         description: Quote request history
 */
router.get('/quote-requests/history', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    if (!requireOtcService(service, res)) return;
    const response = await service.getQuoteRequestHistory({
      cl_quote_req_id: req.query.cl_quote_req_id as string,
      quote_req_id: req.query.quote_req_id as string,
      start_time: req.query.start_time ? parseInt(req.query.start_time as string, 10) : undefined,
      end_time: req.query.end_time ? parseInt(req.query.end_time as string, 10) : undefined,
      limit: req.query.limit ? parseInt(req.query.limit as string, 10) : undefined,
    });

    res.json({
      success: response.code === 0,
      data: response.result,
      code: response.code,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/quotes:
 *   get:
 *     summary: Get open quotes
 *     description: Retrieve active quotes (status = ACTIVE)
 *     tags: [CryptoComOTC]
 *     parameters:
 *       - in: query
 *         name: quote_id
 *         schema:
 *           type: string
 *       - in: query
 *         name: lp_quote_id
 *         schema:
 *           type: string
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *     responses:
 *       200:
 *         description: List of open quotes
 */
router.get('/quotes', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    const response = await service.getOpenQuotes({
      quote_id: req.query.quote_id as string,
      lp_quote_id: req.query.lp_quote_id as string,
      limit: req.query.limit ? parseInt(req.query.limit as string, 10) : undefined,
    });

    res.json({
      success: response.code === 0,
      data: response.result,
      code: response.code,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/quotes/history:
 *   get:
 *     summary: Get quote history
 *     description: Retrieve historical quotes
 *     tags: [CryptoComOTC]
 *     responses:
 *       200:
 *         description: Quote history
 */
router.get('/quotes/history', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    if (!requireOtcService(service, res)) return;
    const response = await service.getQuoteHistory({
      quote_id: req.query.quote_id as string,
      lp_quote_id: req.query.lp_quote_id as string,
      start_time: req.query.start_time ? parseInt(req.query.start_time as string, 10) : undefined,
      end_time: req.query.end_time ? parseInt(req.query.end_time as string, 10) : undefined,
      limit: req.query.limit ? parseInt(req.query.limit as string, 10) : undefined,
    });

    res.json({
      success: response.code === 0,
      data: response.result,
      code: response.code,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/deals:
 *   get:
 *     summary: Get open deals
 *     description: Retrieve deals that are not yet settled
 *     tags: [CryptoComOTC]
 *     parameters:
 *       - in: query
 *         name: deal_id
 *         schema:
 *           type: string
 *       - in: query
 *         name: start_time
 *         schema:
 *           type: integer
 *       - in: query
 *         name: end_time
 *         schema:
 *           type: integer
 *     responses:
 *       200:
 *         description: List of open deals
 */
router.get('/deals', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    const response = await service.getOpenDeals({
      deal_id: req.query.deal_id as string,
      start_time: req.query.start_time ? parseInt(req.query.start_time as string, 10) : undefined,
      end_time: req.query.end_time ? parseInt(req.query.end_time as string, 10) : undefined,
    });

    res.json({
      success: response.code === 0,
      data: response.result,
      code: response.code,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/deals/history:
 *   get:
 *     summary: Get deal history
 *     description: Retrieve closed deals
 *     tags: [CryptoComOTC]
 *     responses:
 *       200:
 *         description: Deal history
 */
router.get('/deals/history', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    if (!requireOtcService(service, res)) return;
    const response = await service.getDealHistory({
      deal_id: req.query.deal_id as string,
      start_time: req.query.start_time ? parseInt(req.query.start_time as string, 10) : undefined,
      end_time: req.query.end_time ? parseInt(req.query.end_time as string, 10) : undefined,
    });

    res.json({
      success: response.code === 0,
      data: response.result,
      code: response.code,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/settle-later/limit:
 *   get:
 *     summary: Get settle later limit
 *     description: Retrieve configured and used settle later limit for account
 *     tags: [CryptoComOTC]
 *     responses:
 *       200:
 *         description: Settle later limit information
 */
router.get('/settle-later/limit', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    const limit = await service.getSettleLaterLimit();

    res.json({
      success: !!limit,
      data: limit,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/settle-later/unsettled:
 *   get:
 *     summary: Get unsettled amounts
 *     description: Retrieve pending settlement amounts by date and instrument
 *     tags: [CryptoComOTC]
 *     responses:
 *       200:
 *         description: Unsettled amounts
 */
router.get('/settle-later/unsettled', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    if (!requireOtcService(service, res)) return;
    const amounts = await service.getUnsettledAmounts();

    res.json({
      success: !!amounts,
      data: amounts,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/settle-later/status:
 *   get:
 *     summary: Get settle later monitoring status
 *     description: Full status with limits, unsettled amounts, and alerts
 *     tags: [CryptoComOTC]
 */
router.get('/settle-later/status', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    if (!requireOtcService(service, res)) return;

    const { SettleLaterTrackingService } = await import('./services/settle-later-tracking.service');
    const tracking = new SettleLaterTrackingService(service.getRestClient());
    const status = await tracking.getStatus();

    res.json({
      success: true,
      data: status,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/rfq/request-quote:
 *   post:
 *     summary: Request a quote (RFQ)
 *     description: Submit a Request for Quote to get pricing for OTC trade
 *     tags: [CryptoComOTC]
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - cl_quote_req_id
 *               - leg_list
 *             properties:
 *               cl_quote_req_id:
 *                 type: string
 *                 description: Unique client-provided ID (a-z, A-Z, 0-9, _, -)
 *               firm_quote:
 *                 type: boolean
 *                 default: false
 *                 description: Whether quoted price is guaranteed
 *               settlement_arrangement:
 *                 type: string
 *                 enum: [IMMEDIATE, T1]
 *                 default: IMMEDIATE
 *               duration:
 *                 type: string
 *                 enum: ['5000', '10000', '30000', '60000', '300000', '600000']
 *                 default: '10000'
 *                 description: Quote request duration in milliseconds
 *               quote_ttl:
 *                 type: string
 *                 enum: ['4000', '5000']
 *                 default: '4000'
 *                 description: Minimum quote validity in milliseconds
 *               leg_list:
 *                 type: array
 *                 items:
 *                   type: object
 *                   required:
 *                     - instrument_name
 *                     - side
 *                   properties:
 *                     instrument_name:
 *                       type: string
 *                       example: BTC_USD
 *                     side:
 *                       type: string
 *                       enum: [BUY, SELL]
 *                     quantity:
 *                       type: string
 *                       description: Either quantity or notional (not both)
 *                     notional:
 *                       type: string
 *                       description: Either quantity or notional (not both)
 *     responses:
 *       200:
 *         description: Quote request submitted
 *       400:
 *         description: Validation error
 */
router.post('/rfq/request-quote', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    const params: RequestQuoteParams = req.body;
    const response = await service.requestQuote(params);

    res.json({
      success: response.code === 0,
      data: response.result,
      code: response.code,
      message: response.message,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/rfq/request-deal:
 *   post:
 *     summary: Execute a deal
 *     description: Execute a deal based on a received quote
 *     tags: [CryptoComOTC]
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - deal_type
 *               - cl_deal_id
 *               - quote_id
 *               - quote_req_id
 *               - leg_list
 *             properties:
 *               deal_type:
 *                 type: string
 *                 enum: [QUOTE_REQUEST]
 *               cl_deal_id:
 *                 type: string
 *                 description: Unique client-provided deal ID
 *               quote_id:
 *                 type: string
 *                 description: Quote ID from user.otc_qr.quotes channel
 *               quote_req_id:
 *                 type: string
 *                 description: Quote request ID from user.otc_qr.requests channel
 *               leg_list:
 *                 type: array
 *                 items:
 *                   type: object
 *                   required:
 *                     - instrument_name
 *                     - price
 *                     - side
 *                   properties:
 *                     instrument_name:
 *                       type: string
 *                     price:
 *                       type: string
 *                       description: bid or ask from quote
 *                     quantity:
 *                       type: string
 *                     notional:
 *                       type: string
 *                     side:
 *                       type: string
 *                       enum: [BUY, SELL]
 *     responses:
 *       200:
 *         description: Deal submitted
 *       400:
 *         description: Validation error
 */
router.post('/rfq/request-deal', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();
    if (!requireOtcService(service, res)) return;
    const params: RequestDealParams = req.body;
    const response = await service.requestDeal(params);

    res.json({
      success: response.code === 0,
      data: response.result,
      code: response.code,
      message: response.message,
      timestamp: new Date(),
    });
  } catch (error) {
    next(error);
  }
});

/**
 * @swagger
 * /api/crypto-com-otc/status:
 *   get:
 *     summary: Get service status
 *     description: Check if the OTC service is connected and operational
 *     tags: [CryptoComOTC]
 *     responses:
 *       200:
 *         description: Service status
 */
router.get('/status', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const service = await getOtcService();

    res.json({
      success: true,
      data: {
        connected: service.isConnected(),
        configured: true,
        instrumentCount: service.getInstruments().length,
      },
      timestamp: new Date(),
    });
  } catch (error) {
    res.json({
      success: false,
      data: {
        connected: false,
        error: (error as Error).message,
      },
      timestamp: new Date(),
    });
  }
});

export default router;
