import json
import logging
from copy import deepcopy
from pathlib import Path

from juice_scheduler.common.date_utils import filename_timestamp
from juice_scheduler.common.files import FileResources
from juice_scheduler.model.configuration import Configuration
from juice_scheduler.model.events import EventsHandler
from juice_scheduler.model.interval_list import DateStrInterval
from juice_scheduler.model.mission_phase import MissionPhaseHandler
from juice_scheduler.model.prime import PrimeHandler
from juice_scheduler.model.resources import ResourceHandler
from juice_scheduler.model.segmentation import Segmentation
from juice_scheduler.schedule.calibration import adjust_calibration_windows, select_calibrations
from juice_scheduler.schedule.minimum_duration import filter_minimum_duration
from juice_scheduler.schedule.opnav_rules import RuleApplicator

logger = logging.getLogger(__name__)

class Runner:

    @staticmethod
    def from_configuration(configuration: Configuration):

        conf_repo_path = configuration.get_conf_repo_path()
        crema_id = configuration.get_crema_id()
        prime_mapping_file_path = configuration.get_prime_mapping_path()

        file_resources = FileResources(conf_repo_path, crema_id)
        phase_handler = MissionPhaseHandler.from_file(file_resources.get_mission_phases_file())
        event_handler = EventsHandler.from_file(file_resources.get_event_file())
        prime_handler = PrimeHandler.from_file(prime_mapping_file_path)
        resource_handler = ResourceHandler.from_sht_file(configuration.get_segmentation_file())
        segmentation = Segmentation.from_sht_file(configuration.get_segmentation_file())

        return Runner(phase_handler, event_handler, prime_handler, resource_handler, segmentation)


    def __init__(self,
                 phase_handler: MissionPhaseHandler,
                 event_handler: EventsHandler,
                 prime_handler: PrimeHandler,
                 resource_handler: ResourceHandler,
                 segmentation: Segmentation):

        self.phase_handler = phase_handler
        self.event_handler = event_handler
        self.prime_handler = prime_handler
        self.resource_handler = resource_handler
        self.segmentation = segmentation


        # Store the opportunity/prime mapping
        self.prime_handler.load_segmentation(self.segmentation, self.event_handler)

    def run(self, runner_configuration):

        # Retrieve specific configuration parameters
        priority_list = runner_configuration.get("priority_list")
        phase_calib_selector = runner_configuration.get("phase_calib_selector")
        dry_run = runner_configuration.get("dry_run", False)
        dump_opnav = runner_configuration.get("dump_opnav", False)


        # Prepare the calibration
        logger.info("Preparing the calibration")
        original_calibration = self.segmentation.select_segment("JMAG_CALROLL")[0]
        calibration_selected = select_calibrations(self.phase_handler, phase_calib_selector, original_calibration)
        calibration_adjusted = adjust_calibration_windows(self.event_handler, calibration_selected)
        self.segmentation.base_map["JMAG_CALROLL"] = calibration_adjusted

        # Basic priorisation
        result = Segmentation("result")
        accum = DateStrInterval()
        logger.info("Starting basic priorisation")

        for pattern in priority_list:
            step_accum = DateStrInterval()
            segment_definitions = self.segmentation.select_segment(pattern)
            if len(segment_definitions) == 0:
                logger.info("Nothing for pattern %s", pattern)

            for item in segment_definitions:
                logger.debug("Processing %s", item.name)
                new = deepcopy(item)
                # We removed the previous
                new.substract(accum)
                # And filter by minimum duration the result
                new = filter_minimum_duration(new)
                result.base_map[item.name] = new
                step_accum = step_accum.union(new.instances)

            accum = accum.union(step_accum)


        rule_applicator = RuleApplicator(
            runner_configuration.get("op_nav_insertion_rules"),
            self.segmentation)
        logger.info("Applying OPNAV rules")
        solved = rule_applicator.apply()

        timestamp = filename_timestamp()
        timestamp = "TEST"

        if dump_opnav:
            opnav_result = {"trajectory": "Crema_5_0"}
            opnav_result["segments"] =  [ x[1][1].to_json("TRANSIT_ET", "GENERIC", x[1][2]) for x in solved.items()]
            output = Path(f"opnav_{timestamp}.json")
            with output.open("w") as f:
                json.dump(opnav_result, f, indent=2)


        # Make the expansion of primes and resources
        result.expand_with_primes(self.prime_handler, self.resource_handler)

        # Check if it is a dry run
        if not dry_run:
            # Dump of the results
            output_filename = f"sched_{timestamp}"

            # Write the result segmentation
            self.json_path = Path(f"{output_filename}.json")
            result.dump_sht_file(self.json_path)
            logger.info("JSON written to %s", self.json_path)


        logger.debug("%s", result.summary())
        return result

