from __future__ import annotations

from concurrent.futures import ThreadPoolExecutor
from enum import Enum
from typing import Iterable, Optional, Protocol


class FactExecutionMode(str, Enum):
    SEQUENTIAL = "sequential"
    PARALLEL = "parallel"


class FactTaskHandler(Protocol):
    def handle(self, segment: str) -> Optional[str]:
        ...


class FactExecutionStrategy:
    def execute(
        self,
        segments: Iterable[str],
        handler: FactTaskHandler,
    ) -> list[Optional[str]]:
        return [handler.handle(segment) for segment in segments]


class SequentialFactExecutor(FactExecutionStrategy):
    def execute(
        self,
        segments: Iterable[str],
        handler: FactTaskHandler,
    ) -> list[Optional[str]]:
        return [handler.handle(segment) for segment in segments]


class ParallelFactExecutor(FactExecutionStrategy):
    def __init__(self, max_workers: int | None = None) -> None:
        self._max_workers = max_workers

    def execute(
        self,
        segments: Iterable[str],
        handler: FactTaskHandler,
    ) -> list[Optional[str]]:
        with ThreadPoolExecutor(max_workers=self._max_workers) as executor:
            futures = [executor.submit(handler.handle, segment) for segment in segments]
            return [future.result() for future in futures]
