/**
 * BlackBox Stocks API Service
 * Fetches options flow data from BlackBox Stocks API
 */

import dotenv from 'dotenv';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { parse } from 'csv-parse/sync';

// Get the directory of the current module
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Load .env from backend root (parent directory of src)
dotenv.config({ path: join(__dirname, '..', '.env') });

// Use native fetch (Node.js 18+) or node-fetch for older versions
let fetchModule = null;

async function getFetch() {
  if (typeof globalThis.fetch !== 'undefined') {
    // Node.js 18+ has native fetch
    return globalThis.fetch;
  }
  
  // Lazy load node-fetch if needed
  if (!fetchModule) {
    try {
      fetchModule = await import('node-fetch');
    } catch (e) {
      throw new Error('Fetch is not available. Please install node-fetch or use Node.js 18+');
    }
  }
  
  return fetchModule.default || fetchModule;
}

const BLACKBOX_API_URL = 'https://api.blackboxstocks.com/api/v2/download/downloadOptionsFlow';
const BLACKBOX_API_TOKEN = process.env.BLACKBOX_API_TOKEN || 'eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoic3JlZGR5MDgxOUBnbWFpbC5jb20iLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6Ijk3ZDA5NThjLTBhOTktNDA5OC05OWQwLWNkYTg3Njg5N2Q3ZiIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3NpZCI6ImViRTByZWZCUm10YTFqMmx1YzZwNHZKZGJqTEJKT1BrMVVJNnNXcmJFTWM9IiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjpbIlN1YnNjcmliZXIiLCJPcHRpb25zIiwiRXF1aXRpZXMiXSwiZXhwIjoxNzY3NDUyMzgwfQ.48oftWtLRXsteaErfIwUhygYoeAmdl_hbPWZ7590wqQ';

/**
 * Build default filters object for the downloadOptionsFlow endpoint
 * @param {Object} customFilters - Custom filter overrides
 * @returns {Object} Filters object
 */
function buildFilters(customFilters = {}) {
  return {
    optionsFlowPuts: true,
    optionsFlowCalls: true,
    optionsFlowYellow: true,
    optionsFlowWhite: true,
    optionsFlowMagenta: true,
    optionsFlowMultileg: false,
    optionsFlowAboveAskOnly: true,
    optionsFlowBelowBidOnly: true,
    optionsFlowAtOrAboveAsk: true,
    optionsFlowAtOrBelowBid: true,
    optionsFlowBelowPoint5: false,
    optionsFlowBelow5: false,
    optionsFlowAbove50k: false,
    optionsFlowAbove100k: false,
    optionsFlowAbove200k: false,
    optionsFlowAbove500k: false,
    optionsFlowAbove1m: false,
    optionsFlowInTheMoney: false,
    optionsFlowOutOfTheMoney: false,
    optionsFlow5000Contracts: false,
    marketCapAbove750B: false,
    optionsFlowSweepOnly: false,
    optionsFlow100Contracts: false,
    optionsFlow500Contracts: false,
    optionsFlowWeeklyOnly: false,
    optionsFlowEarningsReportOnly: false,
    optionsFlowUnusualOnly: false,
    optionsFlowExDiv: false,
    optionsFlowConsumerDiscretionary: true,
    optionsFlowIndustrials: true,
    optionsFlowInformationTechnology: true,
    optionsFlowRealEstate: true,
    optionsFlowHealthCare: true,
    optionsFlowEnergy: true,
    optionsFlowFinancials: true,
    optionsFlowMaterials: true,
    optionsFlowConsumerStaples: true,
    optionsFlowCommunicationServices: true,
    optionsFlowUtilities: true,
    optionsFlowStock: true,
    optionsFlowEtf: true,
    optionsExpirationRange: false,
    optionsFlowOnlyMultiLeg: false,
    optionsFlowSectorNone: true,
    ...customFilters // Allow custom filter overrides
  };
}

/**
 * Fetch options flow data from BlackBox Stocks API
 * @param {Object} options - Request options
 * @param {string|Date} options.startDate - Start date (optional, format: YYYY-MM-DD or Date object)
 * @param {string|Date} options.endDate - End date (optional, format: YYYY-MM-DD or Date object)
 * @param {number} options.count - Maximum number of records to fetch (default: 300)
 * @param {string} options.symbol - Filter by specific symbol (optional)
 * @param {number} options.strike - Filter by strike price (optional)
 * @param {boolean} options.historical - Whether to fetch historical data (default: false)
 * @param {Object} options.filters - Custom filter overrides
 * @returns {Promise<Array>} Array of flow records
 */
