"""Screening engine: run all strategies across a list of tickers."""
from __future__ import annotations

from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Callable, Optional

import pandas as pd

from . import data as data_mod
from .strategies import STRATEGIES


def screen_one(ticker: str) -> Optional[dict]:
    sd = data_mod.fetch(ticker)
    if not sd.ok:
        return None
    row = {
        "Ticker": ticker,
        "Name": sd.name,
        "Sector": sd.sector,
        "Price": sd.price,
    }
    for name, evaluate in STRATEGIES.items():
        try:
            res = evaluate(sd)
            row[name] = round(res.score, 1)
        except Exception:  # noqa: BLE001
            row[name] = None
    scores = [row[n] for n in STRATEGIES if row.get(n) is not None]
    row["Composite"] = round(sum(scores) / len(scores), 1) if scores else None
    return row


def screen(
    tickers: list[str],
    max_workers: int = 8,
    progress: Optional[Callable[[int, int], None]] = None,
) -> pd.DataFrame:
    """Screen a list of tickers concurrently. Returns a scored DataFrame."""
    rows: list[dict] = []
    done = 0
    total = len(tickers)
    with ThreadPoolExecutor(max_workers=max_workers) as pool:
        futures = {pool.submit(screen_one, t): t for t in tickers}
        for fut in as_completed(futures):
            res = fut.result()
            if res:
                rows.append(res)
            done += 1
            if progress:
                progress(done, total)
    if not rows:
        return pd.DataFrame()
    df = pd.DataFrame(rows)
    cols = ["Ticker", "Name", "Sector", "Price",
            "Buffett", "Burry", "Roaring Kitty", "Simons", "Composite"]
    df = df[[c for c in cols if c in df.columns]]
    return df.sort_values("Composite", ascending=False, na_position="last").reset_index(drop=True)
