# radar_data_reader/core/segment_processor.py

"""
Contains the business logic for the Segment Processor feature.
This class handles scanning for exported segments and processing their .out
files in a batch operation to extract structured data.
"""
import threading
import queue
import re
import multiprocessing as mp
import json
import csv
from pathlib import Path
from typing import Dict, Any, List

try:
    import pandas as pd
except ImportError:
    pd = None

from ..utils import logger
from .file_reader import run_worker_process
from ..core.export_profiles import ExportProfile

log = logger.get_logger(__name__)


def _get_value_from_path(batch: Any, path: str) -> Any:
    try:
        if path == "batch_id":
            return getattr(batch, "batch_id", "N/A")
        parts = re.split(r"\.|\[", path)
        current_obj = batch
        for part in parts:
            if current_obj is None:
                return "N/A"
            if part.endswith("]"):
                index_str = part[:-1]
                if not index_str.isdigit():
                    return "N/A"
                try:
                    current_obj = current_obj[int(index_str)]
                except (IndexError, TypeError):
                    return "N/A"
            else:
                current_obj = getattr(current_obj, part, None)
        return current_obj if current_obj is not None else "N/A"
    except Exception:
        return "N/A"


def _convert_ctypes_for_json(obj: Any) -> Any:
    if isinstance(obj, (int, float, str, bool)) or obj is None:
        return obj
    if hasattr(obj, "_length_"):
        return [_convert_ctypes_for_json(item) for item in obj]
    if hasattr(obj, "_fields_"):
        return {
            field: _convert_ctypes_for_json(getattr(obj, field))
            for field, _ in obj._fields_
        }
    if isinstance(obj, bytes):
        return obj.hex()
    return obj