export async function fetchBlackBoxFlow(options = {}) {
  if (!BLACKBOX_API_TOKEN) {
    throw new Error(
      'BLACKBOX_API_TOKEN not found in environment variables.\n' +
      'Please add BLACKBOX_API_TOKEN to your .env file.'
    );
  }

  try {
    const fetch = await getFetch();
    
    // Parse dates - default to today if not provided
    let startDate, endDate;
    if (options.startDate) {
      startDate = options.startDate instanceof Date ? options.startDate : new Date(options.startDate);
    } else {
      startDate = new Date();
    }
    
    if (options.endDate) {
      endDate = options.endDate instanceof Date ? options.endDate : new Date(options.endDate);
    } else {
      endDate = new Date(startDate);
    }
    
    // Ensure dates are valid
    if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
      throw new Error('Invalid date format. Use YYYY-MM-DD or Date object.');
    }
    
    // Build request body in the correct format for downloadOptionsFlow endpoint
    const body = {
      fromDate: startDate.toISOString(),
      toDate: endDate.toISOString(),
      symbol: options.symbol || "",
      filter: buildFilters(options.filters),
      strike: options.strike || 0,
      watchListOmit: options.watchListOmit || false,
      watchListOnly: options.watchListOnly || false
    };

    const response = await fetch(BLACKBOX_API_URL, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': '*/*', // Accept any content type (CSV or JSON)
        'Authorization': `Bearer ${BLACKBOX_API_TOKEN}`
      },
      body: JSON.stringify(body)
    });

    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(
        `BlackBox API error: ${response.status} ${response.statusText}\n` +
        `Response: ${errorText}`
      );
    }

    // Get content type to determine if response is CSV or JSON
    const contentType = response.headers.get('content-type') || '';
    const responseText = await response.text();
    
    // Check if response is CSV
    if (contentType.includes('text/csv') || contentType.includes('application/csv') || 
        responseText.trim().startsWith('CreatedDate') || responseText.trim().startsWith('CreatedDat')) {
      // Parse CSV response
      try {
        const records = parse(responseText, {
          columns: true,
          skip_empty_lines: true,
          trim: true,
          cast: false // Keep as strings to match database schema (TEXT columns)
        });
        console.log(`   📊 Parsed ${records.length} records from CSV response`);
        return records;
      } catch (csvError) {
        console.error('Error parsing CSV response:', csvError);
        // Log first 500 chars of response for debugging
        console.error('Response preview:', responseText.substring(0, 500));
        throw new Error(`Failed to parse CSV response: ${csvError.message}`);
      }
    }
    
    // Try to parse as JSON
    try {
      const data = JSON.parse(responseText);
      
      // Handle different JSON response structures
      if (Array.isArray(data)) {
        return data;
      } else if (data.data && Array.isArray(data.data)) {
        return data.data;
      } else if (data.flows && Array.isArray(data.flows)) {
        return data.flows;
      } else if (data.results && Array.isArray(data.results)) {
        return data.results;
      } else {
        console.warn('Unexpected API response structure:', Object.keys(data));
        return [];
      }
    } catch (jsonError) {
      // If JSON parsing fails, check if it might be CSV
      if (responseText.trim().startsWith('CreatedDate') || responseText.includes(',')) {
        console.log('   📊 Response appears to be CSV, attempting to parse...');
        try {
          const records = parse(responseText, {
            columns: true,
            skip_empty_lines: true,
            trim: true,
            cast: false
          });
          console.log(`   📊 Parsed ${records.length} records from CSV response`);
          return records;
        } catch (csvError) {
          throw new Error(`Failed to parse response as CSV or JSON: ${csvError.message}`);
        }
      }
      throw new Error(`Failed to parse response: ${jsonError.message}`);
    }
  } catch (error) {
    console.error('Error fetching BlackBox flow data:', error);
    throw error;
  }
}

/**
 * Map BlackBox API response to database schema
 * @param {Object} apiRecord - Record from BlackBox API
 * @returns {Object} Mapped record for OptionsFlow_monthly table
 */
export function mapBlackBoxToDatabase(apiRecord) {
  // Helper function to safely extract values
  const get = (obj, ...keys) => {
    for (const key of keys) {
      if (obj[key] !== undefined && obj[key] !== null) {
        return String(obj[key]);
      }
    }
    return null;
  };

  // Helper to format date
  const formatDate = (dateStr) => {
    if (!dateStr) return null;
    // Try to parse and format as YYYY-MM-DD
    try {
      const date = new Date(dateStr);
      if (isNaN(date.getTime())) return String(dateStr);
      return date.toISOString().split('T')[0];
    } catch {
      return String(dateStr);
    }
  };

  // Helper to format time
  const formatTime = (timeStr) => {
    if (!timeStr) return null;
    return String(timeStr);
  };

  // Map fields - try multiple possible field names from API
  const mapped = {
    CreatedDate: formatDate(
      get(apiRecord, 'createdDate', 'CreatedDate', 'date', 'Date', 'timestamp', 'Timestamp')
    ),
    CreatedTime: formatTime(
      get(apiRecord, 'createdTime', 'CreatedTime', 'time', 'Time', 'timestamp', 'Timestamp')
    ),
    Symbol: get(apiRecord, 'symbol', 'Symbol', 'ticker', 'Ticker', 'underlying', 'Underlying'),
    Type: get(apiRecord, 'type', 'Type', 'tradeType', 'TradeType'),
    Volume: get(apiRecord, 'volume', 'Volume', 'vol', 'Vol', 'contracts', 'Contracts'),
    Price: get(apiRecord, 'price', 'Price', 'lastPrice', 'LastPrice', 'tradePrice', 'TradePrice'),
    Side: get(apiRecord, 'side', 'Side', 'tradeSide', 'TradeSide', 'direction', 'Direction'),
    CallPut: get(apiRecord, 'callPut', 'CallPut', 'optionType', 'OptionType', 'putCall', 'PutCall', 'type', 'Type'),
    Strike: get(apiRecord, 'strike', 'Strike', 'strikePrice', 'StrikePrice'),
    Spot: get(apiRecord, 'spot', 'Spot', 'underlyingPrice', 'UnderlyingPrice', 'stockPrice', 'StockPrice'),
    Premium: get(apiRecord, 'premium', 'Premium', 'totalPremium', 'TotalPremium', 'notional', 'Notional'),
    ExpirationDate: formatDate(
      get(apiRecord, 'expirationDate', 'ExpirationDate', 'expiry', 'Expiry', 'expiration', 'Expiration')
    ),
    Color: get(apiRecord, 'color', 'Color', 'tradeColor', 'TradeColor'),
    ImpliedVolatility: get(apiRecord, 'impliedVolatility', 'ImpliedVolatility', 'iv', 'IV', 'volatility', 'Volatility'),
    Dte: get(apiRecord, 'dte', 'Dte', 'DTE', 'daysToExpiration', 'DaysToExpiration', 'daysToExpiry', 'DaysToExpiry'),
    ER: get(apiRecord, 'er', 'ER', 'earnings', 'Earnings', 'earningsReport', 'EarningsReport'),
    StockEtf: get(apiRecord, 'stockEtf', 'StockEtf', 'assetType', 'AssetType', 'securityType', 'SecurityType'),
    Sector: get(apiRecord, 'sector', 'Sector', 'industry', 'Industry'),
    Uoa: get(apiRecord, 'uoa', 'Uoa', 'UOA', 'underlyingOfAsset', 'UnderlyingOfAsset'),
    Weekly: get(apiRecord, 'weekly', 'Weekly', 'isWeekly', 'IsWeekly', 'weeklies', 'Weeklies'),
    MktCap: get(apiRecord, 'mktCap', 'MktCap', 'marketCap', 'MarketCap', 'marketCapitalization', 'MarketCapitalization'),
    OI: get(apiRecord, 'oi', 'OI', 'openInterest', 'OpenInterest', 'openInt', 'OpenInt')
  };

  return mapped;
}

/**
 * Test the BlackBox API connection
 * @returns {Promise<Object>} Test result
 */
export async function testBlackBoxConnection() {
  try {
    const data = await fetchBlackBoxFlow({ count: 1 });
    return {
      success: true,
      message: 'BlackBox API connection successful',
      sampleRecord: data[0] || null,
      recordCount: data.length
    };
  } catch (error) {
    return {
      success: false,
      message: 'BlackBox API connection failed',
      error: error.message
    };
  }
}

