import yfinance as yf
import pandas as pd
import numpy as np
import requests
from datetime import datetime
import os
import re
import urllib.request
import urllib.parse
import xml.etree.ElementTree as ET

from config import TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID
from scanner_common import (
    calculate_rsi,
    detect_bullish_engulfing,
    detect_hammer,
    detect_liquidity_sweep,
    detect_volume_spike,
    detect_volume_ratio,
    detect_rsi_bullish_divergence,
    drop_incomplete_last_bar,
    make_verdict,
    regime_min_score,
    rolling_high,
)

# =========================================================
# CONFIGURATION
# =========================================================
REGIME_FILTER_MODE = "block"
SIGNAL_LOOKBACK_DAYS = 1
MIN_SCORE_BASE = 2

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DASHBOARD_FILE = os.path.join(BASE_DIR, "dashboard_thai.html")
TUNNEL_LOG = os.path.join(BASE_DIR, "tunnel.log")

# =========================================================
# UNIVERSE: SET50 / SET100
# =========================================================
UNIVERSE_THAI = [
    "PTT.BK", "PTTEP.BK", "TOP.BK", "BCP.BK", "GULF.BK", "GPSC.BK", "BGRIM.BK", "BANPU.BK",
    "KBANK.BK", "SCB.BK", "BBL.BK", "KTB.BK", "TTB.BK", "TISCO.BK", "SAWAD.BK", "MTC.BK", "TIDLOR.BK",
    "CPALL.BK", "CPAXT.BK", "CRC.BK", "BJC.BK", "HMPRO.BK", "GLOBAL.BK", "COM7.BK",
    "ADVANC.BK", "TRUE.BK", "DELTA.BK", "HANA.BK", "KCE.BK",
    "BDMS.BK", "BH.BK", "BCH.BK", "CHG.BK",
    "AOT.BK", "BEM.BK", "BTS.BK",
    "CPN.BK", "AP.BK", "SPALI.BK", "LH.BK", "SIRI.BK", "SCC.BK", "SCGP.BK", "WHA.BK", "AMATA.BK",
    "CPF.BK", "TU.BK", "CBG.BK", "OSP.BK", "ICHI.BK", "ITC.BK", "AAI.BK",
    "MINT.BK", "CENTEL.BK", "ERW.BK",
    "IVL.BK", "PTTGC.BK"
]

# =========================================================
# INDICATORS & REGIME CALCULATION
# =========================================================
def check_set_regime():
    try:
        idx = yf.Ticker("^SET.BK").history(period="1y")
        idx, _ = drop_incomplete_last_bar(idx, "TH")
        if idx.empty:
            return "UNKNOWN", 0, 0, 0
        idx['EMA200'] = idx['Close'].ewm(span=200, adjust=False).mean()
        latest_close = idx['Close'].iloc[-1]
        latest_ema200 = idx['EMA200'].iloc[-1]
        pct_diff = ((latest_close - latest_ema200) / latest_ema200) * 100
        is_bull = latest_close > latest_ema200
        return ("BULL", latest_close, latest_ema200, pct_diff) if is_bull else ("BEAR", latest_close, latest_ema200, pct_diff)
    except Exception:
        return "UNKNOWN", 0, 0, 0

# =========================================================
# THAI NEWS FETCHER & TECHNICAL DIAGNOSIS
# =========================================================
def fetch_thai_news(symbol):
    clean_sym = symbol.replace(".BK", "")
    try:
        query = urllib.parse.quote(f"{clean_sym} หุ้น")
        url = f"https://news.google.com/rss/search?q={query}&hl=th&gl=TH&ceid=TH:th"
        req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)'})
        with urllib.request.urlopen(req, timeout=5) as resp:
            xml_data = resp.read()
        root = ET.fromstring(xml_data)
        items = root.findall('./channel/item')
        headlines = []
        for it in items[:3]:
            title = it.find('title').text
            title = re.sub(r"\s*-\s*[^-]+$", "", title)
            headlines.append(title.strip())
        return " | ".join(headlines) if headlines else "ไม่มีข่าวลบที่มีนัยสำคัญ"
    except Exception:
        return "ภาพรวมข่าวสารปกติ"


