import { requireSalesOrAdmin } from '@/server/utils/auth';
import { getExpenses, getCurrentMonthExpenses } from '@/server/utils/nocodb';
import { processExpenseWithCurrency } from '@/server/utils/currency';
import type { ExpenseFilters } from '@/utils/types';

// Retry operation wrapper for database calls
async function retryOperation<T>(
  operation: () => Promise<T>, 
  maxRetries: number = 3,
  baseDelay: number = 1000
): Promise<T> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error: any) {
      console.log(`[get-expenses] Attempt ${attempt}/${maxRetries} failed:`, error.message);
      
      // Don't retry on authentication/authorization errors
      if (error.statusCode === 401 || error.statusCode === 403) {
        throw error;
      }
      
      // Don't retry on client errors (4xx except 404)
      if (error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 404) {
        throw error;
      }
      
      // If this is the last attempt, throw the error
      if (attempt === maxRetries) {
        throw error;
      }
      
      // For retryable errors (5xx, network errors, timeouts), wait before retry
      const delay = baseDelay * Math.pow(2, attempt - 1); // Exponential backoff
      console.log(`[get-expenses] Retrying in ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  throw new Error('Retry operation failed unexpectedly');
}

export default defineEventHandler(async (event) => {
  console.log('[get-expenses] API called with query:', getQuery(event));
  
  try {
    // Set proper headers
    setHeader(event, 'Cache-Control', 'no-cache');
    setHeader(event, 'Content-Type', 'application/json');
    
    // Check authentication first
    try {
      await requireSalesOrAdmin(event);
      console.log('[get-expenses] Authentication successful');
    } catch (authError: any) {
      console.error('[get-expenses] Authentication failed:', authError);
      
      // Return proper error status
      if (authError.statusCode === 403) {
        throw createError({
          statusCode: 403,
          statusMessage: 'Access denied. You need sales or admin role to view expenses.'
        });
      }
      
      // Re-throw other auth errors
      throw authError;
    }
    
    const query = getQuery(event);
    
    // If no date filters provided, default to current month
    if (!query.startDate && !query.endDate) {
      console.log('[get-expenses] No date filters provided, defaulting to current month');
      
      try {
        const result = await retryOperation(() => getCurrentMonthExpenses());
        
        // Process expenses with currency conversion
        const processedExpenses = await Promise.all(
          result.list.map((expense: any) => processExpenseWithCurrency(expense))
        );
        
        return {
          ...result,
          list: processedExpenses
        };
      } catch (dbError: any) {
        console.error('[get-expenses] Database error (current month):', dbError);
        
        if (dbError.statusCode === 403) {
          throw createError({
            statusCode: 503,
            statusMessage: 'Expense database is currently unavailable. Please contact your administrator or try again later.'
          });
        }
        
        if (dbError.statusCode === 404) {
          throw createError({
            statusCode: 404,
            statusMessage: 'No expense records found for the current month.'
          });
        }
        
        throw createError({
          statusCode: 500,
          statusMessage: 'Unable to fetch expense data. Please try again later.'
        });
      }
    }
    
    // Build filters from query parameters
    const filters: ExpenseFilters = {};
    
    if (query.startDate && typeof query.startDate === 'string') {
      filters.startDate = query.startDate;
    }
    
    if (query.endDate && typeof query.endDate === 'string') {
      filters.endDate = query.endDate;
    }
    
    if (query.payer && typeof query.payer === 'string') {
      filters.payer = query.payer;
    }
    
    if (query.category && typeof query.category === 'string') {
      filters.category = query.category as any; // Cast to ExpenseCategory
    }
    
    console.log('[get-expenses] Fetching expenses with filters:', filters);
    
    try {
      const result = await retryOperation(() => getExpenses(filters));
      
      // Process expenses with currency conversion
      const processedExpenses = await Promise.all(
        result.list.map((expense: any) => processExpenseWithCurrency(expense))
      );
      
      // Add formatted dates
      const transformedExpenses = processedExpenses.map(expense => ({
        ...expense,
        FormattedDate: new Date(expense.Time).toLocaleDateString(),
        FormattedTime: new Date(expense.Time).toLocaleTimeString()
      }));
      
      // Calculate summary with USD totals
      const usdTotal = transformedExpenses.reduce((sum, e) => sum + (e.PriceUSD || e.PriceNumber || 0), 0);
      const originalTotal = transformedExpenses.reduce((sum, e) => sum + (e.PriceNumber || 0), 0);
      
      return {
        expenses: transformedExpenses,
        PageInfo: result.PageInfo,
        totalCount: result.PageInfo?.totalRows || transformedExpenses.length,
        summary: {
          total: originalTotal, // Original currency total (mixed currencies)
          totalUSD: usdTotal, // USD converted total
          count: transformedExpenses.length,
          uniquePayers: [...new Set(transformedExpenses.map(e => e.Payer))].length,
          currencies: [...new Set(transformedExpenses.map(e => e.currency))].filter(Boolean)
        }
      };
    } catch (dbError: any) {
      console.error('[get-expenses] Database error (filtered):', dbError);
      
      if (dbError.statusCode === 403) {
        throw createError({
          statusCode: 503,
          statusMessage: 'Expense database is currently unavailable. Please contact your administrator or try again later.'
        });
      }
      
      if (dbError.statusCode === 404) {
        throw createError({
          statusCode: 404,
          statusMessage: 'No expense records found matching the specified criteria.'
        });
      }
      
      throw createError({
        statusCode: 500,
        statusMessage: 'Unable to fetch expense data. Please try again later.'
      });
    }
  } catch (error: any) {
    console.error('[get-expenses] Top-level error:', error);
    
    // If it's already a proper H3 error, re-throw it
    if (error.statusCode) {
      throw error;
    }
    
    // Handle authentication errors specifically
    if (error.message?.includes('authentication') || error.message?.includes('auth')) {
      throw createError({
        statusCode: 401,
        statusMessage: 'Authentication required. Please log in again.'
      });
    }
    
    // Handle database connection errors
    if (error.message?.includes('database') || error.message?.includes('connection')) {
      throw createError({
        statusCode: 503,
        statusMessage: 'Database temporarily unavailable. Please try again later.'
      });
    }
    
    // Generic server error for anything else
    throw createError({
      statusCode: 500,
      statusMessage: 'An unexpected error occurred. Please try again later.'
    });
  }
});
