import { query, queryOne } from '../../config/database.js';
import { logger } from '../../shared/utils/logger.js';

// ============================================================================
// TYPES
// ============================================================================

export interface ReorderAlert {
  product_id: string;
  product_code: string;
  product_name: string;
  warehouse_id?: string;
  warehouse_name?: string;
  current_quantity: number;
  reserved_quantity: number;
  available_quantity: number;
  reorder_point: number;
  reorder_quantity: number;
  min_stock: number;
  max_stock?: number;
  shortage: number;
  suggested_order_qty: number;
  alert_level: 'critical' | 'warning' | 'info';
}

export interface StockLevelReport {
  product_id: string;
  product_code: string;
  product_name: string;
  warehouse_id: string;
  warehouse_name: string;
  location_id: string;
  location_name: string;
  quantity: number;
  reserved_quantity: number;
  available_quantity: number;
  lot_id?: string;
  lot_number?: string;
  uom_name: string;
  valuation: number;
}

export interface StockSummary {
  product_id: string;
  product_code: string;
  product_name: string;
  total_quantity: number;
  total_reserved: number;
  total_available: number;
  warehouse_count: number;
  location_count: number;
  total_valuation: number;
}

export interface ReorderAlertFilters {
  warehouse_id?: string;
  category_id?: string;
  alert_level?: 'critical' | 'warning' | 'info' | 'all';
  page?: number;
  limit?: number;
}

export interface StockLevelFilters {
  product_id?: string;
  warehouse_id?: string;
  location_id?: string;
  include_zero?: boolean;
  page?: number;
  limit?: number;
}

// ============================================================================
// SERVICE
// ============================================================================

