"""Demand forecast and reorder policy — synthetic portfolio case study."""

from math import sqrt
from pathlib import Path

import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_percentage_error


DATA = Path("public/project-assets/data/inventory_demand.csv")
OUT = Path("public/project-assets/data/reorder_policy.csv")
SERVICE_FACTOR = 1.65  # approximately 95% one-sided cycle service level


def prepare_features(frame: pd.DataFrame) -> pd.DataFrame:
    enriched = frame.sort_values(["sku", "date"]).copy()
    enriched["day_index"] = enriched.groupby("sku").cumcount()
    enriched["day_of_week"] = enriched["date"].dt.dayofweek
    enriched["month"] = enriched["date"].dt.month
    return enriched


def main() -> None:
    demand = pd.read_csv(DATA, parse_dates=["date"])
    required = {
        "date",
        "sku",
        "units_sold",
        "lead_time_days",
        "unit_holding_cost_gbp",
        "promotion_flag",
    }
    if missing := required.difference(demand.columns):
        raise ValueError(f"Missing fields: {sorted(missing)}")

    demand = prepare_features(demand)
    features = ["day_index", "day_of_week", "month", "promotion_flag"]
    policies: list[dict] = []

    for sku, history in demand.groupby("sku", sort=True):
        history = history.sort_values("date")
        train, holdout = history.iloc[:-60], history.iloc[-60:]
        model = LinearRegression().fit(train[features], train["units_sold"])
        prediction = np.clip(model.predict(holdout[features]), 1, None)
        mape = mean_absolute_percentage_error(
            holdout["units_sold"].clip(lower=1), prediction
        )

        recent = history.tail(90)["units_sold"]
        lead_time = int(history["lead_time_days"].iloc[-1])
        expected_lead_demand = recent.mean() * lead_time
        safety_stock = SERVICE_FACTOR * recent.std(ddof=1) * sqrt(lead_time)
        reorder_point = round(expected_lead_demand + safety_stock)

        policies.append(
            {
                "sku": sku,
                "holdout_mape_pct": round(mape * 100, 1),
                "average_daily_demand": round(recent.mean(), 1),
                "lead_time_days": lead_time,
                "safety_stock_units": round(safety_stock),
                "reorder_point_units": reorder_point,
                "estimated_holding_cost_gbp": round(
                    safety_stock * history["unit_holding_cost_gbp"].iloc[-1], 2
                ),
            }
        )

    policy = pd.DataFrame(policies).sort_values("reorder_point_units", ascending=False)
    policy.to_csv(OUT, index=False)
    print(policy.to_string(index=False))
    print(f"\nMean holdout MAPE: {policy['holdout_mape_pct'].mean():.1f}%")
    print(f"Policy written to {OUT}")


if __name__ == "__main__":
    main()

