import os
import sqlite3
from datetime import date, datetime
from typing import Any, Dict, List, Optional, Tuple

import pandas as pd
from pt_trading.trading_pair import TradingPair


# Recommended replacement adapters and converters for Python 3.12+
# From: https://docs.python.org/3/library/sqlite3.html#sqlite3-adapter-converter-recipes
def adapt_date_iso(val: date) -> str:
    """Adapt datetime.date to ISO 8601 date."""
    return val.isoformat()


def adapt_datetime_iso(val: datetime) -> str:
    """Adapt datetime.datetime to timezone-naive ISO 8601 date."""
    return val.isoformat()


def convert_date(val: bytes) -> date:
    """Convert ISO 8601 date to datetime.date object."""
    return datetime.fromisoformat(val.decode()).date()


def convert_datetime(val: bytes) -> datetime:
    """Convert ISO 8601 datetime to datetime.datetime object."""
    return datetime.fromisoformat(val.decode())


# Register the adapters and converters
sqlite3.register_adapter(date, adapt_date_iso)
sqlite3.register_adapter(datetime, adapt_datetime_iso)
sqlite3.register_converter("date", convert_date)
sqlite3.register_converter("datetime", convert_datetime)


def create_result_database(db_path: str) -> None:
    """
    Create the SQLite database and required tables if they don't exist.
    """
    try:
        # Create directory if it doesn't exist
        db_dir = os.path.dirname(db_path)
        if db_dir and not os.path.exists(db_dir):
            os.makedirs(db_dir, exist_ok=True)
            print(f"Created directory: {db_dir}")
        
        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()

        # Create the pt_bt_results table for completed trades
        cursor.execute(
            """
            CREATE TABLE IF NOT EXISTS pt_bt_results (
                date DATE,
                pair TEXT,
                symbol TEXT,
                open_time DATETIME,
                open_side TEXT,
                open_price REAL,
                open_quantity INTEGER,
                open_disequilibrium REAL,
                close_time DATETIME,
                close_side TEXT,
                close_price REAL,
                close_quantity INTEGER,
                close_disequilibrium REAL,
                symbol_return REAL,
                pair_return REAL
            )
        """
        )
        cursor.execute("DELETE FROM pt_bt_results;")

        # Create the outstanding_positions table for open positions
        cursor.execute(
            """
            CREATE TABLE IF NOT EXISTS outstanding_positions (
                date DATE,
                pair TEXT,
                symbol TEXT,
                position_quantity REAL,
                last_price REAL,
                unrealized_return REAL,
                open_price REAL,
                open_side TEXT
            )
        """
        )
        cursor.execute("DELETE FROM outstanding_positions;")

        # Create the config table for storing configuration JSON for reference
        cursor.execute(
            """
            CREATE TABLE IF NOT EXISTS config (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                run_timestamp DATETIME,
                config_file_path TEXT,
                config_json TEXT,
                fit_method_class TEXT,
                datafiles TEXT,
                instruments TEXT
            )
        """
        )
        cursor.execute("DELETE FROM config;")

        conn.commit()
        conn.close()

    except Exception as e:
        print(f"Error creating result database: {str(e)}")
        raise


def store_config_in_database(
    db_path: str,
    config_file_path: str,
    config: Dict,
    fit_method_class: str,
    datafiles: List[str],
    instruments: List[str],
) -> None:
    """
    Store configuration information in the database for reference.
    """
    import json

    if db_path.upper() == "NONE":
        return

    try:
        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()

        # Convert config to JSON string
        config_json = json.dumps(config, indent=2, default=str)

        # Convert lists to comma-separated strings for storage
        datafiles_str = ", ".join(datafiles)
        instruments_str = ", ".join(instruments)

        # Insert configuration record
        cursor.execute(
            """
            INSERT INTO config (
                run_timestamp, config_file_path, config_json, fit_method_class, datafiles, instruments
            ) VALUES (?, ?, ?, ?, ?, ?)
        """,
            (
                datetime.now(),
                config_file_path,
                config_json,
                fit_method_class,
                datafiles_str,
                instruments_str,
            ),
        )

        conn.commit()
        conn.close()

        print(f"Configuration stored in database")

    except Exception as e:
        print(f"Error storing configuration in database: {str(e)}")
        import traceback

        traceback.print_exc()


