//+------------------------------------------------------------------+
//|                                                 VaultFX_Sync.mq5 |
//|                                        Copyright 2026, VaultFX   |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "VaultFX"
#property link      "https://vaultfx.app"
#property version   "1.00"

input string SyncKey = "PASTE_KEY_HERE";
input string WebhookURL = "https://vaultfx-fci9.onrender.com/api/webhook/mt5"; // Webhook URL (For Local Testing)

double cached_initial_balance = -1;
int last_history_count = -1;

int OnInit()
  {
   Print("VaultFX Auto-Sync Started. Connected to Key: ", SyncKey);
   
   cached_initial_balance = GetInitialBalance();
   Print("Detected Initial Deposit Balance: ", cached_initial_balance);
   
   EventSetTimer(5);
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   EventKillTimer();
   Print("VaultFX Auto-Sync Stopped.");
  }

double GetInitialBalance()
  {
   if(!HistorySelect(0, TimeCurrent())) return AccountInfoDouble(ACCOUNT_BALANCE);
   
   int total_deals = HistoryDealsTotal();
   for(int i=0; i<total_deals; i++)
     {
      ulong ticket = HistoryDealGetTicket(i);
      long deal_type = HistoryDealGetInteger(ticket, DEAL_TYPE);
      if(deal_type == DEAL_TYPE_BALANCE)
        {
         double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
         if(profit > 0) return profit;
        }
     }
   return AccountInfoDouble(ACCOUNT_BALANCE); // Fallback
  }

void SyncHistoricalTrades()
  {
   if(!HistorySelect(0, TimeCurrent())) return;
   
   int total_deals = HistoryDealsTotal();
   if(total_deals == last_history_count) return; // No new deals
   
   last_history_count = total_deals;
   
   string trades_json = "[";
   bool first_trade = true;
   
   for(int i = 0; i < total_deals; i++)
     {
      ulong ticket = HistoryDealGetTicket(i);
      long deal_entry = HistoryDealGetInteger(ticket, DEAL_ENTRY);
      long deal_type = HistoryDealGetInteger(ticket, DEAL_TYPE);
      
      // Ignore Balance and Credit deposits/withdrawals
      if(deal_type == DEAL_TYPE_BALANCE || deal_type == DEAL_TYPE_CREDIT) continue;
      
      // We only care about OUT deals (when a position is closed)
      if(deal_entry == DEAL_ENTRY_OUT || deal_entry == DEAL_ENTRY_INOUT)
        {
         long position_id = HistoryDealGetInteger(ticket, DEAL_POSITION_ID);
         string symbol = HistoryDealGetString(ticket, DEAL_SYMBOL);
         double out_profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
           double out_commission = HistoryDealGetDouble(ticket, DEAL_COMMISSION);
           double out_swap = HistoryDealGetDouble(ticket, DEAL_SWAP);
         
         // In MT5, DEAL_TYPE of an OUT deal is the opposite of the position. 
         // Example: If position was BUY, the OUT deal is a SELL.
         string side = (deal_type == DEAL_TYPE_SELL) ? "Long" : "Short"; // Reversing the OUT deal type
         double exit_price = HistoryDealGetDouble(ticket, DEAL_PRICE);
         double volume = HistoryDealGetDouble(ticket, DEAL_VOLUME);
         
         long exit_time = HistoryDealGetInteger(ticket, DEAL_TIME);
         long entry_time = exit_time; // Fallback
         double entry_price = 0;
           double in_commission = 0;
           double in_swap = 0;
           
           // Find the IN deal for this position to get entry price & time
         for(int j = 0; j < i; j++)
           {
            ulong in_ticket = HistoryDealGetTicket(j);
            if(HistoryDealGetInteger(in_ticket, DEAL_POSITION_ID) == position_id && 
               HistoryDealGetInteger(in_ticket, DEAL_ENTRY) == DEAL_ENTRY_IN)
              {
               entry_time = HistoryDealGetInteger(in_ticket, DEAL_TIME);
               entry_price = HistoryDealGetDouble(in_ticket, DEAL_PRICE);
                 in_commission = HistoryDealGetDouble(in_ticket, DEAL_COMMISSION);
                 in_swap = HistoryDealGetDouble(in_ticket, DEAL_SWAP);
                 break;
              }
           }
         
         if(!first_trade) trades_json += ",";
         
         // Convert timestamps to milliseconds for JavaScript compatibility
         trades_json += "{\"ticket\":\"" + IntegerToString(position_id) + "\"," +
                        "\"symbol\":\"" + symbol + "\"," +
                        "\"side\":\"" + side + "\"," +
                        "\"profit\":" + DoubleToString(out_profit, 2) + "," +
                          "\"commission\":" + DoubleToString(out_commission + in_commission, 2) + "," +
                          "\"swap\":" + DoubleToString(out_swap + in_swap, 2) + "," +
                        "\"entry_price\":" + DoubleToString(entry_price, 5) + "," +
                        "\"exit_price\":" + DoubleToString(exit_price, 5) + "," +
                        "\"lot_size\":" + DoubleToString(volume, 2) + "," +
                        "\"entry_time\":" + IntegerToString(entry_time * 1000) + "," +
                        "\"exit_time\":" + IntegerToString(exit_time * 1000) + "}";
                        
         first_trade = false;
        }
     }
     
   trades_json += "]";
   
   // If no valid trades found, exit
   // Always send payload even if empty to verify execution
     if(first_trade) { last_history_count = -1; }
   
   string payload = "{\"sync_key\":\"" + SyncKey + "\",\"trades\":" + trades_json + "}";
   
   char post[], result[];
   int str_len = StringLen(payload);
   StringToCharArray(payload, post, 0, str_len); 
   string headers = "Content-Type: application/json\r\n";
   
   string url = WebhookURL + "/trades";
   WebRequest("POST", url, headers, 3000, post, result, headers);
  }

