"""
Renko Chart Image Renderer (Pillow)
Generates high-resolution TradingView-style Renko chart snapshots without browser overhead.
Saves data and CPU while giving instant visual verification of the mathematical line and bricks.
"""
import json
import math
import os
import sys
from datetime import datetime, timezone, timedelta
from PIL import Image, ImageDraw, ImageFont

def render_renko_image(state_path="demo_state.json", out_path="chart.png", width=1280, height=720):
    if not os.path.exists(state_path):
        print(f"Error: {state_path} does not exist.")
        return False

    with open(state_path, "r", encoding="utf-8") as f:
        state = json.load(f)

    bricks = state.get("bricks_1h", [])
    if not bricks:
        print("No bricks in state.")
        return False

    # Take the last 65 bricks for a crisp, detailed view
    view_bricks = bricks[-65:]
    symbol = state.get("symbol", "ZECUSDC")
    mode = state.get("mode", "LIVE")
    price = state.get("price")
    line = state.get("line")
    pos = state.get("pos")
    maker_fee = state.get("maker_fee", 0.0)
    gate = state.get("gate", False)
    updated = state.get("updated", 0)

    # Color palette (TradingView dark theme)
    BG_COLOR = (7, 8, 10)
    PANEL_COLOR = (10, 13, 17)
    BORDER_COLOR = (23, 27, 33)
    GRID_COLOR = (18, 22, 28)
    TEXT_COLOR = (233, 237, 242)
    MUTED_TEXT = (119, 130, 142)
    UP_COLOR = (47, 190, 134)          # #2fbe86
    UP_FILL = (47, 190, 134, 128)      # 50% opacity
    DN_COLOR = (217, 74, 74)           # #d94a4a
    DN_FILL = (217, 74, 74, 128)       # 50% opacity
    LINE_COLOR = (216, 178, 90)        # #d8b25a (gold)
    PRICE_COLOR = (207, 216, 227)      # #cfd8e3
    TAG_BG_ON = (18, 48, 31)
    TAG_BORDER_ON = (29, 92, 60)
    TAG_BG_OFF = (51, 24, 26)
    TAG_BORDER_OFF = (92, 35, 39)

    # Setup base image & overlay for alpha blending
    img = Image.new("RGBA", (width, height), BG_COLOR + (255,))
    overlay = Image.new("RGBA", (width, height), (0, 0, 0, 0))
    draw = ImageDraw.Draw(img)
    draw_ov = ImageDraw.Draw(overlay)

    # Fonts
    try:
        font_lg = ImageFont.truetype("arial.ttf", 18)
        font_md = ImageFont.truetype("arial.ttf", 13)
        font_sm = ImageFont.truetype("arial.ttf", 11)
        font_xs = ImageFont.truetype("arial.ttf", 10)
    except Exception:
        font_lg = font_md = font_sm = font_xs = ImageFont.load_default()

    # Layout regions
    HEADER_H = 46
    CHART_TOP = HEADER_H + 30
    CHART_BOTTOM = height - 45
    CHART_LEFT = 15
    CHART_RIGHT = width - 90  # Leave room for price scale on right

    # 1. Header bar
    draw.rectangle([0, 0, width, HEADER_H], fill=PANEL_COLOR)
    draw.line([0, HEADER_H, width, HEADER_H], fill=BORDER_COLOR, width=1)

    cur_x = 16
    # Symbol
    draw.text((cur_x, 14), symbol, fill=TEXT_COLOR, font=font_lg)
    cur_x += 105

    # Tags: Mode
    mode_text = f" {mode} "
    draw.rectangle([cur_x, 14, cur_x + 55, 32], fill=TAG_BG_ON if mode == "LIVE" else (20, 24, 30),
                   outline=TAG_BORDER_ON if mode == "LIVE" else BORDER_COLOR)
    draw.text((cur_x + 8, 16), mode, fill=UP_COLOR if mode == "LIVE" else MUTED_TEXT, font=font_sm)
    cur_x += 65

    # Tag: Maker fee
    fee_str = f"MAKER {maker_fee*100:.3f}%"
    draw.rectangle([cur_x, 14, cur_x + 88, 32], fill=TAG_BG_ON if maker_fee == 0 else TAG_BG_OFF,
                   outline=TAG_BORDER_ON if maker_fee == 0 else TAG_BORDER_OFF)
    draw.text((cur_x + 7, 16), fee_str, fill=UP_COLOR if maker_fee == 0 else DN_COLOR, font=font_sm)
    cur_x += 98

    # Tag: Gate
    gate_str = "GATE ON" if gate else "GATE OFF"
    draw.rectangle([cur_x, 14, cur_x + 74, 32], fill=TAG_BG_ON if gate else (20, 24, 30),
                   outline=TAG_BORDER_ON if gate else BORDER_COLOR)
    draw.text((cur_x + 8, 16), gate_str, fill=UP_COLOR if gate else MUTED_TEXT, font=font_sm)
    cur_x += 84

    # Tag: Pos
    pos_str = "FLAT"
    if pos:
        dir_str = "LONG" if pos.get("dir") == 1 else "SHORT"
        carried_str = " HELD" if pos.get("carried") else ""
        pos_str = f"{dir_str}{carried_str}"
    draw.rectangle([cur_x, 14, cur_x + 65, 32], fill=TAG_BG_ON if pos else (20, 24, 30),
                   outline=TAG_BORDER_ON if pos else BORDER_COLOR)
    draw.text((cur_x + 8, 16), pos_str, fill=UP_COLOR if pos else MUTED_TEXT, font=font_sm)
    cur_x += 80

    # Line indicator
    if line is not None:
        draw.text((cur_x, 16), f"LINE: {line:.4f}", fill=LINE_COLOR, font=font_md)
        cur_x += 120

    # Price on right
    if price is not None:
        px_str = f"{price:.4f}"
        px_color = UP_COLOR if line and price >= line else DN_COLOR
        draw.text((width - 170, 12), px_str, fill=px_color, font=font_lg)

    # 2. Chart Pane Header
    sub_title = f"1HR CLOSE · THE LINE · MAIN ({len(bricks)} bricks total · last: {bricks[-1].get('label', '')})"
    draw.text((CHART_LEFT, HEADER_H + 8), sub_title, fill=MUTED_TEXT, font=font_sm)

    # Calculate price bounds
    all_prices = []
    for b in view_bricks:
        all_prices.extend([b["o"], b["c"]])
    if line is not None:
        all_prices.append(line)
    if price is not None:
        all_prices.append(price)

    min_p = min(all_prices)
    max_p = max(all_prices)
    p_padding = (max_p - min_p) * 0.08 or 1.0
    min_p -= p_padding
    max_p += p_padding

    def price_to_y(p):
        return CHART_BOTTOM - (p - min_p) / (max_p - min_p) * (CHART_BOTTOM - CHART_TOP)

    # Draw horizontal grid lines & price labels
    n_grid = 7
    step = (max_p - min_p) / n_grid
    for i in range(n_grid + 1):
        gp = min_p + i * step
        gy = price_to_y(gp)
        draw.line([CHART_LEFT, gy, CHART_RIGHT, gy], fill=GRID_COLOR, width=1)
        draw.text((CHART_RIGHT + 8, gy - 6), f"{gp:.2f}", fill=MUTED_TEXT, font=font_xs)

    # Draw vertical grid & bricks
    n_b = len(view_bricks)
    chart_w = CHART_RIGHT - CHART_LEFT
    bar_pitch = chart_w / max(n_b, 1)
    brick_w = max(4, bar_pitch * 0.75)

    last_label = ""
    for idx, b in enumerate(view_bricks):
        bx = CHART_LEFT + idx * bar_pitch + (bar_pitch - brick_w) / 2
        o, c, d = b["o"], b["c"], b.get("d", 1)
        top_p = max(o, c)
        bot_p = min(o, c)
        y_top = price_to_y(top_p)
        y_bot = price_to_y(bot_p)
        if abs(y_bot - y_top) < 2:
            y_bot = y_top + 2

        stroke = UP_COLOR if d == 1 else DN_COLOR
        fill = UP_FILL if d == 1 else DN_FILL

        # Semi-transparent fill on overlay
        draw_ov.rectangle([bx, y_top, bx + brick_w, y_bot], fill=fill)
        # Crisp solid border on main image
        draw.rectangle([bx, y_top, bx + brick_w, y_bot], outline=stroke, width=1)

        # Time labels every ~10 bricks
        lbl = b.get("label", "")
        if lbl and (idx % 10 == 0 or idx == n_b - 1) and lbl != last_label:
            # show HH:00 or MM-DD HH:00
            display_time = lbl.split()[-1] if " " in lbl else lbl
            draw.line([bx + brick_w/2, CHART_BOTTOM, bx + brick_w/2, CHART_BOTTOM + 5], fill=BORDER_COLOR)
            draw.text((bx - 12, CHART_BOTTOM + 8), display_time, fill=MUTED_TEXT, font=font_xs)
            last_label = lbl

    # 3. Dashed LINE overlay
    if line is not None:
        ly = price_to_y(line)
        # Draw dashed gold line
        dash_w = 6
        space_w = 4
        cur = CHART_LEFT
        while cur < CHART_RIGHT:
            draw.line([cur, ly, min(cur + dash_w, CHART_RIGHT), ly], fill=LINE_COLOR, width=2)
            cur += dash_w + space_w

        # Pill label for LINE on right axis
        pill_text = f"LINE {line:.2f}"
        pill_w = 78
        draw.rectangle([CHART_RIGHT, ly - 9, CHART_RIGHT + pill_w, ly + 9], fill=LINE_COLOR)
        draw.text((CHART_RIGHT + 5, ly - 6), pill_text, fill=(10, 13, 16), font=font_sm)

    # 4. PRICE line overlay
    if price is not None:
        py = price_to_y(price)
        draw.line([CHART_LEFT, py, CHART_RIGHT, py], fill=PRICE_COLOR, width=1)
        pill_text = f"{price:.2f}"
        pill_w = 60
        draw.rectangle([CHART_RIGHT, py - 8, CHART_RIGHT + pill_w, py + 8], fill=PRICE_COLOR)
        draw.text((CHART_RIGHT + 5, py - 6), pill_text, fill=(10, 13, 16), font=font_sm)

    # 5. Position Entry Line
    if pos and "entry" in pos:
        ey = price_to_y(pos["entry"])
        e_color = UP_COLOR if pos.get("dir") == 1 else DN_COLOR
        # dotted line
        cur = CHART_LEFT
        while cur < CHART_RIGHT:
            draw.line([cur, ey, min(cur + 3, CHART_RIGHT), ey], fill=e_color, width=1)
            cur += 7
        pill_text = f"ENTRY {pos['entry']:.2f}"
        draw.rectangle([CHART_RIGHT, ey - 8, CHART_RIGHT + 75, ey + 8], fill=e_color)
        draw.text((CHART_RIGHT + 4, ey - 6), pill_text, fill=(10, 13, 16), font=font_xs)

    # Merge alpha overlay into base image
    final_img = Image.alpha_composite(img, overlay)
    final_img.convert("RGB").save(out_path, "PNG")
    print(f"Chart snapshot saved to {out_path}")
    return True

if __name__ == "__main__":
    state_file = sys.argv[1] if len(sys.argv) > 1 else "demo_state.json"
    out_file = sys.argv[2] if len(sys.argv) > 2 else "chart_snapshot.png"
    render_renko_image(state_file, out_file)
