"""Base class for execution algorithms."""

from __future__ import annotations

import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

from ..models import Order, OrderSubmitRequest

logger = logging.getLogger(__name__)


@dataclass
class AlgoSlice:
    """A child order slice produced by an execution algorithm."""

    seq: int
    symbol: str
    side: str
    qty: float
    order_type: str = "market"
    limit_price: Optional[float] = None
    scheduled_time: float = 0.0
    status: str = "pending"
    order_id: str = ""
    fill_price: float = 0.0
    fill_qty: float = 0.0


@dataclass
class AlgoState:
    """Running state of an execution algorithm."""

    parent_order: Optional[OrderSubmitRequest] = None
    total_qty: float = 0.0
    filled_qty: float = 0.0
    slices: List[AlgoSlice] = field(default_factory=list)
    status: str = "pending"
    avg_fill_price: float = 0.0
    total_cost: float = 0.0

    @property
    def remaining_qty(self) -> float:
        return self.total_qty - self.filled_qty

    @property
    def is_complete(self) -> bool:
        return self.filled_qty >= self.total_qty or self.status in ("completed", "canceled")

    @property
    def fill_pct(self) -> float:
        return (self.filled_qty / self.total_qty * 100) if self.total_qty > 0 else 0

    def record_fill(self, slice_seq: int, fill_qty: float, fill_price: float) -> None:
        for s in self.slices:
            if s.seq == slice_seq:
                s.fill_qty = fill_qty
                s.fill_price = fill_price
                s.status = "filled"
                break
        self.filled_qty += fill_qty
        self.total_cost += fill_qty * fill_price
        self.avg_fill_price = self.total_cost / self.filled_qty if self.filled_qty > 0 else 0
        if self.filled_qty >= self.total_qty:
            self.status = "completed"


class ExecAlgorithm(ABC):
    """Base execution algorithm.

    Subclass and implement ``generate_slices`` to define how a parent
    order is split into child orders.
    """

    name: str = "base"

    @abstractmethod
    def generate_slices(
        self, req: OrderSubmitRequest, params: Dict[str, Any]
    ) -> List[AlgoSlice]:
        """Split the parent order into child slices."""
        ...

    def on_fill(self, state: AlgoState, slice_seq: int, fill_qty: float, fill_price: float) -> None:
        """Called when a child slice is filled."""
        state.record_fill(slice_seq, fill_qty, fill_price)

    def on_cancel(self, state: AlgoState) -> None:
        """Cancel all remaining unfilled slices."""
        for s in state.slices:
            if s.status == "pending":
                s.status = "canceled"
        state.status = "canceled"
