from __future__ import annotations

import abc
import enum
from typing import Any, NamedTuple

from signalab.account import Account


class Signal(enum.Enum):
    BUY = "buy"
    SELL = "sell"
    HOLD = "hold"

    @staticmethod
    def from_int(value: int) -> Signal:
        if value == 1:
            return Signal.BUY
        elif value == -1:
            return Signal.SELL
        return Signal.HOLD


class Strategy(abc.ABC):
    def __init__(self, name: str, label: str | None = None):
        self.name = name
        self.label = label or (name.lower().replace(" ", "_"))
        self._account: Account | None = None
        self._signal_columns: list[str] | None = None
        self._signal_names: list[str] | None = None

    @property
    def account(self) -> Account:
        if self._account is None:
            raise ValueError("Account not set for strategy.")
        return self._account

    def set_account(self, account: Account) -> None:
        self._account = account

    def get_signals(self, current_market: NamedTuple) -> list[Signal]:
        if self._signal_columns is None:
            self._signal_columns = [
                col for col in current_market._fields if col.endswith("_signal")
            ]
            self._signal_names = [
                col[: -len("_signal")] for col in self._signal_columns
            ]

        assert self._signal_columns is not None and self._signal_names is not None
        return [
            Signal.from_int(getattr(current_market, col))
            for col in self._signal_columns
        ]

    def get_signals_dict(self, current_market: NamedTuple) -> dict[str, Signal]:
        if self._signal_columns is None:
            self._signal_columns = [
                col for col in current_market._fields if col.endswith("_signal")
            ]
            self._signal_names = [
                col[: -len("_signal")] for col in self._signal_columns
            ]

        assert self._signal_columns is not None and self._signal_names is not None

        return {
            name: Signal.from_int(getattr(current_market, col))
            for name, col in zip(self._signal_names, self._signal_columns, strict=True)
        }

    @abc.abstractmethod
    def perform(self, current_market: Any, accounts: set) -> None:
        raise NotImplementedError