void OnTimer()
  {
   if(SyncKey == "PASTE_KEY_HERE" || SyncKey == "") return;

   // Sync History (Runs if deals changed)
   SyncHistoricalTrades();

   // 1. Fetch live metrics
   double equity = AccountInfoDouble(ACCOUNT_EQUITY);
   double upnl = AccountInfoDouble(ACCOUNT_PROFIT);
   double current_balance = AccountInfoDouble(ACCOUNT_BALANCE);
   
   if(cached_initial_balance <= 0) cached_initial_balance = GetInitialBalance();
   
      // Fetch open positions
   string open_positions = "[";
   int total_positions = PositionsTotal();
   bool first_pos = true;
   for(int i = 0; i < total_positions; i++)
     {
      ulong pos_ticket = PositionGetTicket(i);
      if(pos_ticket > 0)
        {
         if(!first_pos) open_positions += ",";
         first_pos = false;
         open_positions += "{";
         open_positions += "\"ticket\":" + IntegerToString(pos_ticket) + ",";
         open_positions += "\"symbol\":\"" + PositionGetString(POSITION_SYMBOL) + "\",";
         open_positions += "\"type\":" + IntegerToString(PositionGetInteger(POSITION_TYPE)) + ",";
         open_positions += "\"volume\":" + DoubleToString(PositionGetDouble(POSITION_VOLUME), 2) + ",";
         open_positions += "\"open_price\":" + DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN), 5) + ",";
         open_positions += "\"current_price\":" + DoubleToString(PositionGetDouble(POSITION_PRICE_CURRENT), 5) + ",";
         open_positions += "\"sl\":" + DoubleToString(PositionGetDouble(POSITION_SL), 5) + ",";
         open_positions += "\"tp\":" + DoubleToString(PositionGetDouble(POSITION_TP), 5) + ",";
         open_positions += "\"profit\":" + DoubleToString(PositionGetDouble(POSITION_PROFIT), 2) + ",";
         open_positions += "\"hold_time\":" + IntegerToString(TimeCurrent() - PositionGetInteger(POSITION_TIME));
         open_positions += "}";
        }
     }
   open_positions += "]";

   // 2. Format JSON payload manually
   string payload = "{\"sync_key\":\"" + SyncKey + "\"," +
                    "\"equity\":" + DoubleToString(equity, 2) + "," +
                    "\"upnl\":" + DoubleToString(upnl, 2) + "," +
                    "\"current_balance\":" + DoubleToString(current_balance, 2) + "," +
                    "\"open_positions\":" + open_positions + "," +
                    "\"balance\":" + DoubleToString(cached_initial_balance, 2) + "}";
                    
   char post[], result[];
   int str_len = StringLen(payload);
   StringToCharArray(payload, post, 0, str_len); 
   string headers = "Content-Type: application/json\r\n";
   
   string url = WebhookURL + "/equity";
   WebRequest("POST", url, headers, 1000, post, result, headers);
  }

void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
  {
   // Handled by OnTimer history check
  }
