"""
TaskProgrammingBinFile - Program binary file via uart loader protocol (Operation-based)
"""

from __future__ import annotations
from pathlib import Path
from datetime import datetime
import time
from logging import getLogger
from pydantic import Field

from Task.TaskContextBase.task_program_context_base import (
    TaskProgramContextBase
)
from Task.TaskContextBase.task_reset_support_context_base import (
    TaskResetSupportContextBase
)
from csv_report.csv_report_helper import CsvReportHelper
from csv_report_struct.program_full_spend_time import ProgramFullSpendTimeReport
from Task.TaskOperation.reset_operation import (
    ResetOperation, UartLoaderBootSource
)
from Task.TaskOperation.xmodem_operation import (
    XmodemOperation
)
from Task.TaskOperation.uart_loader_operation import UartLoaderOperation
from app_context import AppContext

logger = getLogger(__name__)

class TaskProgramBinFileContext(TaskResetSupportContextBase,
                                TaskProgramContextBase):
    bin_path: str = Field(
        description="Path to binary file to program via uart loader",
    )
    target_address: int = Field(
        description="Target storage address for programming"
    )
    erase_size: int = Field(
        default=0,
        description="Size of data to erase before programming"
    )
    erase_timeout: float = Field(
        default=10.0,
        description="Timeout for erase operation"
    )
    def to_program_binary_file_context(self) \
        -> UartLoaderOperation.ProgramBinFileContext:

        return UartLoaderOperation.ProgramBinFileContext(
            bin_file_path=self.bin_path,
            erase_size=self.erase_size,
            erase_timeout=self.erase_timeout,
            **self.to_program_binary_context_base(
                self.target_address).model_dump()
        )


class TaskProgramBinFile:
    """Task class for programming binary files via uart loader protocol (Operation-based)"""
    
    def __init__(self, ctx: TaskProgramBinFileContext, app_ctx: AppContext):
        """Initialize TaskProgrammingBinFile"""
        self.ctx = ctx
        self.start_timestamp = datetime.now()
        
        # Initialize full report
        self.full_report = ProgramFullSpendTimeReport()
        self.full_report.cmd_start_timestamp = self.start_timestamp
        self.full_report.serial = self.ctx.serial
        self.full_report.xmodem_ram_file = self.ctx.ram_image
        self.full_report.xmodem_ram_size =\
            Path(self.ctx.ram_image).stat().st_size
        self.full_report.uart_loader_file = self.ctx.bin_path
        self.full_report.uart_loader_bin_size = \
            Path(self.ctx.bin_path).stat().st_size
        self.full_report.full_programming_time_ms = 0
        self.full_report.full_verification_time_ms = 0


        self.reset_operation = ResetOperation(
            ctx.to_reset_operation_init_context(app_ctx),
        )

        self.xmodem_operation = XmodemOperation(
            ctx=ctx.to_xmodem_init_context(),
            start_timestamp=self.start_timestamp,
            full_report=self.full_report
        )

        self.uart_loader_operation = UartLoaderOperation(
            ctx=ctx.to_uart_loader_init_context(),
            start_timestamp=self.start_timestamp,
            full_report=self.full_report
        )

    def run(self):
        """Main execution function for TaskProgrammingBinFile task"""
        logger.info(f"=== TaskProgrammingBinFile Task Started (Mode:"
                      f" {self.ctx.uart_loader_mode.value}) ===")
        is_test_success = False
        try:
            self._run_body()
            logger.info("=== TaskEnterXmodem Task Completed ===")
            is_test_success = True
        finally:
            # Clean up
            try:
                self.uart_loader_operation.close_uart_loader_serial()
            except:
                pass
            try:
                self.reset_operation.module_stop(is_test_success)
            except:
                pass

    def _run_body(self):
        """Core task execution logic without error handling"""
        # Execute mode-specific initialization phase
        self.reset_operation.execute_reset_phase()

        if self.ctx.uart_loader_mode == UartLoaderBootSource.RAM:
            self.xmodem_operation.execute_ram_mode_phase()

        # Common phases for both modes
        self.uart_loader_operation.init_uart_loader_serial()

        wait_boot_event_stime = time.time()
        self.uart_loader_operation.wait_for_boot_event()  # Will raise exception if fails
        wait_boot_event_spend_time = \
            int((time.time() - wait_boot_event_stime) * 1000)

        init_stime = time.time()
        self.uart_loader_operation.change_baudrate(
            self.ctx.to_change_baudrate_context())
        self.uart_loader_operation.get_prog_info()
        init_spend_time = int((time.time()- init_stime) * 1000)


        # Program the binary file
        self.uart_loader_operation.program_binary_file(
            self.ctx.to_program_binary_file_context()
        )

        self.full_report.uart_loader_init_ms = init_spend_time
        self.full_report.wait_uart_loader_boot_event_time_ms =\
            wait_boot_event_spend_time
        CsvReportHelper.append(self.full_report)
