/**
 * Server Provisioning Handler
 * POST /api/provision - Create a new server
 * GET /api/provision/orders - Get user's orders
 * GET /api/provision/orders/:id - Get specific order
 * DELETE /api/provision/orders/:id - Delete/terminate server
 * GET /api/provision/balance - Get user's balance
 */

import type { Env, ProvisionRequest } from '../types';
import { ProvisioningService } from '../services/provisioning-service';
import { createErrorResponse, createSuccessResponse } from '../utils/http';
import { LIMITS } from '../config';

/**
 * Validate provision request body
 * Note: user_id in API is the Telegram ID
 */
function validateProvisionRequest(body: unknown): {
  valid: true;
  data: ProvisionRequest;
} | {
  valid: false;
  error: string;
} {
  if (!body || typeof body !== 'object') {
    return { valid: false, error: 'Request body must be a JSON object' };
  }

  const data = body as Record<string, unknown>;

  // Required fields - user_id is the Telegram ID
  if (!data.user_id || typeof data.user_id !== 'string') {
    return { valid: false, error: 'user_id is required and must be a string (Telegram ID)' };
  }

  if (!data.pricing_id || typeof data.pricing_id !== 'number' || data.pricing_id <= 0) {
    return { valid: false, error: 'pricing_id is required and must be a positive number' };
  }

  // Optional fields validation
  if (data.label !== undefined && typeof data.label !== 'string') {
    return { valid: false, error: 'label must be a string' };
  }

  if (data.label && (data.label as string).length > 64) {
    return { valid: false, error: 'label must be 64 characters or less' };
  }

  const validOsImages = ['ubuntu_22_04', 'ubuntu_20_04', 'debian_11', 'debian_12'];
  if (data.image !== undefined && !validOsImages.includes(data.image as string)) {
    return { valid: false, error: `image must be one of: ${validOsImages.join(', ')}` };
  }

  if (data.dry_run !== undefined && typeof data.dry_run !== 'boolean') {
    return { valid: false, error: 'dry_run must be a boolean' };
  }

  return {
    valid: true,
    data: {
      telegram_id: data.user_id as string, // API uses user_id, internally it's telegram_id
      pricing_id: data.pricing_id as number,
      label: data.label as string | undefined,
      image: data.image as string | undefined,
      dry_run: data.dry_run as boolean | undefined,
    },
  };
}

/**
 * POST /api/provision
 * Create a new server
 */
export async function handleProvision(
  request: Request,
  env: Env,
  corsHeaders: Record<string, string>
): Promise<Response> {
  try {
    // Check content length
    const contentLength = request.headers.get('content-length');
    if (contentLength && parseInt(contentLength, 10) > LIMITS.MAX_REQUEST_BODY_BYTES) {
      return createErrorResponse('Request body too large', 413, undefined, corsHeaders);
    }

    // Parse request body
    let body: unknown;
    try {
      body = await request.json();
    } catch {
      return createErrorResponse('Invalid JSON in request body', 400, undefined, corsHeaders);
    }

    // Validate request
    const validation = validateProvisionRequest(body);
    if (!validation.valid) {
      return createErrorResponse(validation.error, 400, 'VALIDATION_ERROR', corsHeaders);
    }

    // Check API keys (skip for dry_run)
    if (!validation.data.dry_run && !env.LINODE_API_KEY && !env.VULTR_API_KEY) {
      return createErrorResponse(
        'No VPS provider API keys configured',
        503,
        'SERVICE_UNAVAILABLE',
        corsHeaders
      );
    }

    // Create provisioning service with both DBs
    const provisioningService = new ProvisioningService(
      env,          // Full env for exchange rate
      env.DB,       // cloud-instances-db
      env.USER_DB,  // telegram-conversations
      env.LINODE_API_KEY,
      env.VULTR_API_KEY
    );

    // Provision server
    const result = await provisioningService.provisionServer(validation.data);

    if (!result.success) {
      // Map error codes to HTTP status codes
      const statusCode = getStatusCodeForError(result.error!.code);
      return createErrorResponse(
        result.error!.message,
        statusCode,
        result.error!.code,
        corsHeaders
      );
    }

    // Return success response (hide root password in response)
    const sanitizedOrder = {
      ...result.order!,
      root_password: '*** Use GET /api/provision/orders/:id to retrieve once ***',
    };

    // Include dry_run_info if present
    const response: Record<string, unknown> = {
      message: validation.data.dry_run
        ? 'Dry run successful. Validation passed.'
        : 'Server provisioned successfully',
      order: sanitizedOrder,
    };

    // Add dry_run details from service
    if ('dry_run_info' in result) {
      response.dry_run_info = (result as Record<string, unknown>).dry_run_info;
    }

    return createSuccessResponse(response, validation.data.dry_run ? 200 : 201, corsHeaders);
  } catch (error) {
    console.error('[handleProvision] Error:', error);
    return createErrorResponse(
      'Internal server error during provisioning',
      500,
      'INTERNAL_ERROR',
      corsHeaders
    );
  }
}

/**
 * GET /api/provision/orders
 * Get user's orders
 */
