from typing import Dict, Iterator, Iterable, Sequence, Tuple, Union

from django.forms import Form


class Workflow:
    def __init__(self, name: str, stages: Sequence['Stage']) -> None:
        self.name = name
        self.stages = stages
        self.mapping = self.build_mapping(stages)

    def build_mapping(self, stages: Sequence['Stage']) -> Dict[str, Tuple[int, int]]:
        mapping: Dict[str, Tuple[int, int]] = {}
        for i, stage in enumerate(stages):
            for j, phase in enumerate(stage):
                while str(phase) in mapping:
                    phase.occurance += 1
                mapping[str(phase)] = (i, j)
        return mapping

    def current_index(self, phase: Union['Phase', str, None]):
        if isinstance(phase, Phase):
            phase = str(phase)
        try:
            return self.mapping[phase]
        except KeyError:
            return 0, -1

    def next(self, current_phase: Union['Phase', str]=None) -> Union['Phase', None]:
        stage_idx, phase_idx = self.current_index(current_phase)
        try:
            return self.stages[stage_idx].phases[phase_idx + 1]
        except IndexError:
            try:
                return self.stages[stage_idx + 1].phases[0]
            except IndexError:
                return None


class Stage(Iterable['Phase']):
    def __init__(self, name: str, form: Form, phases: Sequence['Phase'],
                 current_phase: Union['Phase', None]=None) -> None:
        self.name = name
        self.form = form
        for phase in phases:
            phase.stage = self
        self.phases = phases

    def __iter__(self) -> Iterator['Phase']:
        yield from self.phases

    def __str__(self):
        return self.name


class Phase:
    def __init__(self, name: str) -> None:
        self.name = name
        self.stage: Union['Stage', None] = None
        self.occurance = 0

    def __str__(self):
        return '__'.join([self.stage.name, self.name, str(self.occurance)])


# --- OTF Workflow ---

review = Phase('Under Review')

response = Phase('Ready to Respond')

rejected = Phase('Rejected')

accepted = Phase('Accepted')

progress = Phase('Progress')

standard_stage = Stage('Standard', Form(), [review, response, review, response, accepted, rejected])

first_stage = Stage('Standard', Form(), [review, response, progress, rejected])

single_stage = Workflow('Single Stage', [standard_stage])

two_stage = Workflow('Two Stage', [first_stage, standard_stage])