"""
Chart Generation Functions
"""
import os
from datetime import datetime
from zoneinfo import ZoneInfo

import pandas as pd
import matplotlib
matplotlib.use("Agg")  # biar bisa jalan tanpa display/GUI (server/headless)
import matplotlib.lines as mlines
import mplfinance as mpf


def generate_chart(ticker: str, df: pd.DataFrame, result: dict, trend_sma_period: int, config: dict = None) -> str | None:
    """
    Bikin chart candlestick PNG (pakai mplfinance) + panel volume,
    dengan overlay SMA trend, BB lower, Fibonacci levels, dan anotasi titik sinyal
    (RSI oversold, MACD crossover) - mirip gaya analisa manual TradingView.

    Args:
        ticker: Ticker symbol (tanpa .JK suffix)
        df: DataFrame dengan OHLCV data dan indicators
        result: Analysis result dict
        trend_sma_period: Period untuk SMA trend
        config: Configuration dict

    Returns:
        Path ke generated chart file atau None jika gagal
    """
    if config is None:
        config = {}

    # Extract configuration values
    charts_dir = config.get('CHARTS_DIR', 'charts')
    chart_lookback_candles = config.get('CHART_LOOKBACK_CANDLES', 90)
    rsi_threshold = config.get('RSI_THRESHOLD', 30)
    stoch_oversold = config.get('STOCH_OVERSOLD', 20)
    ma_fast_period = config.get('MA_FAST_PERIOD', 20)
    ma_slow_period = config.get('MA_SLOW_PERIOD', 50)
    max_possible_score = config.get('MAX_POSSIBLE_SCORE', 13)
    market_tz = config.get('MARKET_TZ', 'Asia/Jakarta')

    # Fibonacci configuration
    fibonacci_colors = config.get('FIBONACCI_COLORS', ["#ff1744", "#ff9800", "#ffeb3b", "#9e9e9e", "#ffeb3b", "#ff9800", "#ff1744"])

    try:
        os.makedirs(charts_dir, exist_ok=True)
        plot_df = df.tail(chart_lookback_candles).copy()

        # Marker: cuma ada nilai di titik sinyal, NaN di titik lain (biar mplfinance
        # cuma gambar scatter dot di titik itu saja)
        oversold_marker = plot_df["Close"].where(plot_df["rsi"] < rsi_threshold)
        macd_cross_mask = (plot_df["macd"].shift(1) <= plot_df["macd_signal"].shift(1)) & \
                           (plot_df["macd"] > plot_df["macd_signal"])
        macd_cross_marker = plot_df["Close"].where(macd_cross_mask)
        ma_cross_mask = (plot_df["ma_fast"].shift(1) <= plot_df["ma_slow"].shift(1)) & \
                        (plot_df["ma_fast"] > plot_df["ma_slow"])
        ma_cross_marker = plot_df["Close"].where(ma_cross_mask)

        addplots = [
            mpf.make_addplot(plot_df["sma_trend"], color="#888888", linestyle="--", width=1.2),
            mpf.make_addplot(plot_df["bb_lower"], color="#d62728", linestyle=":", width=0.8),
            mpf.make_addplot(plot_df["ma_fast"], color="#9467bd", linestyle="-", width=1.0),
            mpf.make_addplot(plot_df["ma_slow"], color="#8c564b", linestyle="-", width=3.0),
            mpf.make_addplot(plot_df["vol_sma20"], panel=1, color="#555555", width=1),
            mpf.make_addplot(plot_df["stoch_k"], panel=2, color="#1f77b4", width=1.1, ylabel="StochRSI"),
            mpf.make_addplot(plot_df["stoch_d"], panel=2, color="#ff7f0e", width=1.0),
        ]
        has_oversold = oversold_marker.notna().any()
        has_macd_cross = macd_cross_marker.notna().any()
        has_ma_cross = ma_cross_marker.notna().any()
        if has_oversold:
            addplots.append(mpf.make_addplot(
                oversold_marker, type="scatter", markersize=70, marker="v", color="orange"))
        if has_macd_cross:
            addplots.append(mpf.make_addplot(
                macd_cross_marker, type="scatter", markersize=90, marker="^", color="blue"))
        if has_ma_cross:
            addplots.append(mpf.make_addplot(
                ma_cross_marker, type="scatter", markersize=90, marker="*", color="gold"))

        # Dark mode style dengan background hitam dan warna candle default
        style = mpf.make_mpf_style(
            base_mpf_style="nightclouds",  # Gunakan dark theme sebagai base
            rc={
                "font.size": 8,
                "axes.facecolor": "#1a1a1a",  # Background utama hitam
                "figure.facecolor": "#0d0d0d",  # Figure background hitam
                "axes.edgecolor": "#333333",   # Border abu-abu gelap
                "text.color": "#cccccc",        # Text abu-abu terang
                "ytick.color": "#cccccc",
                "xtick.color": "#cccccc",
                "grid.color": "#333333",        # Grid abu-abu gelap
                "grid.linestyle": "--",
                "grid.linewidth": 0.5,
            },
            marketcolors={
                "candle": {"up": "g", "down": "r"},      # Hijau bullish, Merah bearish
                "edge": {"up": "g", "down": "r"},        # Border candle sesuai warna
                "wick": {"up": "g", "down": "r"},        # Wick/shadow sesuai warna
                "ohlc": {"up": "g", "down": "r"},        # OHLC bar sesuai warna
                "volume": {"up": "g", "down": "r"},      # Volume bar sesuai warna
                "vcedge": {"up": "g", "down": "r"},      # Volume bar edge sesuai warna
                "vcdopcod": False,                        # Volume colors follow candle colors
                "alpha": 0.9,                            # Transparency
            }
        )
        title_reasons = ", ".join(result["reasons"])
        ff_text = f"{result['free_float_pct']}%" if result.get("free_float_pct") is not None else "N/A"
        value_b = result["avg_value_traded"] / 1_000_000_000
        info_line = f"Free Float: {ff_text} | Avg Value 20D: Rp {value_b:.2f} Miliar"

        fig, axes = mpf.plot(
            plot_df[["Open", "High", "Low", "Close", "Volume"]],
            type="candle",
            style=style,
            volume=True,
            addplot=addplots,
            panel_ratios=(3, 1, 1),
            title=f"\n{ticker} — Score {result['score']}/{max_possible_score}\n{title_reasons}\n{info_line}",
            figsize=(11, 8.5),
            returnfig=True,
            datetime_format="%d %b",
            xrotation=45,
        )

        # Add Fibonacci horizontal lines
        if result.get("fibonacci") and result["fibonacci"].get("levels"):
            fib_ax = axes[0]  # Price panel
            fib_data = result["fibonacci"]

            # Add horizontal lines untuk tiap Fibonacci level
            for i, (level_name, level_price) in enumerate(fib_data["levels"].items()):
                if level_name in ["0.0%", "100.0%"]:
                    color = fibonacci_colors[0] if level_name == "0.0%" else fibonacci_colors[-1]
                    width = 2.0
                    linestyle = "-"
                else:
                    # Find index dengan approximate matching (untuk handle floating point precision)
                    level_value = float(level_name.rstrip("%")) / 100
                    idx = None
                    fib_levels = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1]
                    for j, fib_level in enumerate(fib_levels):
                        if abs(fib_level - level_value) < 0.001:  # Tolerance untuk floating point
                            idx = j
                            break
                    if idx is None:
                        idx = i  # Fallback ke iteration index

                    color = fibonacci_colors[idx]
                    width = 1.0
                    # linestyle = "--"
                    linestyle = "-"

                fib_ax.axhline(y=level_price, color=color, linestyle=linestyle,
                            linewidth=width, alpha=0.7)

                # Add label untuk level
                fib_ax.text(0.01, level_price, f"  {level_name} ({level_price:,.0f})",
                          transform=fib_ax.get_yaxis_transform(), fontsize=7,
                          verticalalignment='center', color=color, weight='bold')

        # Garis referensi oversold/overbought di panel Stochastic RSI (panel index 2 -> axes[4] karena
        # tiap panel punya twin-axis, jadi urutannya: [price, price2, volume, volume2, stoch, stoch2])
        try:
            stoch_ax = axes[4]
            stoch_ax.axhline(stoch_oversold, color="gray", linestyle=":", linewidth=0.8)
            stoch_ax.axhline(100 - stoch_oversold, color="gray", linestyle=":", linewidth=0.8)
            stoch_ax.set_ylim(0, 100)
        except (IndexError, AttributeError):
            pass

        # mplfinance gak auto-legend addplot, jadi bikin manual
        legend_elems = [
            mlines.Line2D([], [], color="#888888", linestyle="--", label=f"SMA{trend_sma_period}"),
            mlines.Line2D([], [], color="#d62728", linestyle=":", label="BB Lower"),
            mlines.Line2D([], [], color="#9467bd", label=f"MA{ma_fast_period}"),
            mlines.Line2D([], [], color="#8c564b", linewidth=3.0, label=f"MA{ma_slow_period}"),
        ]
        if has_oversold:
            legend_elems.append(mlines.Line2D(
                [], [], color="orange", marker="v", linestyle="None", markersize=8, label="RSI Oversold"))
        if has_macd_cross:
            legend_elems.append(mlines.Line2D(
                [], [], color="blue", marker="^", linestyle="None", markersize=8, label="MACD Cross ↑"))
        if has_ma_cross:
            legend_elems.append(mlines.Line2D(
                [], [], color="gold", marker="*", linestyle="None", markersize=10,
                label=f"Golden Cross MA{ma_fast_period}/{ma_slow_period}"))
        axes[0].legend(handles=legend_elems, loc="upper left", fontsize=7.5, ncol=2)

        # Info harga terakhir + kapan data ini diambil (jam candle terakhir vs jam script jalan)
        last_candle_time = plot_df.index[-1]
        last_close = plot_df["Close"].iloc[-1]
        fetch_time = datetime.now(ZoneInfo(market_tz))

        # Check market status - pass as parameter instead of importing to avoid circular dependency
        # We'll get market status from config or use a default
        market_open = config.get('market_open', True)
        market_msg = config.get('market_msg', 'Market buka')

        # Check apakah data candle terakhir adalah hari ini
        today = fetch_time.date()
        last_candle_date = last_candle_time.date() if hasattr(last_candle_time, 'date') else last_candle_time
        data_stale = last_candle_date < today

        is_intraday = (plot_df.index.hour != 0).any() or (plot_df.index.minute != 0).any()
        time_fmt = "%d %b %Y %H:%M" if is_intraday else "%d %b %Y"

        # Market status indicator
        market_status_text = "[MARKET BUKA]" if market_open else "[MARKET TUTUP]"
        if data_stale and not market_open:
            market_status_text += f" (Data: {last_candle_time.strftime(time_fmt)})"

        # Fibonacci info
        fib_info = ""
        if result.get("fibonacci") and result["fibonacci"].get("levels"):
            fib = result["fibonacci"]
            current_close = result["close"]  # Use result close price
            # Cek posisi harga relatif terhadap Fibonacci levels
            fib_levels = fib["levels"]
            near_level = None
            min_dist = float('inf')
            for level_name, level_price in fib_levels.items():
                dist = abs(current_close - level_price) / current_close * 100
                if dist < min_dist:
                    min_dist = dist
                    near_level = level_name

            if near_level and min_dist < 5:  # Kalau dalam 5% dari suatu level
                fib_info = f"\nNear Fibo {near_level} ({min_dist:.1f}% away)"

        info_text = (
            f"{market_status_text}{fib_info}\n"
            f"Harga Terakhir: {last_close:,.0f}\n"
            f"Candle Terakhir: {last_candle_time.strftime(time_fmt)}\n"
            f"Analisa: {fetch_time.strftime('%d %b %Y %H:%M')} WIB"
        )
        axes[0].text(
            0.99, 0.98, info_text, transform=axes[0].transAxes,
            fontsize=8, ha="right", va="top",
            bbox=dict(boxstyle="round", facecolor="#0d0d0d", alpha=0.85, edgecolor="#333333", pad=5)
        )

        filename = f"{ticker}_{datetime.now().strftime('%Y%m%d_%H%M')}.png"
        filepath = os.path.join(charts_dir, filename)
        fig.savefig(filepath, dpi=130, bbox_inches="tight")
        import matplotlib.pyplot as plt
        plt.close(fig)
        return filepath
    except Exception as e:
        print(f"[WARN] Gagal bikin chart {ticker}: {e}")
        return None
