"""
Async RSN data processing module.

Provides asynchronous processing for better performance when
handling multiple chains or when integrating with async systems.
"""

import asyncio
import time
import logging
from typing import Tuple
from ..common.database_async import AsyncDatabaseConfig, AsyncDatabaseConnection
from ..common.logging_utils import setup_logger, log_elapsed_time


async def process_rsn_chain_async(control_unit_id: str, chain: str) -> int:
    """
    Process RSN chain data asynchronously.

    Args:
        control_unit_id: Control unit identifier
        chain: Chain identifier

    Returns:
        0 if successful, 1 if error
    """
    start_time = time.time()

    # Setup logger
    logger = setup_logger(control_unit_id, chain, "RSN-Async")

    try:
        # Load database configuration
        config = AsyncDatabaseConfig()

        # Connect to database with async connection pool
        async with AsyncDatabaseConnection(config) as conn:
            logger.info("Async database connection established")

            # Load configuration concurrently
            logger.info("Loading configuration in parallel")

            # These queries can run concurrently
            unit_query = "SELECT unitID FROM control_units WHERE controlUnitCode = %s"
            config_query = """
                SELECT initialDate, initialTime
                FROM chain_configuration
                WHERE unitID = %s AND chain = %s
            """

            # Run queries concurrently using asyncio.gather
            unit_result, config_result = await asyncio.gather(
                conn.execute_query(unit_query, (control_unit_id,)),
                # We don't have unit_id yet, so this is a simplified example
                # In practice, you'd do this in two stages
                conn.execute_query("SELECT NOW() as current_time")
            )

            if not unit_result:
                raise ValueError(f"Control unit {control_unit_id} not found")

            unit_id = unit_result[0]['unitID']

            # Get node types
            nodes_query = """
                SELECT idTool, nodeID, nodeType
                FROM chain_nodes
                WHERE unitID = %s AND chain = %s
                ORDER BY nodeOrder
            """
            nodes_result = await conn.execute_query(nodes_query, (unit_id, chain))

            if not nodes_result:
                logger.warning("No nodes found for this chain")
                return 0

            # Organize nodes by type
            rsn_nodes = [r['nodeID'] for r in nodes_result if r['nodeType'] == 'RSN']
            rsn_hr_nodes = [r['nodeID'] for r in nodes_result if r['nodeType'] == 'RSNHR']
            ll_nodes = [r['nodeID'] for r in nodes_result if r['nodeType'] == 'LL']

            logger.info(f"Found {len(rsn_nodes)} RSN, {len(rsn_hr_nodes)} RSNHR, {len(ll_nodes)} LL nodes")

            # Load calibration data for all sensor types concurrently
            cal_queries = []
            if rsn_nodes:
                cal_queries.append(
                    load_calibration_async(conn, control_unit_id, chain, rsn_nodes, 'RSN')
                )
            if rsn_hr_nodes:
                cal_queries.append(
                    load_calibration_async(conn, control_unit_id, chain, rsn_hr_nodes, 'RSNHR')
                )
            if ll_nodes:
                cal_queries.append(
                    load_calibration_async(conn, control_unit_id, chain, ll_nodes, 'LL')
                )

            if cal_queries:
                calibrations = await asyncio.gather(*cal_queries)
                logger.info(f"Loaded calibration for {len(calibrations)} sensor types concurrently")

            # Load raw data (this could also be parallelized by sensor type)
            logger.info("Loading sensor data")

            # Process data (CPU-bound, so still sync but in executor if needed)
            # For truly CPU-bound operations, use ProcessPoolExecutor
            loop = asyncio.get_event_loop()
            # result = await loop.run_in_executor(None, process_cpu_intensive_task, data)

            # Write processed data back (can be done concurrently per sensor type)
            logger.info("Writing processed data to database")

            # Simulate write operations
            write_tasks = []
            if rsn_nodes:
                write_tasks.append(
                    write_sensor_data_async(conn, control_unit_id, chain, 'RSN', [])
                )
            if rsn_hr_nodes:
                write_tasks.append(
                    write_sensor_data_async(conn, control_unit_id, chain, 'RSNHR', [])
                )

            if write_tasks:
                await asyncio.gather(*write_tasks)

            logger.info("RSN async processing completed successfully")

        # Log elapsed time
        elapsed = time.time() - start_time
        log_elapsed_time(logger, elapsed)

        return 0

    except Exception as e:
        logger.error(f"Error processing RSN chain async: {e}", exc_info=True)
        return 1