def store_results_in_database(
    db_path: str, datafile: str, bt_result: "BacktestResult"
) -> None:
    """
    Store backtest results in the SQLite database.
    """
    if db_path.upper() == "NONE":
        return

    def convert_timestamp(timestamp: Any) -> Optional[datetime]:
        """Convert pandas Timestamp to Python datetime object for SQLite compatibility."""
        if timestamp is None:
            return None
        if hasattr(timestamp, "to_pydatetime"):
            return timestamp.to_pydatetime()
        return timestamp

    try:
        # Extract date from datafile name (assuming format like 20250528.mktdata.ohlcv.db)
        filename = os.path.basename(datafile)
        date_str = filename.split(".")[0]  # Extract date part

        # Convert to proper date format
        try:
            date_obj = datetime.strptime(date_str, "%Y%m%d").date()
        except ValueError:
            # If date parsing fails, use current date
            date_obj = datetime.now().date()

        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()

        # Process each trade from bt_result
        trades = bt_result.get_trades()

        for pair_name, symbols in trades.items():
            # Calculate pair return for this pair
            pair_return = 0.0
            pair_trades = []

            # First pass: collect all trades and calculate returns
            for symbol, symbol_trades in symbols.items():
                if len(symbol_trades) == 0:  # No trades for this symbol
                    print(
                        f"Warning: No trades found for symbol {symbol} in pair {pair_name}"
                    )
                    continue

                elif len(symbol_trades) >= 2:  # Completed trades (entry + exit)
                    # Handle both old and new tuple formats
                    if len(symbol_trades[0]) == 2:  # Old format: (action, price)
                        entry_action, entry_price = symbol_trades[0]
                        exit_action, exit_price = symbol_trades[1]
                        open_disequilibrium = 0.0  # Fallback for old format
                        open_scaled_disequilibrium = 0.0
                        close_disequilibrium = 0.0
                        close_scaled_disequilibrium = 0.0
                        open_time = datetime.now()
                        close_time = datetime.now()
                    else:  # New format: (action, price, disequilibrium, scaled_disequilibrium, timestamp)
                        (
                            entry_action,
                            entry_price,
                            open_disequilibrium,
                            open_scaled_disequilibrium,
                            open_time,
                        ) = symbol_trades[0]
                        (
                            exit_action,
                            exit_price,
                            close_disequilibrium,
                            close_scaled_disequilibrium,
                            close_time,
                        ) = symbol_trades[1]

                        # Handle None values
                        open_disequilibrium = (
                            open_disequilibrium
                            if open_disequilibrium is not None
                            else 0.0
                        )
                        open_scaled_disequilibrium = (
                            open_scaled_disequilibrium
                            if open_scaled_disequilibrium is not None
                            else 0.0
                        )
                        close_disequilibrium = (
                            close_disequilibrium
                            if close_disequilibrium is not None
                            else 0.0
                        )
                        close_scaled_disequilibrium = (
                            close_scaled_disequilibrium
                            if close_scaled_disequilibrium is not None
                            else 0.0
                        )

                        # Convert pandas Timestamps to Python datetime objects
                        open_time = convert_timestamp(open_time) or datetime.now()
                        close_time = convert_timestamp(close_time) or datetime.now()

                    # Calculate actual share quantities based on funding per pair
                    # Split funding equally between the two positions
                    funding_per_position = bt_result.config["funding_per_pair"] / 2
                    shares = funding_per_position / entry_price

                    # Calculate symbol return
                    symbol_return = 0.0
                    if entry_action == "BUY" and exit_action == "SELL":
                        symbol_return = (exit_price - entry_price) / entry_price * 100
                    elif entry_action == "SELL" and exit_action == "BUY":
                        symbol_return = (entry_price - exit_price) / entry_price * 100

                    pair_return += symbol_return

                    pair_trades.append(
                        {
                            "symbol": symbol,
                            "entry_action": entry_action,
                            "entry_price": entry_price,
                            "exit_action": exit_action,
                            "exit_price": exit_price,
                            "symbol_return": symbol_return,
                            "open_disequilibrium": open_disequilibrium,
                            "open_scaled_disequilibrium": open_scaled_disequilibrium,
                            "close_disequilibrium": close_disequilibrium,
                            "close_scaled_disequilibrium": close_scaled_disequilibrium,
                            "open_time": open_time,
                            "close_time": close_time,
                            "shares": shares,
                            "is_completed": True,
                        }
                    )

                # Skip one-sided trades - they will be handled by outstanding_positions table
                elif len(symbol_trades) == 1:
                    print(
                        f"Skipping one-sided trade for {symbol} in pair {pair_name} - will be stored in outstanding_positions table"
                    )
                    continue

                else:
                    # This should not happen, but handle unexpected cases
                    print(
                        f"Warning: Unexpected number of trades ({len(symbol_trades)}) for symbol {symbol} in pair {pair_name}"
                    )
                    continue

            # Second pass: insert completed trade records into database
            for trade in pair_trades:
                # Only store completed trades in pt_bt_results table
                cursor.execute(
                    """
                    INSERT INTO pt_bt_results (
                        date, pair, symbol, open_time, open_side, open_price, 
                        open_quantity, open_disequilibrium, close_time, close_side, 
                        close_price, close_quantity, close_disequilibrium, 
                        symbol_return, pair_return
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """,
                    (
                        date_obj,
                        pair_name,
                        trade["symbol"],
                        trade["open_time"],
                        trade["entry_action"],
                        trade["entry_price"],
                        trade["shares"],
                        trade["open_scaled_disequilibrium"],
                        trade["close_time"],
                        trade["exit_action"],
                        trade["exit_price"],
                        trade["shares"],
                        trade["close_scaled_disequilibrium"],
                        trade["symbol_return"],
                        pair_return,
                    ),
                )

        # Store outstanding positions in separate table
        outstanding_positions = bt_result.get_outstanding_positions()
        for pos in outstanding_positions:
            # Calculate position quantity (negative for SELL positions)
            position_qty_a = (
                pos["shares_a"] if pos["side_a"] == "BUY" else -pos["shares_a"]
            )
            position_qty_b = (
                pos["shares_b"] if pos["side_b"] == "BUY" else -pos["shares_b"]
            )

            # Calculate unrealized returns
            # For symbol A: (current_price - open_price) / open_price * 100 * position_direction
            unrealized_return_a = (
                (pos["current_px_a"] - pos["open_px_a"]) / pos["open_px_a"] * 100
            ) * (1 if pos["side_a"] == "BUY" else -1)
            unrealized_return_b = (
                (pos["current_px_b"] - pos["open_px_b"]) / pos["open_px_b"] * 100
            ) * (1 if pos["side_b"] == "BUY" else -1)

            # Store outstanding position for symbol A
            cursor.execute(
                """
                INSERT INTO outstanding_positions (
                    date, pair, symbol, position_quantity, last_price, unrealized_return, open_price, open_side
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            """,
                (
                    date_obj,
                    pos["pair"],
                    pos["symbol_a"],
                    position_qty_a,
                    pos["current_px_a"],
                    unrealized_return_a,
                    pos["open_px_a"],
                    pos["side_a"],
                ),
            )

            # Store outstanding position for symbol B
            cursor.execute(
                """
                INSERT INTO outstanding_positions (
                    date, pair, symbol, position_quantity, last_price, unrealized_return, open_price, open_side
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            """,
                (
                    date_obj,
                    pos["pair"],
                    pos["symbol_b"],
                    position_qty_b,
                    pos["current_px_b"],
                    unrealized_return_b,
                    pos["open_px_b"],
                    pos["side_b"],
                ),
            )

        conn.commit()
        conn.close()

    except Exception as e:
        print(f"Error storing results in database: {str(e)}")
        import traceback

        traceback.print_exc()


