import sys
import asyncio
import yaml
from PySide6.QtWidgets import QApplication
from loguru import logger
from sqlalchemy.orm import sessionmaker

from ui.main_window import MainWindow
from core.broker import FyersBroker
from core.data_feed import FyersDataFeed
from storage.database import get_engine, init_db
from core.order_manager import OrderManager
from core.strategy_engine import StrategyEngine
from core.risk_manager import RiskManager


async def on_connect():
    """Callback on WebSocket connect."""
    logger.success("Data feed connected.")


async def on_close():
    """Callback on WebSocket close."""
    logger.warning("Data feed connection closed.")


async def on_error(error_msg):
    """Callback for WebSocket errors."""
    logger.error(f"Data feed error: {error_msg}")


def setup_logging(settings: dict):
    """Configures the logger based on settings."""
    logger.add(
        settings["logging"]["file"],
        level=settings["logging"]["level"],
        rotation="10 MB",
    )


async def run_backend(
    broker,
    data_feed,
    db_session,
    order_manager,
    risk_manager,
    strategy_engine,
    settings,
):
    try:
        # --- Broker and Core Components Initialization ---
        access_token = await broker.get_access_token()

        if access_token and broker.fyers:
            profile = await broker.get_profile()
            logger.info(f"Broker Profile: {profile}")

            # Now set the order_manager in risk_manager
            risk_manager.order_manager = order_manager

            # --- Connect and Run ---
            ws_access_token = f"{broker.client_id}:{access_token}"
            await data_feed.connect(ws_access_token)

            # Load strategy and subscribe to symbols required by it
            strategy_engine.load_strategy("simple_crossover")
            await data_feed.subscribe(["NSE:SBIN-EQ"])

            logger.info("Auto-trading strategy is now running. Waiting for signals...")
            # Keep the application running to listen for ticks
            # The UI event loop will keep the application alive
            # asyncio.sleep(30) is removed as UI will manage lifetime
        else:
            logger.error(
                "Broker or token initialization failed. Check credentials and logs."
            )

    except Exception as e:
        logger.opt(exception=True).critical(
            f"An error occurred during backend runtime: {e}"
        )
    finally:
        # Cleanup should be handled by the UI's close event or a dedicated shutdown
        logger.info("Backend run function finished.")


async def main():
    """Main function to run the application."""
    try:
        with open("config/settings.yaml", "r") as f:
            settings = yaml.safe_load(f)
    except FileNotFoundError:
        print("Error: settings.yaml not found. Please ensure the file exists.")
        sys.exit(1)
    except yaml.YAMLError as e:
        print(f"Error parsing settings.yaml: {e}")
        sys.exit(1)

    setup_logging(settings)
    logger.info(f"Starting {settings['app']['name']} v{settings['app']['version']}")

    # --- Database Initialization ---
    db_session = None
    try:
        db_path = settings["database"]["path"]
        engine = get_engine(db_path)
        init_db(engine)
        Session = sessionmaker(bind=engine)
        db_session = Session()
        logger.success("Database initialized successfully.")
    except Exception as e:
        logger.opt(exception=True).critical(
            f"An error occurred during database initialization: {e}"
        )
        sys.exit(1)

    broker = None
    data_feed = None
    order_manager = None
    risk_manager = None
    strategy_engine = None

    try:
        broker = FyersBroker(settings=settings["fyers"])
        risk_manager = RiskManager(order_manager=None, settings=settings, logger=logger)
        order_manager = OrderManager(
            broker=broker, db_session=db_session, risk_manager=risk_manager
        )
        strategy_engine = StrategyEngine(order_manager=order_manager)
        data_feed = FyersDataFeed(
            fyers=broker.fyers,
            on_connect=on_connect,
            on_tick=strategy_engine.on_tick,
            on_close=on_close,
            on_error=on_error,
        )

        # Launch UI and pass backend components
        app = QApplication(sys.argv)
        main_window = MainWindow(
            broker=broker,
            data_feed=data_feed,
            db_session=db_session,
            order_manager=order_manager,
            risk_manager=risk_manager,
            strategy_engine=strategy_engine,
            settings=settings,
            backend_runner=run_backend # Pass the async backend runner
        )
        main_window.show()
        logger.info("Application UI started.")

        # Run the Qt event loop in a separate thread or integrate with asyncio
        # For simplicity, we'll let QApplication manage the main thread
        # and run the backend in the asyncio event loop managed by a QThread or similar
        # For now, simply run the Qt app. The backend needs to be started
        # from within the UI or via a separate asyncio loop.
        sys.exit(app.exec())

    except Exception as e:
        logger.opt(exception=True).critical(f"An error occurred: {e}")
    finally:
        if db_session:
            db_session.close()
        logger.info("Application shutdown complete.")


if __name__ == "__main__":
    asyncio.run(main())