def ask_ai_thai(symbol, price, drawdown, rsi, news_text,
                sweep=False, pa=False, ema=False, score=0,
                volume_spike=False, rsi_divergence=False, vol_ratio=1.0):
    """สร้างคำอธิบายแบบ rule-based (ไม่ใช้ AI) — ผลคงที่ทุกครั้ง"""
    return make_verdict(drawdown, rsi, sweep, pa, ema, score, news_text,
                        volume_spike, rsi_divergence, vol_ratio)


def get_live_tunnel_url():
    """อ่าน URL ล่าสุดของ Cloudflare tunnel จาก tunnel.log"""
    if os.path.exists(TUNNEL_LOG):
        try:
            with open(TUNNEL_LOG, "r") as f:
                matches = re.findall(r"https://[a-zA-Z0-9-]+\.trycloudflare\.com", f.read())
                if matches:
                    return matches[-1]
        except Exception:
            pass
    return None


def send_telegram_alert(text):
    url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
    payload = {
        "chat_id": TELEGRAM_CHAT_ID,
        "text": text,
        "parse_mode": "Markdown"
    }
    try:
        res = requests.post(url, data=payload, timeout=10)
        return res.json()
    except Exception as e:
        print(f"Error sending telegram: {e}")
        return None


# =========================================================
# DASHBOARD GENERATOR
# =========================================================
SECTOR_MAP_THAI = {
    "PTT.BK": "พลังงานและก๊าซธรรมชาติ", "PTTEP.BK": "สำรวจและผลิตปิโตรเลียม",
    "TOP.BK": "โรงกลั่นน้ำมัน", "BCP.BK": "โรงกลั่นและพลังงานทดแทน",
    "GULF.BK": "โรงไฟฟ้าและพลังงานหมุนเวียน", "GPSC.BK": "สาธารณูปโภคพลังงาน",
    "BGRIM.BK": "โรงไฟฟ้าอุตสาหกรรม", "BANPU.BK": "พลังงานและทรัพยากร",
    "KBANK.BK": "ธนาคารพาณิชย์", "SCB.BK": "ธนาคารและการเงินดิจิทัล",
    "BBL.BK": "ธนาคารพาณิชย์และธุรกิจ", "KTB.BK": "ธนาคารพาณิชย์และภาครัฐ",
    "TTB.BK": "ธนาคารพาณิชย์", "TISCO.BK": "เช่าซื้อและสินเชื่อ",
    "SAWAD.BK": "สินเชื่อรายย่อยจำนำทะเบียน", "MTC.BK": "สินเชื่อรายย่อยและไมโครไฟแนนซ์",
    "TIDLOR.BK": "สินเชื่อทะเบียนรถและประกัน",
    "CPALL.BK": "ค้าปลีกสะดวกซื้อ (7-Eleven)", "CPAXT.BK": "ค้าส่งและค้าปลีก (Makro/Lotus)",
    "CRC.BK": "ห้างสรรพสินค้าและไลฟ์สไตล์", "BJC.BK": "ค้าปลีกและอุปโภค (Big C)",
    "HMPRO.BK": "ค้าปลีกวัสดุตกแต่งบ้าน", "GLOBAL.BK": "ค้าปลีกวัสดุก่อสร้าง",
    "COM7.BK": "ค้าปลีกสินค้าไอทีสมาร์ทโฟน",
    "ADVANC.BK": "โทรคมนาคมและดิจิทัล (AIS)", "TRUE.BK": "โทรคมนาคมครบวงจร",
    "DELTA.BK": "อิเล็กทรอนิกส์ & AI Data Center",
    "HANA.BK": "ชิ้นส่วนเซมิคอนดักเตอร์", "KCE.BK": "แผงวงจรอิเล็กทรอนิกส์ (PCB)",
    "BDMS.BK": "การแพทย์และโรงพยาบาลกรุงเทพ", "BH.BK": "โรงพยาบาลพรีเมียม (บำรุงราษฎร์)",
    "BCH.BK": "โรงพยาบาลเกษมราษฎร์", "CHG.BK": "โรงพยาบาลจุฬารัตน์",
    "AOT.BK": "ท่าอากาศยานสากลแห่งประเทศไทย", "BEM.BK": "รถไฟฟ้าสายสีน้ำเงินและทางด่วน",
    "BTS.BK": "ระบบขนส่งมวลชนและสื่อโฆษณา",
    "CPN.BK": "พัฒนาศูนย์การค้าเซ็นทรัล", "AP.BK": "พัฒนาอสังหาริมทรัพย์",
    "SPALI.BK": "ที่อยู่อาศัยและคอนโดมิเนียม", "LH.BK": "บ้านเดี่ยวและคอนโด (แลนด์แอนด์เฮ้าส์)",
    "SIRI.BK": "คอนโดมิเนียมและบ้านจัดสรร", "SCC.BK": "วัสดุก่อสร้างและเคมีคอลส์ (SCG)",
    "SCGP.BK": "บรรจุภัณฑ์ครบวงจร (SCG)", "WHA.BK": "นิคมอุตสาหกรรมและโลจิสติกส์",
    "AMATA.BK": "นิคมอุตสาหกรรมอมตะ",
    "CPF.BK": "เกษตรอุตสาหกรรมและอาหาร (ซีพีเอฟ)", "TU.BK": "อาหารทะเลแปรรูป (ไทยยูเนี่ยน)",
    "CBG.BK": "เครื่องดื่มชูกำลัง (คาราบาวแดง)", "OSP.BK": "เครื่องดื่มและอุปโภค (โอสถสภา)",
    "ICHI.BK": "เครื่องดื่มชาเขียว (อิชิตัน)", "ITC.BK": "อาหารสัตว์เลี้ยงพรีเมียม",
    "AAI.BK": "อาหารสัตว์เลี้ยงส่งออก",
    "MINT.BK": "โรงแรม รีสอร์ต และอาหาร (ไมเนอร์)", "CENTEL.BK": "โรงแรมเซ็นทาราและอาหาร",
    "ERW.BK": "เครือข่ายโรงแรมเอราวัณ",
    "IVL.BK": "ปิโตรเคมีและโพลีเอสเตอร์", "PTTGC.BK": "เคมีภัณฑ์และปิโตรเคมี (พีทีทีจีซี)"
}