async def load_calibration_async(
    conn: AsyncDatabaseConnection,
    control_unit_id: str,
    chain: str,
    node_list: list,
    sensor_type: str
):
    """
    Load calibration data asynchronously.

    Args:
        conn: Async database connection
        control_unit_id: Control unit identifier
        chain: Chain identifier
        node_list: List of node IDs
        sensor_type: Sensor type

    Returns:
        Calibration data array
    """
    query = """
        SELECT nodeID, calibration_values
        FROM sensor_calibration
        WHERE IDcentralina = %s
          AND DTcatena = %s
          AND sensorType = %s
          AND nodeID IN (%s)
        ORDER BY calibrationDate DESC
    """

    node_ids = ','.join(map(str, node_list))
    results = await conn.execute_query(
        query,
        (control_unit_id, chain, sensor_type, node_ids)
    )

    logger.info(f"Loaded calibration for {len(results)} {sensor_type} sensors")
    return results


async def write_sensor_data_async(
    conn: AsyncDatabaseConnection,
    control_unit_id: str,
    chain: str,
    sensor_type: str,
    data: list
) -> None:
    """
    Write sensor data asynchronously.

    Args:
        conn: Async database connection
        control_unit_id: Control unit identifier
        chain: Chain identifier
        sensor_type: Sensor type
        data: Data to write
    """
    if not data:
        return

    query = f"""
        INSERT INTO elaborated_{sensor_type.lower()}_data
        (IDcentralina, DTcatena, timestamp, nodeID, value1, value2, error_flag)
        VALUES (%s, %s, %s, %s, %s, %s, %s)
        ON DUPLICATE KEY UPDATE
        value1 = VALUES(value1),
        value2 = VALUES(value2),
        error_flag = VALUES(error_flag)
    """

    await conn.execute_many(query, data)
    logger.info(f"Wrote {len(data)} {sensor_type} records")


# Batch processing of multiple stations
async def process_all_stations_async(stations_config: list) -> dict:
    """
    Process all configured stations concurrently.

    This is the main benefit of async - processing multiple independent
    stations at the same time instead of sequentially.

    Args:
        stations_config: List of station configurations

    Returns:
        Dictionary with results per station

    Example:
        stations = [
            {'id': 'CU001', 'chain': 'A'},
            {'id': 'CU002', 'chain': 'B'},
            {'id': 'CU003', 'chain': 'C'},
        ]
        results = await process_all_stations_async(stations)
        # Processes all 3 stations concurrently!
    """
    tasks = []
    for station in stations_config:
        task = process_rsn_chain_async(station['id'], station['chain'])
        tasks.append((station['id'], station['chain'], task))

    logger.info(f"Processing {len(tasks)} stations concurrently")

    results = {}
    for station_id, chain, task in tasks:
        try:
            result = await task
            results[f"{station_id}-{chain}"] = {
                'success': result == 0,
                'error': None
            }
        except Exception as e:
            results[f"{station_id}-{chain}"] = {
                'success': False,
                'error': str(e)
            }

    return results


async def main():
    """
    Main entry point for async processing.

    Usage:
        python -m src.rsn.main_async CU001 A
    """
    import sys

    if len(sys.argv) < 3:
        print("Usage: python -m src.rsn.main_async <control_unit_id> <chain>")
        sys.exit(1)

    control_unit_id = sys.argv[1]
    chain = sys.argv[2]

    exit_code = await process_rsn_chain_async(control_unit_id, chain)
    sys.exit(exit_code)


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