from .errors import ResultUnwrapError, KoruspyError
from typing import Generic, TypeVar

T = TypeVar("T")
E = TypeVar("E")

class Okay(Generic[T]):
    __match_args__ = ("value",)
    __slots__ = ("value",)

    def __init__(self, value: T):
        self.value = value

    def unwrap(self):
        return self.value

    def flat_map(self, func):
        return func(self.value)

    def unwrap_or(self, default):
        return self.value

    def unwrap_or_else(self, func):
        return self.value

    def fold(self, on_okay, on_err):
        return on_okay(self.value)

    def map(self, func):
        try:
            return Okay(func(self.value))
        except Exception as e:
            return Err(e)

    def __repr__(self):
        return f"\033[92mOkay({self.value})\033[0m"

    def is_okay(self):
        return True

    def is_err(self):
        return False
    
    def unwrap_err(self):
        raise ResultUnwrapError(f"tried unwrap 'Okay({self.value})' using 'unwrap_err()' use 'unwrap()' when the instance is 'Okay'") 

    def expect(self, err):
        return self.value


class Err(Generic[E]):
    __match_args__ = ("error",)

    def __init__(self, error: E):
        self.error = error

    def fold(self, on_okay, on_err):
        return on_err(self.error)

    def expect(self, err) -> E:
        raise KoruspyError(err)

    def unwrap(self) -> E:
        raise ResultUnwrapError(f"tried to access Err: {self.error}")

    def unwrap_or(self, default) -> T:
        return default

    def unwrap_or_else(self, func):
        return func(self.error)

    def unwrap_err(self) -> E:
        return self.error

    def map(self, func):
        return self

    def flat_map(self, func):
        return self

    def __repr__(self):
        return f"\033[31mErr({self.error})\033[0m"

    def is_okay(self):
        return False

    def is_err(self):
        return True