APPLE_ACCENTS = [
    ("from-emerald-400 to-teal-500", "bg-emerald-50 text-emerald-700 border-emerald-200"),
    ("from-sky-400 to-blue-500", "bg-sky-50 text-sky-700 border-sky-200"),
    ("from-indigo-400 to-violet-500", "bg-indigo-50 text-indigo-700 border-indigo-200"),
    ("from-purple-400 to-fuchsia-500", "bg-purple-50 text-purple-700 border-purple-200"),
    ("from-amber-400 to-orange-500", "bg-amber-50 text-amber-700 border-amber-200"),
    ("from-rose-400 to-pink-500", "bg-rose-50 text-rose-700 border-rose-200")
]


def generate_dashboard_thai_html(regime, set_close, set_ema, pct_diff, grade_a, watchlist):
    timestamp_str = datetime.now().strftime("%d %b %Y | %H:%M น.")
    count_top = len([s for s in grade_a if s['score'] >= 4])

    cards_html = ""
    for idx, s in enumerate(grade_a):
        clean_sym = s['symbol'].replace('.BK', '')
        sector = SECTOR_MAP_THAI.get(s['symbol'], "SET100 Blue Chip")
        score = s['score']
        is_top = (score >= 4)
        grad_color, pill_color = APPLE_ACCENTS[idx % len(APPLE_ACCENTS)]

        if is_top:
            star_badge = """
            <span class="inline-flex items-center gap-1.5 px-3.5 py-1 rounded-full text-sm font-semibold bg-amber-500/15 text-amber-900 border border-amber-300/80 shadow-sm">
              <span class="text-amber-500">★</span> สัญญาณครบ 4/4 (Prime Setup)
            </span>
            """
            card_ring = "ring-2 ring-amber-400/50 shadow-[0_12px_36px_rgba(245,158,11,0.12)]"
        else:
            star_badge = f"""
            <span class="inline-flex items-center gap-1.5 px-3.5 py-1 rounded-full text-sm font-medium bg-sky-500/10 text-sky-900 border border-sky-200/80">
              <span class="text-sky-500">●</span> ผ่าน {score}/4 สัญญาณ
            </span>
            """
            card_ring = "border border-white/60 shadow-[0_8px_30px_rgba(0,0,0,0.04)]"

        cards_html += f"""
        <div class="stock-card group relative rounded-[2rem] p-5 sm:p-6 bg-white/80 backdrop-blur-2xl transition-all duration-300 hover:-translate-y-1.5 hover:shadow-[0_20px_45px_rgba(0,113,227,0.12)] {card_ring}"
             data-symbol="{clean_sym}" data-score="{score}">
          <div class="flex items-start justify-between gap-3 mb-4">
            <div class="flex items-center gap-3 min-w-0">
              <div class="w-[52px] h-[52px] shrink-0 rounded-2xl bg-gradient-to-tr {grad_color} text-white font-bold flex items-center justify-center text-lg tracking-tight shadow-md shadow-blue-500/20">
                {clean_sym[:4]}
              </div>
              <div class="min-w-0">
                <h3 class="font-bold text-slate-900 text-2xl tracking-tight truncate group-hover:text-[#0071e3] transition">{clean_sym}</h3>
                <p class="text-xs sm:text-sm text-slate-500 font-medium truncate mt-0.5">{sector}</p>
              </div>
            </div>
            <div class="text-right shrink-0">
              <div class="text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight">฿{s['price']:.2f}</div>
              <div class="flex items-center justify-end gap-1.5 mt-1">
                <span class="text-xs font-semibold px-2 py-0.5 rounded-full bg-rose-500/10 text-rose-700 border border-rose-200">
                  -{s['drawdown']:.1f}%
                </span>
                <span class="text-xs font-semibold px-2 py-0.5 rounded-full bg-emerald-500/10 text-emerald-700 border border-emerald-200">
                  RSI {s['rsi']:.1f}
                </span>
              </div>
            </div>
          </div>
          <div class="mb-4">{star_badge}</div>
          <div class="grid grid-cols-4 gap-1.5 py-2.5 px-3 bg-slate-100/70 backdrop-blur-md rounded-2xl text-xs sm:text-sm font-semibold text-slate-700 mb-4 border border-slate-200/50">
            <div class="flex items-center justify-center gap-1 {'text-emerald-600 font-bold' if s['sweep'] else 'text-slate-400'}">
              <span>{'✓' if s['sweep'] else '—'}</span> <span class="truncate">SMC Sweep</span>
            </div>
            <div class="flex items-center justify-center gap-1 {'text-emerald-600 font-bold' if s['pa'] else 'text-slate-400'}">
              <span>{'✓' if s['pa'] else '—'}</span> <span class="truncate">Price Action</span>
            </div>
            <div class="flex items-center justify-center gap-1 {'text-emerald-600 font-bold' if s['ema'] else 'text-slate-400'}">
              <span>{'✓' if s['ema'] else '—'}</span> <span class="truncate">เหนือ EMA9</span>
            </div>
            <div class="flex items-center justify-center gap-1 {'text-emerald-600 font-bold' if s.get('volume_spike') else 'text-slate-400'}">
              <span>{'✓' if s.get('volume_spike') else '—'}</span> <span class="truncate">Volume {s.get('vol_ratio', 1.0):.1f}x</span>
            </div>
          </div>
          <div class="grid grid-cols-3 gap-2 mb-4">
            <div class="rounded-xl px-3 py-2 bg-slate-50 border border-slate-200/70 text-center">
              <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wide">จุดเข้า</div>
              <div class="text-base font-bold text-slate-900">฿{s['price']:.2f}</div>
            </div>
            <div class="rounded-xl px-3 py-2 bg-rose-50 border border-rose-200/70 text-center">
              <div class="text-[11px] font-semibold text-rose-400 uppercase tracking-wide">ตัดขาดทุน</div>
              <div class="text-base font-bold text-rose-700">฿{s.get('stop_loss', 0):.2f}</div>
              <div class="text-[11px] font-medium text-rose-500">{s.get('sl_pct', 0):.1f}%</div>
            </div>
            <div class="rounded-xl px-3 py-2 bg-emerald-50 border border-emerald-200/70 text-center">
              <div class="text-[11px] font-semibold text-emerald-500 uppercase tracking-wide">เป้าขาย</div>
              <div class="text-base font-bold text-emerald-700">฿{s.get('take_profit', 0):.2f}</div>
              <div class="text-[11px] font-medium text-emerald-600">+{s.get('tp_pct', 0):.1f}%</div>
            </div>
          </div>
          <div class="rounded-2xl p-4 bg-gradient-to-br from-indigo-50/80 via-white/70 to-blue-50/60 border border-blue-100/80 backdrop-blur-md mb-4 shadow-sm">
            <div class="flex items-center gap-2 mb-2 text-xs sm:text-sm font-bold text-[#0071e3]">
              <span>🧠</span> <span>วิเคราะห์ตามสัญญาณเทคนิค:</span>
            </div>
            <p class="text-sm sm:text-base text-slate-700 leading-relaxed font-medium">
              {s.get('ai_verdict', 'ย่อตัวตามภาวะตลาดรวม')}
            </p>
            <div class="mt-2.5 pt-2 border-t border-blue-100/60 text-xs text-slate-500 italic">
              📰 ข่าวล่าสุด: {s.get('news_summary', '')[:90]}...
            </div>
          </div>
          {'<div class="mb-4 px-3 py-2 rounded-xl bg-emerald-50/80 border border-emerald-200/60 text-sm font-semibold text-emerald-700">📈 RSI Divergence detected — ขาลงอ่อนแรง มีโอกาสกลับตัว</div>' if s.get('rsi_divergence') else ''}
        </div>
        """

    watchlist_html = ""
    for w in watchlist:
        clean_w = w['symbol'].replace('.BK', '')
        w_sector = SECTOR_MAP_THAI.get(w['symbol'], 'SET100')
        watchlist_html += f"""
        <div class="flex items-center justify-between p-4 rounded-2xl bg-white/60 hover:bg-white/90 border border-slate-200/60 transition shadow-sm">
          <div class="flex items-center gap-3">
            <div class="w-10 h-10 rounded-xl bg-slate-100 text-slate-700 font-bold flex items-center justify-center text-sm">{clean_w[:4]}</div>
            <div>
              <div class="font-bold text-slate-900 text-base sm:text-lg">{clean_w}</div>
              <div class="text-xs sm:text-sm text-slate-500 font-medium">{w_sector}</div>
            </div>
          </div>
          <div class="text-right">
            <div class="text-base sm:text-lg font-bold text-slate-800">฿{w['price']:.2f}</div>
            <div class="text-xs sm:text-sm font-semibold text-rose-600">-{w['drawdown']:.1f}% (RSI {w['rsi']:.1f})</div>
          </div>
        </div>
        """

    empty_html = """
    <div class="col-span-full py-16 px-8 rounded-[2.5rem] bg-white/60 backdrop-blur-xl border border-white/80 text-center">
      <div class="text-5xl mb-4">🛡️</div>
      <h3 class="text-2xl font-bold text-slate-800 mb-2">วันนี้ยังไม่มีหุ้นเกรด A+ ครบทุกเงื่อนไข</h3>
      <p class="text-slate-500 max-w-lg mx-auto text-base">ระบบสแกนหุ้น SET100 ทั้งหมดแล้ว ไม่พบหุ้นที่เข้าเกณฑ์</p>
    </div>
    """

    regime_color = "bg-emerald-500 shadow-emerald-500" if regime == "BULL" else "bg-amber-500 shadow-amber-500"
    regime_text = f"BULL (+{pct_diff:.1f}% เหนือ EMA200)" if regime == "BULL" else f"BEAR ({pct_diff:.1f}% ต่ำกว่า EMA200)"

    full_html = f"""<!DOCTYPE html>
<html lang="th">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SET100 Radar Terminal</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <style>
    body {{ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Sukhumvit Set", sans-serif; background: radial-gradient(circle at 10% 20%, rgba(216, 241, 255, 0.8) 0%, rgba(255, 245, 235, 0.7) 45%, rgba(238, 242, 255, 0.8) 100%); min-height: 100vh; color: #1d1d1f; }}
    .traffic-dot {{ width: 12px; height: 12px; border-radius: 50%; }}
  </style>
</head>
<body class="p-4 sm:p-6 lg:p-8">
  <div class="max-w-7xl mx-auto flex justify-center gap-3 mb-6">
    <a href="dashboard.html" class="px-5 py-2 rounded-full font-bold text-sm bg-white/80 hover:bg-white text-slate-600 border border-slate-200/80 shadow-sm flex items-center gap-2 transition-all">🇺🇸 ตลาดหุ้นอเมริกา (S&amp;P 500)</a>
    <a href="dashboard_thai.html" class="px-5 py-2 rounded-full font-bold text-sm bg-[#0071e3] text-white shadow-md shadow-[#0071e3]/30 flex items-center gap-2">🇹🇭 ตลาดหุ้นไทย (SET100)</a>
  </div>
  <div class="max-w-7xl mx-auto bg-white/75 backdrop-blur-2xl rounded-[2.5rem] p-6 sm:p-8 mb-8 border border-white/80 shadow-[0_20px_50px_rgba(0,0,0,0.06)]">
    <div class="flex items-center justify-between pb-6 mb-6 border-b border-slate-200/60">
      <div class="flex items-center gap-2.5">
        <span class="traffic-dot bg-[#ff5f56] border border-[#e0443e]"></span>
        <span class="traffic-dot bg-[#ffbd2e] border border-[#dea123]"></span>
        <span class="traffic-dot bg-[#27c93f] border border-[#1aab29]"></span>
        <span class="ml-3 text-xs font-semibold tracking-wide text-slate-400 uppercase">SET100 Terminal</span>
      </div>
      <div class="text-xs sm:text-sm font-medium text-slate-500">🕒 {timestamp_str}</div>
    </div>
    <div class="flex flex-col md:flex-row items-start md:items-center justify-between gap-6">
      <div>
        <h1 class="text-3xl sm:text-4xl lg:text-5xl font-extrabold tracking-tight text-[#1d1d1f]">SET100 Radar Terminal</h1>
        <p class="text-base sm:text-lg font-medium text-slate-500">คัดกรองหุ้นไทยชั้นนำ ย่อตัวลึก + SMC Sweep + แท่งกลับตัว + วิเคราะห์ตามสัญญาณ</p>
      </div>
      <div class="bg-white/90 backdrop-blur-xl px-5 py-3.5 rounded-2xl border border-white/80 shadow-sm flex items-center gap-4">
        <div class="w-3.5 h-3.5 rounded-full {regime_color} shadow-sm animate-pulse"></div>
        <div>
          <div class="text-xs font-semibold text-slate-400 uppercase tracking-wider">SET Index</div>
          <div class="text-base sm:text-lg font-bold text-slate-800">{regime_text}</div>
        </div>
      </div>
    </div>
    <div class="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-6 pt-6 border-t border-slate-200/60">
      <div class="bg-white/60 backdrop-blur-lg rounded-2xl p-4 sm:p-5 border border-white/80 shadow-sm">
        <div class="text-sm font-semibold text-slate-500">หุ้นเกรด A+</div>
        <div class="text-4xl font-extrabold text-[#1d1d1f] mt-1">{len(grade_a)} <span class="text-sm font-semibold text-[#0071e3]">ตัว</span></div>
        <div class="text-xs sm:text-sm text-slate-400 font-medium mt-0.5">ผ่านเกณฑ์ครบ: {count_top} ตัว</div>
      </div>
      <div class="bg-white/60 backdrop-blur-lg rounded-2xl p-4 sm:p-5 border border-white/80 shadow-sm">
        <div class="text-sm font-semibold text-slate-500">Watchlist</div>
        <div class="text-4xl font-extrabold text-[#1d1d1f] mt-1">{len(watchlist)} <span class="text-sm font-semibold text-amber-600">ตัว</span></div>
        <div class="text-xs sm:text-sm text-slate-400 font-medium mt-0.5">ย่อตัวลึก รอสัญญาณ</div>
      </div>
      <div class="bg-white/60 backdrop-blur-lg rounded-2xl p-4 sm:p-5 border border-white/80 shadow-sm">
        <div class="text-sm font-semibold text-slate-500">SET Index</div>
        <div class="text-4xl font-extrabold text-[#1d1d1f] mt-1">{set_close:.2f} <span class="text-sm font-semibold text-emerald-600">จุด</span></div>
        <div class="text-xs sm:text-sm text-slate-400 font-medium mt-0.5">EMA200: {set_ema:.2f}</div>
      </div>
    </div>
  </div>
  <main class="max-w-7xl mx-auto">
    <div id="cardsGrid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
      {cards_html if grade_a else empty_html}
    </div>
    <div class="mt-12 mb-8">
      <div class="flex items-center justify-between mb-4">
        <h2 class="text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight">👀 Watchlist หุ้นคุณภาพในโซนถูก</h2>
        <span class="px-3.5 py-1 rounded-full text-sm font-bold bg-amber-500/15 text-amber-900 border border-amber-300">{len(watchlist)} ตัว</span>
      </div>
      <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">{watchlist_html}</div>
    </div>
  </main>
  <footer class="max-w-7xl mx-auto text-center py-10 text-xs sm:text-sm text-slate-400 font-medium">
    Automated SET100 Scanner • Rule-based Technical Analysis
  </footer>
</body>
</html>"""
    with open(DASHBOARD_FILE, "w", encoding="utf-8") as f:
        f.write(full_html)
    print(f"✅ Dashboard saved: {DASHBOARD_FILE}")