export async function handleGetOrders(
  request: Request,
  env: Env,
  corsHeaders: Record<string, string>
): Promise<Response> {
  try {
    const url = new URL(request.url);
    const userId = url.searchParams.get('user_id'); // This is telegram_id

    if (!userId) {
      return createErrorResponse('user_id query parameter is required', 400, undefined, corsHeaders);
    }

    const provisioningService = new ProvisioningService(
      env,
      env.DB,
      env.USER_DB,
      env.LINODE_API_KEY,
      env.VULTR_API_KEY
    );

    const orders = await provisioningService.getUserOrders(userId);

    // Sanitize root passwords
    const sanitizedOrders = orders.map((order) => ({
      ...order,
      root_password: order.root_password ? '***REDACTED***' : null,
    }));

    return createSuccessResponse({ orders: sanitizedOrders }, 200, corsHeaders);
  } catch (error) {
    console.error('[handleGetOrders] Error:', error);
    return createErrorResponse('Internal server error', 500, undefined, corsHeaders);
  }
}

/**
 * GET /api/provision/orders/:id
 * Get specific order with root password (one-time view concept)
 */
export async function handleGetOrder(
  request: Request,
  env: Env,
  orderId: string,
  corsHeaders: Record<string, string>
): Promise<Response> {
  try {
    const url = new URL(request.url);
    const userId = url.searchParams.get('user_id'); // This is telegram_id

    if (!userId) {
      return createErrorResponse('user_id query parameter is required', 400, undefined, corsHeaders);
    }

    const provisioningService = new ProvisioningService(
      env,
      env.DB,
      env.USER_DB,
      env.LINODE_API_KEY,
      env.VULTR_API_KEY
    );

    const order = await provisioningService.getOrder(orderId);

    if (!order) {
      return createErrorResponse('Order not found', 404, 'NOT_FOUND', corsHeaders);
    }

    // Get user to verify ownership
    const balance = await provisioningService.getUserBalance(userId);
    if (!balance || order.user_id !== balance.user_id) {
      return createErrorResponse('Unauthorized', 403, 'UNAUTHORIZED', corsHeaders);
    }

    // Include root password for order owner
    return createSuccessResponse({ order }, 200, corsHeaders);
  } catch (error) {
    console.error('[handleGetOrder] Error:', error);
    return createErrorResponse('Internal server error', 500, undefined, corsHeaders);
  }
}

/**
 * DELETE /api/provision/orders/:id
 * Delete/terminate a server
 */
export async function handleDeleteOrder(
  request: Request,
  env: Env,
  orderId: string,
  corsHeaders: Record<string, string>
): Promise<Response> {
  try {
    const url = new URL(request.url);
    const userId = url.searchParams.get('user_id'); // This is telegram_id

    if (!userId) {
      return createErrorResponse('user_id query parameter is required', 400, undefined, corsHeaders);
    }

    const provisioningService = new ProvisioningService(
      env,
      env.DB,
      env.USER_DB,
      env.LINODE_API_KEY,
      env.VULTR_API_KEY
    );

    const result = await provisioningService.deleteServer(orderId, userId);

    if (!result.success) {
      return createErrorResponse(result.error!, 400, 'DELETE_FAILED', corsHeaders);
    }

    return createSuccessResponse({ message: 'Server terminated successfully' }, 200, corsHeaders);
  } catch (error) {
    console.error('[handleDeleteOrder] Error:', error);
    return createErrorResponse('Internal server error', 500, undefined, corsHeaders);
  }
}

/**
 * GET /api/provision/balance
 * Get user's balance (in KRW)
 */
export async function handleGetBalance(
  request: Request,
  env: Env,
  corsHeaders: Record<string, string>
): Promise<Response> {
  try {
    const url = new URL(request.url);
    const userId = url.searchParams.get('user_id'); // This is telegram_id

    if (!userId) {
      return createErrorResponse('user_id query parameter is required', 400, undefined, corsHeaders);
    }

    const provisioningService = new ProvisioningService(
      env,
      env.DB,
      env.USER_DB,
      env.LINODE_API_KEY,
      env.VULTR_API_KEY
    );

    const balance = await provisioningService.getUserBalance(userId);

    if (!balance) {
      return createErrorResponse('User not found', 404, 'NOT_FOUND', corsHeaders);
    }

    return createSuccessResponse(
      {
        balance_krw: balance.balance_krw,
        currency: 'KRW',
      },
      200,
      corsHeaders
    );
  } catch (error) {
    console.error('[handleGetBalance] Error:', error);
    return createErrorResponse('Internal server error', 500, undefined, corsHeaders);
  }
}

/**
 * Map error codes to HTTP status codes
 */
function getStatusCodeForError(code: string): number {
  switch (code) {
    case 'USER_NOT_FOUND':
    case 'PRICING_NOT_FOUND':
      return 404;
    case 'INSUFFICIENT_BALANCE':
      return 402; // Payment Required
    case 'PROVIDER_NOT_CONFIGURED':
      return 503;
    case 'VALIDATION_ERROR':
      return 400;
    case 'UNAUTHORIZED':
      return 403;
    default:
      return 500;
  }
}