class ReorderAlertsService {
  /**
   * Get all products below their reorder point
   * Checks inventory.stock_quants against products.products reorder settings
   */
  async getReorderAlerts(
    tenantId: string,
    companyId: string,
    filters: ReorderAlertFilters = {}
  ): Promise<{ data: ReorderAlert[]; total: number }> {
    const { warehouse_id, category_id, alert_level = 'all', page = 1, limit = 50 } = filters;
    const offset = (page - 1) * limit;

    let whereClause = `WHERE p.tenant_id = $1 AND p.active = true`;
    const params: any[] = [tenantId];
    let paramIndex = 2;

    if (warehouse_id) {
      whereClause += ` AND l.warehouse_id = $${paramIndex++}`;
      params.push(warehouse_id);
    }

    if (category_id) {
      whereClause += ` AND p.category_id = $${paramIndex++}`;
      params.push(category_id);
    }

    // Count total alerts
    const countResult = await queryOne<{ count: string }>(
      `SELECT COUNT(DISTINCT p.id) as count
       FROM products.products p
       LEFT JOIN inventory.stock_quants sq ON sq.product_id = p.inventory_product_id AND sq.tenant_id = p.tenant_id
       LEFT JOIN inventory.locations l ON sq.location_id = l.id
       ${whereClause}
       AND p.reorder_point IS NOT NULL
       AND COALESCE(sq.quantity, 0) - COALESCE(sq.reserved_quantity, 0) < p.reorder_point`,
      params
    );

    // Get alerts with stock details
    params.push(limit, offset);
    const alerts = await query<ReorderAlert>(
      `SELECT
         p.id as product_id,
         p.code as product_code,
         p.name as product_name,
         w.id as warehouse_id,
         w.name as warehouse_name,
         COALESCE(SUM(sq.quantity), 0)::numeric as current_quantity,
         COALESCE(SUM(sq.reserved_quantity), 0)::numeric as reserved_quantity,
         COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0) as available_quantity,
         p.reorder_point,
         p.reorder_quantity,
         p.min_stock,
         p.max_stock,
         p.reorder_point - (COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0)) as shortage,
         COALESCE(p.reorder_quantity, p.reorder_point * 2) as suggested_order_qty,
         CASE
           WHEN COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0) <= p.min_stock THEN 'critical'
           WHEN COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0) < p.reorder_point THEN 'warning'
           ELSE 'info'
         END as alert_level
       FROM products.products p
       LEFT JOIN inventory.stock_quants sq ON sq.product_id = p.inventory_product_id AND sq.tenant_id = p.tenant_id
       LEFT JOIN inventory.locations l ON sq.location_id = l.id AND l.location_type = 'internal'
       LEFT JOIN inventory.warehouses w ON l.warehouse_id = w.id
       ${whereClause}
       AND p.reorder_point IS NOT NULL
       GROUP BY p.id, p.code, p.name, w.id, w.name, p.reorder_point, p.reorder_quantity, p.min_stock, p.max_stock
       HAVING COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0) < p.reorder_point
       ORDER BY
         CASE
           WHEN COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0) <= p.min_stock THEN 1
           WHEN COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0) < p.reorder_point THEN 2
           ELSE 3
         END,
         (p.reorder_point - (COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0))) DESC
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

    // Filter by alert level if specified
    const filteredAlerts = alert_level === 'all'
      ? alerts
      : alerts.filter(a => a.alert_level === alert_level);

    logger.info('Reorder alerts retrieved', {
      tenantId,
      companyId,
      totalAlerts: parseInt(countResult?.count || '0', 10),
      returnedAlerts: filteredAlerts.length,
    });

    return {
      data: filteredAlerts,
      total: parseInt(countResult?.count || '0', 10),
    };
  }

  /**
   * Get stock levels by product, warehouse, and location
   * TASK-006-03: Vista niveles de stock
   */
  async getStockLevels(
    tenantId: string,
    filters: StockLevelFilters = {}
  ): Promise<{ data: StockLevelReport[]; total: number }> {
    const { product_id, warehouse_id, location_id, include_zero = false, page = 1, limit = 100 } = filters;
    const offset = (page - 1) * limit;

    let whereClause = `WHERE sq.tenant_id = $1`;
    const params: any[] = [tenantId];
    let paramIndex = 2;

    if (product_id) {
      whereClause += ` AND sq.product_id = $${paramIndex++}`;
      params.push(product_id);
    }

    if (warehouse_id) {
      whereClause += ` AND l.warehouse_id = $${paramIndex++}`;
      params.push(warehouse_id);
    }

    if (location_id) {
      whereClause += ` AND sq.location_id = $${paramIndex++}`;
      params.push(location_id);
    }

    if (!include_zero) {
      whereClause += ` AND (sq.quantity != 0 OR sq.reserved_quantity != 0)`;
    }

    const countResult = await queryOne<{ count: string }>(
      `SELECT COUNT(*) as count
       FROM inventory.stock_quants sq
       JOIN inventory.locations l ON sq.location_id = l.id
       ${whereClause}`,
      params
    );

    params.push(limit, offset);
    const data = await query<StockLevelReport>(
      `SELECT
         sq.product_id,
         p.code as product_code,
         p.name as product_name,
         w.id as warehouse_id,
         w.name as warehouse_name,
         l.id as location_id,
         l.name as location_name,
         sq.quantity,
         sq.reserved_quantity,
         sq.quantity - sq.reserved_quantity as available_quantity,
         sq.lot_id,
         lot.name as lot_number,
         uom.name as uom_name,
         COALESCE(sq.quantity * p.cost_price, 0) as valuation
       FROM inventory.stock_quants sq
       JOIN inventory.products p ON sq.product_id = p.id
       JOIN inventory.locations l ON sq.location_id = l.id
       LEFT JOIN inventory.warehouses w ON l.warehouse_id = w.id
       LEFT JOIN inventory.lots lot ON sq.lot_id = lot.id
       LEFT JOIN core.uom uom ON p.uom_id = uom.id
       ${whereClause}
       ORDER BY p.name, w.name, l.name
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

    return {
      data,
      total: parseInt(countResult?.count || '0', 10),
    };
  }

  /**
   * Get stock summary grouped by product
   */
  async getStockSummary(
    tenantId: string,
    productId?: string
  ): Promise<StockSummary[]> {
    let whereClause = `WHERE sq.tenant_id = $1`;
    const params: any[] = [tenantId];

    if (productId) {
      whereClause += ` AND sq.product_id = $2`;
      params.push(productId);
    }

    return query<StockSummary>(
      `SELECT
         p.id as product_id,
         p.code as product_code,
         p.name as product_name,
         SUM(sq.quantity) as total_quantity,
         SUM(sq.reserved_quantity) as total_reserved,
         SUM(sq.quantity - sq.reserved_quantity) as total_available,
         COUNT(DISTINCT l.warehouse_id) as warehouse_count,
         COUNT(DISTINCT sq.location_id) as location_count,
         COALESCE(SUM(sq.quantity * p.cost_price), 0) as total_valuation
       FROM inventory.stock_quants sq
       JOIN inventory.products p ON sq.product_id = p.id
       JOIN inventory.locations l ON sq.location_id = l.id
       ${whereClause}
       GROUP BY p.id, p.code, p.name
       ORDER BY p.name`,
      params
    );
  }

  /**
   * Check if a specific product needs reorder
   */
  async checkProductReorder(
    productId: string,
    tenantId: string,
    warehouseId?: string
  ): Promise<ReorderAlert | null> {
    let whereClause = `WHERE p.id = $1 AND p.tenant_id = $2`;
    const params: any[] = [productId, tenantId];

    if (warehouseId) {
      whereClause += ` AND l.warehouse_id = $3`;
      params.push(warehouseId);
    }

    const result = await queryOne<ReorderAlert>(
      `SELECT
         p.id as product_id,
         p.code as product_code,
         p.name as product_name,
         w.id as warehouse_id,
         w.name as warehouse_name,
         COALESCE(SUM(sq.quantity), 0)::numeric as current_quantity,
         COALESCE(SUM(sq.reserved_quantity), 0)::numeric as reserved_quantity,
         COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0) as available_quantity,
         p.reorder_point,
         p.reorder_quantity,
         p.min_stock,
         p.max_stock,
         GREATEST(0, p.reorder_point - (COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0))) as shortage,
         COALESCE(p.reorder_quantity, p.reorder_point * 2) as suggested_order_qty,
         CASE
           WHEN COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0) <= p.min_stock THEN 'critical'
           WHEN COALESCE(SUM(sq.quantity), 0) - COALESCE(SUM(sq.reserved_quantity), 0) < p.reorder_point THEN 'warning'
           ELSE 'info'
         END as alert_level
       FROM products.products p
       LEFT JOIN inventory.stock_quants sq ON sq.product_id = p.inventory_product_id AND sq.tenant_id = p.tenant_id
       LEFT JOIN inventory.locations l ON sq.location_id = l.id AND l.location_type = 'internal'
       LEFT JOIN inventory.warehouses w ON l.warehouse_id = w.id
       ${whereClause}
       GROUP BY p.id, p.code, p.name, w.id, w.name, p.reorder_point, p.reorder_quantity, p.min_stock, p.max_stock`,
      params
    );

    // Only return if below reorder point
    if (result && Number(result.available_quantity) < Number(result.reorder_point)) {
      return result;
    }

    return null;
  }

  /**
   * Get products with low stock for dashboard/notifications
   */
  async getLowStockProductsCount(
    tenantId: string,
    companyId: string
  ): Promise<{ critical: number; warning: number; total: number }> {
    const result = await queryOne<{ critical: string; warning: string }>(
      `SELECT
         COUNT(DISTINCT CASE WHEN available <= p.min_stock THEN p.id END) as critical,
         COUNT(DISTINCT CASE WHEN available > p.min_stock AND available < p.reorder_point THEN p.id END) as warning
       FROM products.products p
       LEFT JOIN (
         SELECT product_id, SUM(quantity) - SUM(reserved_quantity) as available
         FROM inventory.stock_quants sq
         JOIN inventory.locations l ON sq.location_id = l.id AND l.location_type = 'internal'
         WHERE sq.tenant_id = $1
         GROUP BY product_id
       ) stock ON stock.product_id = p.inventory_product_id
       WHERE p.tenant_id = $1 AND p.active = true AND p.reorder_point IS NOT NULL`,
      [tenantId]
    );

    const critical = parseInt(result?.critical || '0', 10);
    const warning = parseInt(result?.warning || '0', 10);

    return {
      critical,
      warning,
      total: critical + warning,
    };
  }
}

export const reorderAlertsService = new ReorderAlertsService();