class BacktestResult:
    """
    Class to handle backtest results, trades tracking, PnL calculations, and reporting.
    """

    def __init__(self, config: Dict[str, Any]):
        self.config = config
        self.trades: Dict[str, Dict[str, Any]] = {}
        self.total_realized_pnl = 0.0
        self.outstanding_positions: List[Dict[str, Any]] = []

    def add_trade(
        self,
        pair_nm: str,
        symbol: str,
        action: str,
        price: Any,
        disequilibrium: Optional[float] = None,
        scaled_disequilibrium: Optional[float] = None,
        timestamp: Optional[datetime] = None,
    ) -> None:
        """Add a trade to the results tracking."""
        pair_nm = str(pair_nm)

        if pair_nm not in self.trades:
            self.trades[pair_nm] = {symbol: []}
        if symbol not in self.trades[pair_nm]:
            self.trades[pair_nm][symbol] = []
        self.trades[pair_nm][symbol].append(
            (action, price, disequilibrium, scaled_disequilibrium, timestamp)
        )

    def add_outstanding_position(self, position: Dict[str, Any]) -> None:
        """Add an outstanding position to tracking."""
        self.outstanding_positions.append(position)

    def add_realized_pnl(self, realized_pnl: float) -> None:
        """Add realized PnL to the total."""
        self.total_realized_pnl += realized_pnl

    def get_total_realized_pnl(self) -> float:
        """Get total realized PnL."""
        return self.total_realized_pnl

    def get_outstanding_positions(self) -> List[Dict[str, Any]]:
        """Get all outstanding positions."""
        return self.outstanding_positions

    def get_trades(self) -> Dict[str, Dict[str, Any]]:
        """Get all trades."""
        return self.trades

    def clear_trades(self) -> None:
        """Clear all trades (used when processing new files)."""
        self.trades.clear()

    def collect_single_day_results(self, pairs_trades: List[pd.DataFrame]) -> None:
        """Collect and process single day trading results."""
        result = pd.concat(pairs_trades, ignore_index=True)
        result["time"] = pd.to_datetime(result["time"])
        result = result.set_index("time").sort_index()

        print("\n --------------  Suggested Trades ")
        print(result)

        for row in result.itertuples():
            action = row.action
            symbol = row.symbol
            price = row.price
            disequilibrium = getattr(row, "disequilibrium", None)
            scaled_disequilibrium = getattr(row, "scaled_disequilibrium", None)
            timestamp = getattr(row, "time", None)
            self.add_trade(
                pair_nm=str(row.pair),
                action=str(action),
                symbol=str(symbol),
                price=float(str(price)),
                disequilibrium=disequilibrium,
                scaled_disequilibrium=scaled_disequilibrium,
                timestamp=timestamp,
            )

    def print_single_day_results(self) -> None:
        """Print single day results summary."""
        for pair, symbols in self.trades.items():
            print(f"\n--- {pair} ---")
            for symbol, trades in symbols.items():
                for trade_data in trades:
                    if len(trade_data) >= 2:
                        side, price = trade_data[:2]
                        print(f"{symbol} {side} at ${price}")

    def print_results_summary(self, all_results: Dict[str, Dict[str, Any]]) -> None:
        """Print summary of all processed files."""
        print("\n====== Summary of All Processed Files ======")
        for filename, data in all_results.items():
            trade_count = sum(
                len(trades)
                for symbol_trades in data["trades"].values()
                for trades in symbol_trades.values()
            )
            print(f"{filename}: {trade_count} trades")

    def calculate_returns(self, all_results: Dict[str, Dict[str, Any]]) -> None:
        """Calculate and print returns by day and pair."""
        print("\n====== Returns By Day and Pair ======")

        for filename, data in all_results.items():
            day_return = 0
            print(f"\n--- {filename} ---")

            self.outstanding_positions = data["outstanding_positions"]

            # Process each pair
            for pair, symbols in data["trades"].items():
                pair_return = 0
                pair_trades = []

                # Calculate individual symbol returns in the pair
                for symbol, trades in symbols.items():
                    if len(trades) == 0:
                        continue
                    
                    symbol_return = 0
                    symbol_trades = []
                    
                    # Process all trades sequentially for this symbol
                    for i, trade in enumerate(trades):
                        # Handle both old and new tuple formats
                        if len(trade) == 2:  # Old format: (action, price)
                            action, price = trade
                            disequilibrium = None
                            scaled_disequilibrium = None
                            timestamp = None
                        else:  # New format: (action, price, disequilibrium, scaled_disequilibrium, timestamp)
                            action, price = trade[:2]
                            disequilibrium = trade[2] if len(trade) > 2 else None
                            scaled_disequilibrium = trade[3] if len(trade) > 3 else None
                            timestamp = trade[4] if len(trade) > 4 else None
                        
                        symbol_trades.append((action, price, disequilibrium, scaled_disequilibrium, timestamp))
                    
                    # Calculate returns for all trade combinations
                    for i in range(len(symbol_trades) - 1):
                        trade1 = symbol_trades[i]
                        trade2 = symbol_trades[i + 1]
                        
                        action1, price1, diseq1, scaled_diseq1, ts1 = trade1
                        action2, price2, diseq2, scaled_diseq2, ts2 = trade2
                        
                        # Calculate return based on action combination
                        trade_return = 0
                        if action1 == "BUY" and action2 == "SELL":
                            # Long position
                            trade_return = (price2 - price1) / price1 * 100
                        elif action1 == "SELL" and action2 == "BUY":
                            # Short position
                            trade_return = (price1 - price2) / price1 * 100
                        
                        symbol_return += trade_return
                        
                        # Store trade details for reporting
                        pair_trades.append(
                            (
                                symbol,
                                action1,
                                price1,
                                action2,
                                price2,
                                trade_return,
                                scaled_diseq1,
                                scaled_diseq2,
                                i + 1,  # Trade sequence number
                            )
                        )
                    
                    pair_return += symbol_return

                # Print pair returns with disequilibrium information
                if pair_trades:
                    print(f"  {pair}:")
                    for (
                        symbol,
                        action1,
                        price1,
                        action2,
                        price2,
                        trade_return,
                        scaled_diseq1,
                        scaled_diseq2,
                        trade_num,
                    ) in pair_trades:
                        disequil_info = ""
                        if (
                            scaled_diseq1 is not None
                            and scaled_diseq2 is not None
                        ):
                            disequil_info = f" | Open Dis-eq: {scaled_diseq1:.2f}, Close Dis-eq: {scaled_diseq2:.2f}"

                        print(
                            f"    {symbol} (Trade #{trade_num}): {action1} @ ${price1:.2f}, {action2} @ ${price2:.2f}, Return: {trade_return:.2f}%{disequil_info}"
                        )
                    print(f"    Pair Total Return: {pair_return:.2f}%")
                    day_return += pair_return

            # Print day total return and add to global realized PnL
            if day_return != 0:
                print(f"  Day Total Return: {day_return:.2f}%")
                self.add_realized_pnl(day_return)

    def print_outstanding_positions(self) -> None:
        """Print all outstanding positions with share quantities and current values."""
        if not self.get_outstanding_positions():
            print("\n====== NO OUTSTANDING POSITIONS ======")
            return

        print(f"\n====== OUTSTANDING POSITIONS ======")
        print(
            f"{'Pair':<15}"
            f" {'Symbol':<10}"
            f" {'Side':<4}"
            f" {'Shares':<10}"
            f" {'Open $':<8}"
            f" {'Current $':<10}"
            f" {'Value $':<12}"
            f" {'Disequilibrium':<15}"
        )
        print("-" * 100)

        total_value = 0.0

        for pos in self.get_outstanding_positions():
            # Print position A
            print(
                f"{pos['pair']:<15}"
                f" {pos['symbol_a']:<10}"
                f" {pos['side_a']:<4}"
                f" {pos['shares_a']:<10.2f}"
                f" {pos['open_px_a']:<8.2f}"
                f" {pos['current_px_a']:<10.2f}"
                f" {pos['current_value_a']:<12.2f}"
                f" {'':<15}"
            )

            # Print position B
            print(
                f"{'':<15}"
                f" {pos['symbol_b']:<10}"
                f" {pos['side_b']:<4}"
                f" {pos['shares_b']:<10.2f}"
                f" {pos['open_px_b']:<8.2f}"
                f" {pos['current_px_b']:<10.2f}"
                f" {pos['current_value_b']:<12.2f}"
            )

            # Print pair totals with disequilibrium info
            print(
                f"{'':<15}"
                f" {'PAIR TOTAL':<10}"
                f" {'':<4}"
                f" {'':<10}"
                f" {'':<8}"
                f" {'':<10}"
                f" {pos['total_current_value']:<12.2f}"
            )

            # Print disequilibrium details
            print(
                f"{'':<15}"
                f" {'DISEQUIL':<10}"
                f" {'':<4}"
                f" {'':<10}"
                f" {'':<8}"
                f" {'':<10}"
                f" Raw: {pos['current_disequilibrium']:<6.4f}"
                f" Scaled: {pos['current_scaled_disequilibrium']:<6.4f}"
            )

            print("-" * 100)

            total_value += pos["total_current_value"] 

        print(f"{'TOTAL OUTSTANDING VALUE':<80} ${total_value:<12.2f}")

    def print_grand_totals(self) -> None:
        """Print grand totals across all pairs."""
        print(f"\n====== GRAND TOTALS ACROSS ALL PAIRS ======")
        print(f"Total Realized PnL: {self.get_total_realized_pnl():.2f}%")

    def handle_outstanding_position(
        self,
        pair: TradingPair,
        pair_result_df: pd.DataFrame,
        last_row_index: int,
        open_side_a: str,
        open_side_b: str,
        open_px_a: float,
        open_px_b: float,
        open_tstamp: datetime,
    ) -> Tuple[float, float, float]:
        """
        Handle calculation and tracking of outstanding positions when no close signal is found.

        Args:
            pair: TradingPair object
            pair_result_df: DataFrame with pair results
            last_row_index: Index of the last row in the data
            open_side_a, open_side_b: Trading sides for symbols A and B
            open_px_a, open_px_b: Opening prices for symbols A and B
            open_tstamp: Opening timestamp
        """
        if pair_result_df is None or pair_result_df.empty:
            return 0, 0, 0

        last_row = pair_result_df.loc[last_row_index]
        last_tstamp = last_row["tstamp"]
        colname_a, colname_b = pair.colnames()
        last_px_a = last_row[colname_a]
        last_px_b = last_row[colname_b]

        # Calculate share quantities based on funding per pair
        # Split funding equally between the two positions
        funding_per_position = self.config["funding_per_pair"] / 2
        shares_a = funding_per_position / open_px_a
        shares_b = funding_per_position / open_px_b

        # Calculate current position values (shares * current price)
        current_value_a = shares_a * last_px_a * (-1 if open_side_a == "SELL" else 1)
        current_value_b = shares_b * last_px_b * (-1 if open_side_b == "SELL" else 1)
        total_current_value = current_value_a + current_value_b

        # Get disequilibrium information
        current_disequilibrium = last_row["disequilibrium"]
        current_scaled_disequilibrium = last_row["scaled_disequilibrium"]

        # Store outstanding positions
        self.add_outstanding_position(
            {
                "pair": str(pair),
                "symbol_a": pair.symbol_a_,
                "symbol_b": pair.symbol_b_,
                "side_a": open_side_a,
                "side_b": open_side_b,
                "shares_a": shares_a,
                "shares_b": shares_b,
                "open_px_a": open_px_a,
                "open_px_b": open_px_b,
                "current_px_a": last_px_a,
                "current_px_b": last_px_b,
                "current_value_a": current_value_a,
                "current_value_b": current_value_b,
                "total_current_value": total_current_value,
                "open_time": open_tstamp,
                "last_time": last_tstamp,
                "current_abs_term": current_scaled_disequilibrium,
                "current_disequilibrium": current_disequilibrium,
                "current_scaled_disequilibrium": current_scaled_disequilibrium,
            }
        )

        # Print position details
        print(f"{pair}: NO CLOSE SIGNAL FOUND - Position held until end of session")
        print(f"  Open: {open_tstamp} | Last: {last_tstamp}")
        print(
            f"  {pair.symbol_a_}: {open_side_a} {shares_a:.2f} shares @ ${open_px_a:.2f} -> ${last_px_a:.2f} | Value: ${current_value_a:.2f}"
        )
        print(
            f"  {pair.symbol_b_}: {open_side_b} {shares_b:.2f} shares @ ${open_px_b:.2f} -> ${last_px_b:.2f} | Value: ${current_value_b:.2f}"
        )
        print(f"  Total Value: ${total_current_value:.2f}")
        print(
            f"  Disequilibrium: {current_disequilibrium:.4f} | Scaled: {current_scaled_disequilibrium:.4f}"
        )

        return current_value_a, current_value_b, total_current_value