# =========================================================
# MAIN SCANNER LOGIC
# =========================================================
def main():
    print("=====================================================")
    print("🚀 เริ่มสแกนตลาดหุ้นไทย (SET100)...")
    print(f"🕒 เวลา: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print("=====================================================")

    regime, set_close, set_ema, pct_diff = check_set_regime()
    print(f"📊 สภาวะ SET Index: {regime} (SET: {set_close:.2f} / EMA200: {set_ema:.2f})")

    grade_a = []
    watchlist = []

    min_score = regime_min_score(regime, base_score=MIN_SCORE_BASE, mode=REGIME_FILTER_MODE)
    if min_score >= 90:
        print(f"⚠️ SET เป็น {regime} -> ระงับการแจ้งเตือนทั้งหมดวันนี้")
    elif min_score != MIN_SCORE_BASE:
        print(f"⚠️ SET เป็น {regime} -> ยกระดับเกณฑ์เป็น {min_score}/3")
    else:
        print(f"ℹ️ เกณฑ์แจ้งเตือนขั้นต่ำ: {min_score}/4 สัญญาณ")

    warned_bar = False
    for sym in UNIVERSE_THAI:
        try:
            t = yf.Ticker(sym)
            df = t.history(period="1y")
            df, note = drop_incomplete_last_bar(df, "TH")
            if note and not warned_bar:
                print(f"ℹ️ {note}")
                warned_bar = True
            if df.empty or len(df) < 30:
                continue

            latest_close = df['Close'].iloc[-1]
            df['52W_High'] = rolling_high(df, window=250, min_periods=30)
            high_52w = df['52W_High'].iloc[-1]
            drawdown = ((latest_close - high_52w) / high_52w) * 100

            df['RSI'] = calculate_rsi(df['Close'])
            latest_rsi = df['RSI'].iloc[-1]

            if (drawdown <= -10.0 and latest_rsi <= 45.0) or (drawdown <= -8.0 and latest_rsi <= 38.0):
                df['EMA9'] = df['Close'].ewm(span=9, adjust=False).mean()
                df['EMA50'] = df['Close'].ewm(span=50, adjust=False).mean()
                ema_pass = bool(latest_close > df['EMA9'].iloc[-1])

                lb = SIGNAL_LOOKBACK_DAYS
                sweep_pass = bool(detect_liquidity_sweep(df, lookback=10).iloc[-lb:].any())

                pa_series = detect_hammer(df, wick_mult=1.8) | detect_bullish_engulfing(df)
                pa_pass = bool(pa_series.iloc[-lb:].any())

                vol_spike = detect_volume_spike(df, window=20, threshold=2.0)
                vol_ratio = detect_volume_ratio(df, window=20)
                rsi_div = detect_rsi_bullish_divergence(df)

                score = int(sweep_pass) + int(pa_pass) + int(ema_pass) + int(vol_spike)

                stop_loss = float(df['Low'].iloc[-3:].min()) * 0.99
                take_profit = max(float(df['EMA50'].iloc[-1]), float(latest_close) * 1.08)

                news_text = fetch_thai_news(sym)

                stock_data = {
                    "symbol": sym,
                    "price": latest_close,
                    "drawdown": abs(drawdown),
                    "rsi": latest_rsi,
                    "sweep": sweep_pass,
                    "pa": pa_pass,
                    "ema": ema_pass,
                    "volume_spike": vol_spike,
                    "vol_ratio": vol_ratio,
                    "rsi_divergence": rsi_div,
                    "score": score,
                    "stop_loss": stop_loss,
                    "take_profit": take_profit,
                    "sl_pct": ((stop_loss - latest_close) / latest_close) * 100,
                    "tp_pct": ((take_profit - latest_close) / latest_close) * 100,
                    "news_summary": news_text
                }

                if score >= min_score:
                    print(f"🔎 พบ {sym} (คะแนน {score}/4)")
                    stock_data["ai_verdict"] = ask_ai_thai(
                        sym, latest_close, abs(drawdown), latest_rsi, news_text,
                        sweep_pass, pa_pass, ema_pass, score,
                        vol_spike, rsi_div, vol_ratio)
                    grade_a.append(stock_data)
                else:
                    stock_data["ai_verdict"] = "โซนของถูก รอยืนยันสัญญาณกลับตัว"
                    watchlist.append(stock_data)

        except Exception as e:
            print(f"Error processing {sym}: {e}")
            continue

    grade_a = sorted(grade_a, key=lambda x: (x['score'], -x['drawdown']), reverse=True)
    watchlist = sorted(watchlist, key=lambda x: (x['score'], -x['drawdown']), reverse=True)[:6]

    generate_dashboard_thai_html(regime, set_close, set_ema, pct_diff, grade_a, watchlist)

    if grade_a:
        msg = f"🇹🇭 *แจ้งเตือนเรดาร์หุ้นไทย (SET100) เกรด A+*\n"
        msg += f"🗓️ {datetime.now().strftime('%d/%m/%Y')} | สภาวะ SET: *{regime}*\n"
        msg += f"------------------------------------\n"
        for s in grade_a[:4]:
            clean_name = s['symbol'].replace('.BK', '')
            score_text = "ผ่านครบ 4 สัญญาณ" if s['score'] >= 4 else f"ผ่าน {s['score']}/4 สัญญาณ"
            extras = []
            if s.get('volume_spike'):
                extras.append(f"🔥 Vol {s['vol_ratio']:.1f}x")
            if s.get('rsi_divergence'):
                extras.append("📈 RSI Div")
            extra_txt = " | ".join(extras)
            msg += f"🟢 *{clean_name}* (ราคา ฿{s['price']:.2f})\n"
            msg += f"• ย่อตัว: *-{s['drawdown']:.1f}%* | RSI: *{s['rsi']:.1f}*\n"
            msg += f"• สัญญาณ: *{score_text}*"
            if extra_txt:
                msg += f" ({extra_txt})"
            msg += "\n"
            msg += f"• 🎯 *จุดเข้า:* ฿{s['price']:.2f} | 🛑 *SL:* ฿{s['stop_loss']:.2f} ({s['sl_pct']:.1f}%)\n"
            msg += f"• 🏆 *เป้าขาย:* ฿{s['take_profit']:.2f} (+{s['tp_pct']:.1f}%)\n"
            msg += f"• 🧠 *วิเคราะห์สัญญาณ:* _{s['ai_verdict']}_\n\n"

        live_url = get_live_tunnel_url()
        if live_url:
            msg += f"🌐 *เปิดดู Dashboard ฉบับเต็ม:*\n{live_url}/dashboard_thai.html\n"
        else:
            msg += "_(หา URL ของ tunnel ไม่เจอ — ตรวจสอบว่า cloudflared ทำงานอยู่หรือไม่)_\n"
        send_telegram_alert(msg)
        print(f"✅ ส่งแจ้งเตือนหุ้นไทย {len(grade_a)} ตัว เข้า Telegram สำเร็จ!")
    elif min_score >= 90:
        print(f"ℹ️ SET อยู่ใต้ EMA200 (BEAR) — ระงับการแจ้งเตือนวันนี้")
    else:
        print("ℹ️ วันนี้ไม่มีหุ้นไทยเข้าเกณฑ์เกรด A+")

    print(f"\n📊 สรุป: {len(grade_a)} หุ้นเกรด A+ | {len(watchlist)} หุ้น Watchlist")


if __name__ == "__main__":
    main()