class SegmentProcessor:
    """Manages the batch processing of exported flight segments."""

    def __init__(
        self,
        config: Dict[str, Any],
        result_queue: queue.Queue,
        command_queue: queue.Queue,
    ):
        self.config = config
        self.result_queue = result_queue
        self.command_queue = command_queue
        self._worker_thread: threading.Thread = None
        self._stop_event = threading.Event()

    def start(self):
        """Starts the batch processing in a background thread."""
        if self._worker_thread and self._worker_thread.is_alive():
            log.warning("Segment processing is already running.")
            return

        self._stop_event.clear()
        self._worker_thread = threading.Thread(
            target=self._run_batch_processing, daemon=True
        )
        self._worker_thread.start()

    def _run_batch_processing(self):
        """The main loop for processing segments, executed in a thread."""
        segments_to_process = self.config.get("segments_to_process", [])
        output_dir = Path(self.config.get("output_dir"))
        create_folders = self.config.get("create_separate_folders", True)

        profiles = self.config.get("profiles", [])
        csv_profile = next(
            (p for p in profiles if p.name == self.config.get("csv_profile_name")), None
        )
        json_profile = next(
            (p for p in profiles if p.name == self.config.get("json_profile_name")),
            None,
        )

        log.info(f"Starting batch processing for {len(segments_to_process)} segments.")

        for i, segment_info in enumerate(segments_to_process):
            if self._stop_event.is_set():
                log.info("Stop command received, halting batch processing.")
                break

            segment_name = segment_info.get("folder_name")
            out_file_path = Path(segment_info.get("out_file_path"))
            log.info(
                f"Processing segment {i+1}/{len(segments_to_process)}: {segment_name}"
            )

            self.result_queue.put(
                {
                    "type": "batch_progress",
                    "current": i + 1,
                    "total": len(segments_to_process),
                    "segment_name": segment_name,
                }
            )

            current_output_dir = (
                output_dir / segment_name if create_folders else output_dir
            )
            current_output_dir.mkdir(parents=True, exist_ok=True)

            self._process_single_segment(
                out_file_path,
                current_output_dir,
                segment_name,
                csv_profile,
                json_profile,
            )

        log.info("Batch processing finished.")
        self.result_queue.put(
            {"type": "complete", "message": "Segment batch processing complete."}
        )

    def _process_single_segment(
        self,
        in_path: Path,
        out_dir: Path,
        base_name: str,
        csv_p: ExportProfile,
        json_p: ExportProfile,
    ):
        worker_cmd_q = mp.Queue()
        worker_res_q = mp.Queue()

        active_profile = csv_p if self.config.get("generate_csv") else json_p
        if not active_profile:
            return

        worker_args = (in_path, worker_cmd_q, worker_res_q, active_profile)
        process = mp.Process(target=run_worker_process, args=worker_args, daemon=True)
        process.start()

        csv_writer, json_data, csv_fh = None, [], None

        try:
            if self.config.get("generate_csv") and csv_p:
                csv_filename = f"{base_name}_{csv_p.name}.csv"
                csv_path = out_dir / csv_filename
                csv_fh = open(csv_path, "w", encoding="utf-8", newline="")
                delimiter = "\t" if self.config.get("csv_use_tab") else ","
                csv_writer = csv.writer(csv_fh, delimiter=delimiter)
                header = [
                    (
                        field.data_path
                        if self.config.get("use_full_path_headers")
                        else field.column_name
                    )
                    for field in csv_p.fields
                ]
                csv_writer.writerow(header)

            if self.config.get("generate_json") and json_p:
                json_data = []  # Assicurati che sia vuota per ogni segmento

            while True:
                msg = worker_res_q.get()
                if msg["type"] == "data_batch":
                    batch = msg["data"]
                    if csv_writer and csv_p:
                        row = [
                            _get_value_from_path(batch, field.data_path)
                            for field in csv_p.fields
                        ]
                        csv_writer.writerow(row)
                    if json_data is not None and json_p:
                        row_dict = {}
                        for field in json_p.fields:
                            key = (
                                field.data_path
                                if self.config.get("use_full_path_headers")
                                else field.column_name
                            )
                            raw_value = _get_value_from_path(batch, field.data_path)
                            row_dict[key] = _convert_ctypes_for_json(raw_value)
                        json_data.append(row_dict)
                elif msg["type"] == "complete":
                    break
                elif msg["type"] == "error":
                    log.error(f"Worker for {in_path.name} failed: {msg['message']}")
                    break
        finally:
            if csv_fh:
                csv_fh.close()
            if self.config.get("generate_json") and json_p and json_data:
                json_filename = f"{base_name}_{json_p.name}.json"
                json_path = out_dir / json_filename
                with open(json_path, "w", encoding="utf-8") as f:
                    json.dump(json_data, f, indent=4)
            process.join(timeout=2)
            if process.is_alive():
                process.terminate()

    @staticmethod
    def scan_for_segments(
        flight_path: Path, flight_summary_df: pd.DataFrame, naming_template: str
    ) -> List[Dict[str, Any]]:
        if flight_summary_df is None or flight_summary_df.empty:
            return []

        verified_segments = []
        for _, row in flight_summary_df.iterrows():
            placeholders = {
                "{Segment}": str(row.get("Segment (Mode | Scale | WF)", "")),
                "{StartBatch}": str(row.get("Start Batch", "")),
                "{EndBatch}": str(row.get("End Batch", "")),
                "{BatchCount}": str(row.get("Batch Count", "")),
            }
            folder_name = naming_template
            for key, value in placeholders.items():
                folder_name = folder_name.replace(key, value)
            safe_folder_name = re.sub(r'[\\/*?:"<>|]', "-", folder_name)

            segment_path = flight_path / safe_folder_name
            out_file_path = segment_path / f"{safe_folder_name}.out"

            segment_info = row.to_dict()
            segment_info["folder_name"] = safe_folder_name

            if (
                segment_path.is_dir()
                and out_file_path.is_file()
                and out_file_path.stat().st_size > 0
            ):
                segment_info["is_exported_and_valid"] = True
                segment_info["out_file_path"] = out_file_path
            else:
                segment_info["is_exported_and_valid"] = False
                segment_info["out_file_path"] = None

            verified_segments.append(segment_info)

        return verified_segments
