"""
Scaffold new strategies and indicators from templates.
"""

import re
from pathlib import Path

import typer

_DEFAULT_STRATEGIES_DIR = Path.home() / ".sixtysix" / "data" / "strategies"
_DEFAULT_INDICATORS_DIR = Path.home() / ".sixtysix" / "data" / "indicators"


# ---------------------------------------------------------------------------
# Name helpers
# ---------------------------------------------------------------------------

def _to_snake(name: str) -> str:
    """'MyStrategy' or 'My Strategy' -> 'my_strategy'."""
    name = name.strip().replace(" ", "_").replace("-", "_")
    name = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", name)
    name = re.sub(r"([a-z\d])([A-Z])", r"\1_\2", name)
    return name.lower()


def _to_pascal(name: str) -> str:
    """'my_strategy' or 'MyStrategy' or 'My Strategy' -> 'MyStrategy'."""
    return "".join(word.capitalize() for word in _to_snake(name).split("_"))


def _to_display(name: str) -> str:
    """'my_strategy' or 'MyStrategy' -> 'My Strategy'."""
    return " ".join(word.capitalize() for word in _to_snake(name).split("_"))


# ---------------------------------------------------------------------------
# Templates
# ---------------------------------------------------------------------------

_STRATEGY_TEMPLATE = '''\
from sixtysix import strategy, computed, param, Line

@strategy(
    name='{name}',
    display_name='{display_name}',
    category='Trend Following',
)
class {class_name}:
    fast = param.number(default=20, min=5, max=100, label='Fast Period')
    slow = param.number(default=50, min=10, max=200, label='Slow Period')

    @computed
    def fast_sma(self, df):
        return df['close'].rolling(self.fast).mean()

    @computed
    def slow_sma(self, df):
        return df['close'].rolling(self.slow).mean()

    def plot(self, df):
        return [
            Line(y=self.fast_sma, color='#3b82f6', legend='Fast SMA'),
            Line(y=self.slow_sma, color='#f97316', legend='Slow SMA'),
        ]

    def on_bar(self, df):
        if self.position.is_flat and self.ta.crossover(self.fast_sma, self.slow_sma):
            return self.buy(reason='Golden cross')
        if self.position.is_long and self.ta.crossunder(self.fast_sma, self.slow_sma):
            return self.close(reason='Death cross')
'''

_INDICATOR_TEMPLATE = '''\
from sixtysix import indicator, param, Line

@indicator(
    name='{name}',
    display_name='{display_name}',
    type='overlay',      # 'overlay' (on chart) or 'oscillator' (separate panel)
    category='Custom',
)
class {class_name}:
    period = param.number(default=20, min=2, max=500, label='Period')
    color  = param.color(default='#3b82f6', label='Color')

    def plot(self, df):
        values = df['close'].rolling(self.period).mean()
        return [
            Line(y=values, color=self.color, line_width=2),
        ]
'''


# ---------------------------------------------------------------------------
# Commands
# ---------------------------------------------------------------------------

def create_strategy(name: str, output_dir: Path | None) -> None:
    snake = _to_snake(name)
    pascal = _to_pascal(name)
    display = _to_display(name)

    dest_dir = output_dir or _DEFAULT_STRATEGIES_DIR
    dest_dir.mkdir(parents=True, exist_ok=True)

    out = dest_dir / f"{snake}.py"
    if out.exists():
        typer.echo(typer.style(f"File already exists: {out}", fg=typer.colors.RED), err=True)
        raise typer.Exit(1)

    out.write_text(_STRATEGY_TEMPLATE.format(
        name=snake,
        display_name=display,
        class_name=pascal,
    ))

    typer.echo()
    typer.echo(typer.style(f"✓ Strategy created: {out}", fg=typer.colors.GREEN))
    typer.echo()
    typer.echo(f"  name:         {snake}")
    typer.echo(f"  class:        {pascal}")
    typer.echo(f"  display_name: {display}")
    typer.echo()
    typer.echo("The file is automatically picked up by the backend.")
    typer.echo("Restart the container after saving changes:")
    typer.echo()
    typer.echo("  sixtysix restart")
    typer.echo()
    typer.echo("Then run a backtest:")
    typer.echo()
    typer.echo(f"  sixtysix backtest run -s {snake} -S AAPL -t 1h")


def create_indicator(name: str, output_dir: Path | None) -> None:
    snake = _to_snake(name)
    pascal = _to_pascal(name)
    display = _to_display(name)

    dest_dir = output_dir or _DEFAULT_INDICATORS_DIR
    dest_dir.mkdir(parents=True, exist_ok=True)

    out = dest_dir / f"{snake}.py"
    if out.exists():
        typer.echo(typer.style(f"File already exists: {out}", fg=typer.colors.RED), err=True)
        raise typer.Exit(1)

    out.write_text(_INDICATOR_TEMPLATE.format(
        name=snake,
        display_name=display,
        class_name=pascal,
    ))

    typer.echo()
    typer.echo(typer.style(f"✓ Indicator created: {out}", fg=typer.colors.GREEN))
    typer.echo()
    typer.echo(f"  name:         {snake}")
    typer.echo(f"  class:        {pascal}")
    typer.echo(f"  display_name: {display}")
    typer.echo()
    typer.echo("The file is automatically picked up by the backend.")
    typer.echo("Restart the container after saving changes:")
    typer.echo()
    typer.echo("  sixtysix restart")
