"""Concurrent Position Analysis: Position clustering and correlation.

Are simultaneous positions correlated? Analyzes trade blotter to find
overlapping position groups and their sector distribution.

Usage:
  python -m dev.analysis.concurrent_position_analysis \
    --run-dir runs/run_20260313_xyz [--output-dir ./data/analysis]
"""
from __future__ import annotations

import argparse
import csv
import datetime as dt
from collections import Counter
from pathlib import Path
from typing import Any

import pyarrow.parquet as pq


def _load_blotter(run_dir: Path) -> list[dict[str, Any]]:
    """Load trade blotter from parquet."""
    blotter_path = run_dir / "trade_blotter.parquet"
    if not blotter_path.exists():
        # Try CSV fallback
        csv_path = run_dir / "trade_blotter.csv"
        if csv_path.exists():
            rows = []
            with open(csv_path) as f:
                reader = csv.DictReader(f)
                for row in reader:
                    rows.append(row)
            return rows
        raise FileNotFoundError(f"No trade blotter found in {run_dir}")

    table = pq.read_table(blotter_path)
    return table.to_pylist()


def _parse_date(val: Any) -> dt.date | None:
    """Parse a date value from various formats."""
    if val is None:
        return None
    if isinstance(val, dt.date):
        return val
    if isinstance(val, dt.datetime):
        return val.date()
    try:
        return dt.date.fromisoformat(str(val)[:10])
    except (ValueError, TypeError):
        return None


def analyze(trades: list[dict[str, Any]], output_dir: Path) -> None:
    output_dir.mkdir(parents=True, exist_ok=True)

    # Parse entry/exit dates
    positions: list[dict[str, Any]] = []
    for t in trades:
        entry = _parse_date(t.get("entry_date"))
        exit_ = _parse_date(t.get("exit_date"))
        if entry and exit_:
            positions.append({
                "trade_id": t.get("trade_id", ""),
                "symbol": t.get("symbol", ""),
                "sector": t.get("sector", "UNKNOWN"),
                "entry_date": entry,
                "exit_date": exit_,
            })

    if not positions:
        print("No valid trades found.")
        return

    # Build daily position timeline
    all_dates = set()
    for p in positions:
        d = p["entry_date"]
        while d <= p["exit_date"]:
            all_dates.add(d)
            d += dt.timedelta(days=1)

    daily_positions: dict[dt.date, list[dict[str, Any]]] = {}
    for d in sorted(all_dates):
        active = [p for p in positions if p["entry_date"] <= d <= p["exit_date"]]
        if active:
            daily_positions[d] = active

    # Compute concurrent position stats
    concurrent_counts = [len(v) for v in daily_positions.values()]
    max_concurrent = max(concurrent_counts) if concurrent_counts else 0
    avg_concurrent = sum(concurrent_counts) / len(concurrent_counts) if concurrent_counts else 0

    # Find overlap groups (days with >1 position)
    overlap_days = {d: ps for d, ps in daily_positions.items() if len(ps) > 1}

    # Sector distribution in overlap groups
    overlap_sector_counts: Counter[str] = Counter()
    overlap_symbol_counts: Counter[str] = Counter()
    for d, ps in overlap_days.items():
        for p in ps:
            overlap_sector_counts[p["sector"]] += 1
            overlap_symbol_counts[p["symbol"]] += 1

    # Peak overlap days
    peak_days = sorted(overlap_days.items(), key=lambda x: len(x[1]), reverse=True)[:10]

    # CSV: daily concurrent position counts
    csv_rows: list[dict[str, Any]] = []
    for d in sorted(daily_positions):
        ps = daily_positions[d]
        sectors = Counter(p["sector"] for p in ps)
        csv_rows.append({
            "date": str(d),
            "concurrent_positions": len(ps),
            "unique_sectors": len(sectors),
            "sectors": "|".join(f"{s}:{c}" for s, c in sectors.most_common()),
            "symbols": "|".join(p["symbol"] for p in ps),
        })

    csv_path = output_dir / "concurrent_position_analysis.csv"
    if csv_rows:
        fieldnames = list(csv_rows[0].keys())
        with open(csv_path, "w", newline="") as f:
            writer = csv.DictWriter(f, fieldnames=fieldnames)
            writer.writeheader()
            writer.writerows(csv_rows)

    # Console output
    print(f"\n{'='*70}")
    print(f"Concurrent Position Analysis — {len(positions)} trades")
    print(f"{'='*70}")
    print(f"\n  Total trading days with positions: {len(daily_positions)}")
    print(f"  Days with >1 concurrent position:  {len(overlap_days)}")
    print(f"  Max concurrent positions:          {max_concurrent}")
    print(f"  Avg concurrent positions:          {avg_concurrent:.1f}")

    if overlap_sector_counts:
        print(f"\n  Sector distribution in overlap periods:")
        for sector, count in overlap_sector_counts.most_common():
            print(f"    {sector:<20} {count:>5} position-days")

    if peak_days:
        print(f"\n  Top 10 peak overlap days:")
        for d, ps in peak_days:
            symbols = ", ".join(p["symbol"] for p in ps)
            sectors = set(p["sector"] for p in ps)
            print(f"    {d}  {len(ps)} positions  sectors={len(sectors)}  [{symbols}]")

    print(f"\nCSV written to: {csv_path}")


def main() -> None:
    parser = argparse.ArgumentParser(description="Concurrent Position Analysis")
    parser.add_argument("--run-dir", required=True, help="Path to backtest run directory")
    parser.add_argument("--output-dir", default="./data/analysis", help="Output directory")
    args = parser.parse_args()

    trades = _load_blotter(Path(args.run_dir))
    if not trades:
        print("No trades found in blotter.")
        return

    analyze(trades, Path(args.output_dir))


if __name__ == "__main__":
    main()
