import {CardPositionItem, TradeChart} from '..'
import {Backtest} from '../../../generated/ManagingApi'
import {CardPosition, CardText} from '../../mollecules'

interface IBacktestRowDetailsProps {
  backtest: Backtest;
  optimizedMoneyManagement: {
    stopLoss: number;
    takeProfit: number;
  };
}

const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
  backtest,
  optimizedMoneyManagement
}) => {
  const {
    candles,
    positions,
    walletBalances,
    strategiesValues,
    signals,
    statistics,
    config
  } = backtest;

  // Helper function to calculate position open time in hours
  const calculateOpenTimeInHours = (position: any) => {
    const openDate = new Date(position.open.date);
    let closeDate: Date | null = null;

    // Determine close date based on realized P&L (matching backend logic)
    if (position.profitAndLoss?.realized != null) {
      if (position.profitAndLoss.realized > 0) {
        // Profitable close = Take Profit
        closeDate = new Date(position.takeProfit1.date);
      } else {
        // Loss or breakeven close = Stop Loss
        closeDate = new Date(position.stopLoss.date);
      }
    }

    if (closeDate) {
      const diffInMs = closeDate.getTime() - openDate.getTime();
      return diffInMs / (1000 * 60 * 60); // Convert to hours
    }
    return 0;
  };

  // Calculate average open time for winning positions
  const getAverageOpenTimeWinning = () => {
    const winningPositions = positions.filter((p) => {
      const realized = p.profitAndLoss?.realized ?? 0;
      return realized > 0;
    });

    if (winningPositions.length === 0) return "0.00";

    const totalHours = winningPositions.reduce((sum, position) => {
      return sum + calculateOpenTimeInHours(position);
    }, 0);

    const averageHours = totalHours / winningPositions.length;
    return averageHours.toFixed(2);
  };

  // Calculate average open time for losing positions
  const getAverageOpenTimeLosing = () => {
    const losingPositions = positions.filter((p) => {
      const realized = p.profitAndLoss?.realized ?? 0;
      return realized <= 0;
    });

    if (losingPositions.length === 0) return "0.00";

    const totalHours = losingPositions.reduce((sum, position) => {
      return sum + calculateOpenTimeInHours(position);
    }, 0);

    const averageHours = totalHours / losingPositions.length;
    return averageHours.toFixed(2);
  };

  // Calculate maximum open time for winning positions
  const getMaxOpenTimeWinning = () => {
    const winningPositions = positions.filter((p) => {
      const realized = p.profitAndLoss?.realized ?? 0;
      return realized > 0;
    });

    if (winningPositions.length === 0) return "0.00";

    const openTimes = winningPositions.map(position => calculateOpenTimeInHours(position));
    const maxHours = Math.max(...openTimes);
    return maxHours.toFixed(2);
  };

  // Calculate median opening time for all positions
  const getMedianOpenTime = () => {
    if (positions.length === 0) return "0.00";

    const openTimes = positions.map(position => calculateOpenTimeInHours(position));
    const sortedTimes = openTimes.sort((a, b) => a - b);
    
    const mid = Math.floor(sortedTimes.length / 2);
    const median = sortedTimes.length % 2 === 0
      ? (sortedTimes[mid - 1] + sortedTimes[mid]) / 2
      : sortedTimes[mid];
    
    return median.toFixed(2);
  };

  // Calculate total volume traded with leverage
  const getTotalVolumeTraded = () => {
    let totalVolume = 0;

    positions.forEach((position) => {
      // Calculate volume for open trade
      const openLeverage = position.open.leverage || 1;
      const openVolume = position.open.quantity * position.open.price * openLeverage;
      totalVolume += openVolume;

      // Calculate volume for close trade (stopLoss or takeProfit based on realized P&L)
      if (position.profitAndLoss?.realized != null) {
        let closeTrade;
        if (position.profitAndLoss.realized > 0) {
          // Profitable close = Take Profit
          closeTrade = position.takeProfit1;
        } else {
          // Loss or breakeven close = Stop Loss
          closeTrade = position.stopLoss;
        }

        if (closeTrade) {
          const closeLeverage = closeTrade.leverage || 1;
          const closeVolume = closeTrade.quantity * closeTrade.price * closeLeverage;
          totalVolume += closeVolume;
        }
      }
    });

    return totalVolume;
  };

  // Calculate estimated UI fee (0.02% of total volume)
  const getEstimatedUIFee = () => {
    const totalVolume = getTotalVolumeTraded();
    const uiFeePercentage = 0.001; // 0.1%
    return totalVolume * uiFeePercentage;
  };

  // Calculate recommended cooldown based on positions that fail after a win
  const getCooldownRecommendations = () => {
    if (positions.length < 2 || !candles || candles.length < 2) {
      return { percentile75: "0", average: "0", median: "0" };
    }

    // Determine candle timeframe in milliseconds
    const candleTimeframeMs = new Date(candles[1].date).getTime() - new Date(candles[0].date).getTime();

    const sortedPositions = [...positions].sort((a, b) => {
      const dateA = new Date(a.open.date).getTime();
      const dateB = new Date(b.open.date).getTime();
      return dateA - dateB;
    });

    const failAfterWinGaps: number[] = [];

    for (let i = 0; i < sortedPositions.length - 1; i++) {
      const currentPosition = sortedPositions[i];
      const nextPosition = sortedPositions[i + 1];

      const currentRealized = currentPosition.profitAndLoss?.realized ?? 0;
      const nextRealized = nextPosition.profitAndLoss?.realized ?? 0;

      // Check if current position is winning and next position is losing
      if (currentRealized > 0 && nextRealized <= 0) {
        // Calculate the close time of the current (winning) position
        let currentCloseDate: Date | null = null;
        if (currentPosition.profitAndLoss?.realized != null) {
          if (currentPosition.profitAndLoss.realized > 0) {
            currentCloseDate = new Date(currentPosition.takeProfit1.date);
          } else {
            currentCloseDate = new Date(currentPosition.stopLoss.date);
          }
        }

        if (currentCloseDate) {
          const nextOpenDate = new Date(nextPosition.open.date);
          const gapInMs = nextOpenDate.getTime() - currentCloseDate.getTime();
          
          if (gapInMs >= 0) { // Only consider positive gaps
            // Convert milliseconds to number of candles
            const gapInCandles = Math.floor(gapInMs / candleTimeframeMs);
            failAfterWinGaps.push(gapInCandles);
          }
        }
      }
    }

    if (failAfterWinGaps.length === 0) {
      return { percentile75: "0", average: "0", median: "0" };
    }

    // Calculate the 75th percentile
    const sortedGaps = [...failAfterWinGaps].sort((a, b) => a - b);
    const percentile75Index = Math.floor(sortedGaps.length * 0.75);
    const percentile75 = sortedGaps[percentile75Index] || 0;

    // Calculate the average
    const sum = failAfterWinGaps.reduce((acc, gap) => acc + gap, 0);
    const average = sum / failAfterWinGaps.length;

    // Calculate the median
    const mid = Math.floor(sortedGaps.length / 2);
    const median = sortedGaps.length % 2 === 0
      ? (sortedGaps[mid - 1] + sortedGaps[mid]) / 2
      : sortedGaps[mid];
    
    return {
      percentile75: Math.ceil(percentile75).toString(),
      average: Math.ceil(average).toString(),
      median: Math.ceil(median).toString()
    };
  };

  const cooldownRecommendations = getCooldownRecommendations();

  // Calculate average trades per day
  const getAverageTradesPerDay = () => {
    if (positions.length === 0) return "0.00";

    // Get all trade dates and sort them
    const tradeDates = positions.map(position => new Date(position.open.date)).sort((a, b) => a.getTime() - b.getTime());
    
    if (tradeDates.length < 2) return positions.length.toString();

    // Calculate the date range in days
    const firstTradeDate = tradeDates[0];
    const lastTradeDate = tradeDates[tradeDates.length - 1];
    const diffInMs = lastTradeDate.getTime() - firstTradeDate.getTime();
    const diffInDays = Math.max(1, diffInMs / (1000 * 60 * 60 * 24)); // Ensure at least 1 day

    const averageTradesPerDay = positions.length / diffInDays;
    return averageTradesPerDay.toFixed(2);
  };

  return (
    <>
      <div className="grid grid-flow-row">
        <div className="grid grid-cols-4 p-5">
          <CardPosition
            positivePosition={true}
            positions={positions.filter((p) => {
              const realized = p.profitAndLoss?.realized ?? 0
              return realized > 0 ? p : null
            })}
          ></CardPosition>
          <CardPosition
            positivePosition={false}
            positions={positions.filter((p) => {
              const realized = p.profitAndLoss?.realized ?? 0
              return realized <= 0 ? p : null
            })}
          ></CardPosition>
          <CardPositionItem positions={positions}></CardPositionItem>
          <CardText
            title="Max Drowdown"
            content={
              statistics.maxDrawdown?.toFixed(4).toString() +
              '$'
            }
          ></CardText>
          <CardText
            title="Sharpe Ratio"
            content={
              (statistics.sharpeRatio
                ? statistics.sharpeRatio * 100
                : 0
              )
                .toFixed(4)
                .toString() + '%'
            }
          ></CardText>
          <CardText
            title="Max Drawdown Recovery Time"
            content={
              statistics.maxDrawdownRecoveryTime?.toString() +
              ' days'
            }
          ></CardText>
          <CardText
            title="Money Management"
            content={
              "SL: " +(config.moneyManagement?.stopLoss * 100).toFixed(2) + "% TP: " + 
              (config.moneyManagement?.takeProfit * 100).toFixed(2) + "%"
            }
          ></CardText>
          <CardText
            title="Optimized Money Management"
            content={
              "SL: " + backtest.optimizedMoneyManagement?.stopLoss.toFixed(2) + "% TP: " + backtest.optimizedMoneyManagement?.takeProfit.toFixed(2) + "%"
            }
          ></CardText>
          <CardText
            title="Avg Open Time (Winning)"
            content={getAverageOpenTimeWinning() + " hours"}
          ></CardText>
          <CardText
            title="Avg Open Time (Losing)"
            content={getAverageOpenTimeLosing() + " hours"}
          ></CardText>
          <CardText
            title="Max Open Time (Winning)"
            content={getMaxOpenTimeWinning() + " hours"}
          ></CardText>
          <CardText
            title="Median Open Time"
            content={getMedianOpenTime() + " hours"}
          ></CardText>
          <CardText
            title="Volume Traded"
            content={"$" + getTotalVolumeTraded().toLocaleString('en-US', { 
              minimumFractionDigits: 2, 
              maximumFractionDigits: 2 
            })}
          ></CardText>
          <CardText
            title="Estimated UI Fee"
            content={"$" + getEstimatedUIFee().toLocaleString('en-US', { 
              minimumFractionDigits: 2, 
              maximumFractionDigits: 2 
            })}
          ></CardText>
          <CardText
            title="Recommended Cooldown"
            content={cooldownRecommendations.percentile75 + " candles"}
          ></CardText>
          <CardText
            title="Average Cooldown"
            content={cooldownRecommendations.average + " candles"}
          ></CardText>
          <CardText
            title="Median Cooldown"
            content={cooldownRecommendations.median + " candles"}
          ></CardText>
          <CardText
            title="Avg Trades Per Day"
            content={getAverageTradesPerDay() + " trades/day"}
          ></CardText>
        </div>
        <div>
          <figure>
            <TradeChart
              width={1400}
              height={1100}
              candles={candles}
              positions={positions}
              walletBalances={walletBalances}
              strategiesValues={strategiesValues}
              signals={signals}
            ></TradeChart>
          </figure>
        </div>
      </div>
    </>
  )
}

export default BacktestRowDetails
