"""  Module for Job Shop Scheduling Problem (JSSP)."""
from collections import defaultdict
from typing import Literal
try:
    from qlauncher.problems.problem_formulations.jssp.qiskit_scheduler import get_jss_hamiltonian
except ModuleNotFoundError:
    pass
from qlauncher.base import Problem


class JSSP(Problem):
    """
    Class for Job Shop Scheduling Problem.

    This class represents Job Shop Scheduling Problem (JSSP) which is a combinatorial optimization problem that involves 
    scheduling a set of jobs on a set of machines. Each job consists of a sequence of operations that must be performed 
    on different machines. The objective is to find a schedule that minimizes the makespan, i.e., the total time required
    to complete all jobs. The class contains an instance of the problem, so it can be passed into QLauncher.


    Attributes:
        max_time (int): The maximum time for the scheduling problem.
        onehot (str): The one-hot encoding method to be used.
        optimization_problem (bool): Flag indicating whether the problem is an optimization problem or a decision problem.
        results (dict): Dictionary to store the results of the problem instance.

    """

    def __init__(self, max_time: int, instance: dict[str, list[tuple[str, int]]],
                 instance_name: str = 'unnamed', optimization_problem: bool = False, onehot: Literal['exact', 'quadratic'] = 'exact') -> None:
        super().__init__(instance=instance, instance_name=instance_name)
        self.max_time = max_time
        self.onehot = onehot
        self.optimization_problem = optimization_problem

        self.h_d, self.h_o, self.h_pos_by_label, self.h_label_by_pos = get_jss_hamiltonian(self.instance, max_time,
                                                                                           onehot)

        self.results = {'instance_name': instance_name,
                        'max_time': max_time,
                        'onehot': onehot,
                        'H_pos_by_label': self.h_pos_by_label,
                        'H_label_by_pos': self.h_label_by_pos}

        self.variant = 'optimization' if optimization_problem else 'decision'

    @property
    def setup(self) -> dict:
        return {
            'max_time': self.max_time,
            'onehot': self.onehot,
            'optimization_problem': self.optimization_problem,
            'instance_name': self.instance_name
        }

    def _get_path(self) -> str:
        return f'{self.name}@{self.instance_name}@{self.max_time}@{"optimization" if self.optimization_problem else "decision"}@{self.onehot}'

    @staticmethod
    def from_preset(instance_name: str, **kwargs) -> "JSSP":
        match instance_name:
            case 'default':
                max_time = 3
                instance = {"cupcakes": [("mixer", 2), ("oven", 1)],
                            "smoothie": [("mixer", 1)],
                            "lasagna": [("oven", 2)]}
            case _:
                raise ValueError(f"Instance {instance_name} does not exist choose instance_name from the following: ('toy')")

        return JSSP(max_time=max_time, instance=instance, instance_name=instance_name, **kwargs)

    @classmethod
    def from_file(cls, path: str, **kwargs) -> "JSSP":
        job_dict = defaultdict(list)
        with open(path, 'r', encoding='utf-8') as file_:
            file_.readline()
            for i, line in enumerate(file_):
                lint = list(map(int, line.split()))
                job_dict[i + 1] = [x for x in
                                   zip(lint[::2],  # machines
                                       lint[1::2]  # operation lengths
                                       )]
        return JSSP(instance=job_dict, **kwargs)
