import requests
from bs4 import BeautifulSoup
import telegram
import asyncio
import os
import json
import time
import random
import re
from urllib.parse import unquote  # URL 디코딩 (한글 변환)
from datetime import datetime
from mm_sender import bot  # 매터모스트 봇 가져오기

# ---------------- 설 정 ----------------

TARGET_MODE = 'ALL' 
TELEGRAM_TOKEN = '7912820100:AAGw0MWReevw6QyWm3QTSK4Wy7otuq8fxMo'
CHAT_ID = '-5238702679'

# 🌟 [추가됨] 랜덤 User-Agent 리스트
USER_AGENTS = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0',
    'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
]

def get_keywords():
    try:
        with open('carrot_region_sel_one_keywords.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
            raw_list = data.get('keywords', [])
            processed_list = []
            for item in raw_list:
                if isinstance(item, str):
                    processed_list.append({"text": item, "min": 0, "max": 0})
                else:
                    processed_list.append(item)
            return processed_list
    except:
        return []

LOG_FILE = 'daangn_sent_sel_one_ids.json'
REGION_FILE = 'regions.json'

# ---------------- 함 수 ----------------

def parse_price(price_str):
    if not price_str or "가격없음" in price_str:
        return 0
    if "나눔" in price_str:
        return 0  # 나눔은 0 원으로 처리
    # 숫자만 추출 (콤마, 원, 공백 등 제거)
    numbers = re.sub(r"[^0-9]", "", price_str)
    try:
        return int(numbers) if numbers else 0
    except Exception:
        return 0


def _normalize_record(val):
    """
    sent_data에 저장되는 값을 일관된 형태로 맞춘다.
    항상 {'price': int, 'time': str|None} 형태로 반환.
    """
    if isinstance(val, dict):
        price = val.get("price", 0)
        time_str = val.get("time")
        try:
            price = int(price)
        except Exception:
            price = 0
        if time_str is not None and not isinstance(time_str, str):
            time_str = str(time_str)
        return {"price": price, "time": time_str}
    # 과거 데이터: 숫자만 있는 경우
    try:
        price = int(val)
    except Exception:
        price = 0
    return {"price": price, "time": None}


def get_saved_price(record):
    """
    sent_data에서 기존 가격(int)을 꺼낼 때 사용.
    구(숫자), 신(dict) 구조를 모두 지원.
    """
    if isinstance(record, dict):
        record = record.get("price", 0)
    try:
        return int(record)
    except Exception:
        return 0

def load_region_data(mode):
    if not os.path.exists(REGION_FILE):
        print("❌ regions.json 파일이 없습니다!")
        return {}
    try:
        with open(REGION_FILE, 'r', encoding='utf-8') as f:
            data = json.load(f)
        rich = data.get('RICH', {})
        vicinity = data.get('VICINITY', {})
        jjin = data.get('JJIN_VICINITY', {})
        
        if mode == 'RICH': return rich
        elif mode == 'VICINITY': return vicinity
        elif mode == 'JJIN': return jjin
        else:
            merged = {**rich, **vicinity, **jjin}
            print(f"🌏 [모드] '전체(ALL)' 지역 로드함 (총 {len(merged)}곳)")
            return merged
    except Exception as e:
        print(f"❌ JSON 파일 읽기 실패: {e}")
        return {}

def load_sent_data():
    if not os.path.exists(LOG_FILE):
        return {}
    try:
        with open(LOG_FILE, "r", encoding="utf-8") as f:
            data = json.load(f)

        # 아주 예전 리스트 형태라면 -1 가격으로 초기화
        if isinstance(data, list):
            data = {x: {"price": -1, "time": None} for x in data}
        elif isinstance(data, dict):
            changed = False
            new_data = {}
            for k, v in data.items():
                norm = _normalize_record(v)
                if norm != v:
                    changed = True
                new_data[k] = norm
            data = new_data
            # 파일에 섞여 있던 옛 형식을 한 번에 정리
            if changed:
                save_sent_data(data)

        return data
    except Exception:
        return {}

def save_sent_data(data):
    # 최신 3000 개만 유지 (메모리 + 속도 최적화)
    if len(data) > 3000:
        keys_to_keep = list(data.keys())[-3000:]
        data = {k: data[k] for k in keys_to_keep}
    # 원자적 저장 (파일 깨짐 방지)
    temp_file = LOG_FILE + '.tmp'
    try:
        with open(temp_file, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        os.replace(temp_file, LOG_FILE)
    except Exception as e:
        print(f"파일 저장 오류: {e}")

async def send_telegram(message):
    try:
        bot = telegram.Bot(token=TELEGRAM_TOKEN)
        await bot.send_message(chat_id=CHAT_ID, text=message, parse_mode='HTML')
    except Exception as e:
        print(f"❌ 텔레그램 전송 실패: {e}")

async def check_daangn_local():
    # 1회용이므로 시작 딜레이(time.sleep) 삭제함
    
    sent_data = load_sent_data()
    region_codes = load_region_data(TARGET_MODE)
    target_dong_names = list(region_codes.keys())
    
    random.shuffle(target_dong_names) # 동네 섞기 (유지)
    
    print(f"🚀 [1회용] 수동 크롤링 시작... 총 {len(target_dong_names)}개 동네", flush=True)

    loop_cnt = 0
    
    # 루프 시작 시 키워드 다시 읽기 (JSON 변경 반영)
    KEYWORDS = get_keywords()
    print(f"📝 [키워드] 총 {len(KEYWORDS)}개 로드: {[k['text'] for k in KEYWORDS]}", flush=True)

    for dong_name in target_dong_names:
        region_code = region_codes[dong_name]
        
        # 30회마다 휴식 (안전을 위해 유지 권장)
        if loop_cnt > 0 and loop_cnt % 30 == 0:
            rest_time = random.uniform(5, 10) # 수동이니까 조금 짧게 잡음
            print(f"\n☕ [휴식] {rest_time:.1f}초 쉬었다 갑니다...\n")
            time.sleep(rest_time)
        
        loop_cnt += 1

        for item in KEYWORDS:
            keyword = item['text']
            min_price = item.get('min', 0)
            max_price = item.get('max', 0)
            # category_id가 지정돼 있으면 해당 카테고리만, 없으면(0/빈값) 전체 카테고리 검색
            category_id = item.get('category_id', 0)
            # 제외 단어(exclude): 제목에 이 단어가 포함되면 결과에서 걸러냄 (대소문자 무시)
            # JSON에서 문자열 하나만 넣어도 되고, 리스트로 여러 개 넣어도 됨
            exclude_raw = item.get('exclude', [])
            if isinstance(exclude_raw, str):
                exclude_raw = [exclude_raw]
            exclude_words = [w.lower() for w in exclude_raw if isinstance(w, str) and w.strip()]

            url = f"https://www.daangn.com/kr/buy-sell/?in={dong_name}-{region_code}&search={keyword}"
            if category_id:
                url += f"&category_id={category_id}"
            
            # 로그에 region_code(ID)도 같이 찍어서 랜덤 여부 확인
            print(f"🔎 [{loop_cnt}/{len(target_dong_names)}] {dong_name}({region_code}) - {keyword} ", end='', flush=True)
            
            # 🌟 [수정됨] 헤더를 여기서 랜덤으로 생성
            headers = {
                'User-Agent': random.choice(USER_AGENTS),
                'Referer': 'https://www.daangn.com/', # 메인에서 온 척 위장
                'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7'
            }

            try:
                res = requests.get(url, headers=headers, timeout=5)
                if res.status_code != 200:
                    print(f"⚠️ 상태 {res.status_code}")
                    time.sleep(10)
                    continue

                res.encoding = 'utf-8'
                soup = BeautifulSoup(res.text, 'html.parser')
                articles = soup.find_all("a", {"data-gtm": "search_article"})
                
                if len(articles) == 0:
                    print("매물X")
                    time.sleep(random.uniform(0.5, 0.8)) # 수동이니까 아주 살짝 빠르게
                    continue
                
                new_cnt = 0
                drop_cnt = 0
                skip_cnt = 0 
                
                for article in articles:
                    try:
                        href = article['href']
                        # URL 디코딩 적용 (한글 키 저장)
                        raw_id = href.rstrip('/').split('/')[-1]
                        article_id = unquote(raw_id)
                        
                        text_lines = list(article.stripped_strings)
                        # 첫 줄은 제목으로 사용
                        title = text_lines[0] if len(text_lines) >= 1 else "제목없음"

                        # [제외 필터] 제목에 제외 단어가 포함되면 알림에서 걸러냄 (대소문자 무시)
                        if exclude_words:
                            title_lower = title.lower()
                            if any(ex in title_lower for ex in exclude_words):
                                skip_cnt += 1
                                continue

                        # 가격 줄은 '원'이 포함된 첫 번째 문자열로 선택 (리스트 구조 변화에도 안전)
                        price_str = "가격없음"
                        for s in text_lines[1:]:
                            if "원" in s and any(ch.isdigit() for ch in s):
                                price_str = s
                                break
                        current_price = parse_price(price_str)

                        if min_price > 0 and current_price < min_price:
                            skip_cnt += 1; continue
                        if max_price > 0 and current_price > max_price:
                            skip_cnt += 1; continue
                        if "판매완료" in article.text or "예약중" in article.text or "거래완료" in article.text:
                            # 판매완료/예약중도 일관된 구조로 저장
                            if article_id not in sent_data:
                                sent_data[article_id] = {
                                    "price": current_price,
                                    "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                                }
                            continue

                        is_new = article_id not in sent_data
                        is_drop = False
                        old_price = -1

                        if not is_new:
                            old_price = get_saved_price(sent_data[article_id])
                            if old_price > 0 and current_price < old_price:
                                is_drop = True
                            else:
                                sent_data[article_id] = current_price
                                continue
                        
                        full_link = f"https://www.daangn.com{href}"
                        img_tag = article.find('img')
                        img_url = img_tag['src'].replace('s=300x300', 's=600x600') if img_tag else ""
                        hidden_link = f"<a href='{img_url}'>&#8205;</a>" if img_url else ""

                        # 텔레그램 & 매터모스트 메시지 각각 생성
                        if is_drop:
                            drop_cnt += 1
                            msg_tg = f"{hidden_link}🔻 <b>[{dong_name}] 가격인하!</b>\n📉 {format(old_price, ',')}원 ➡️ <b>{format(current_price, ',')}원</b>\n📦 {title}\n🔗 <a href='{full_link}'>바로이동</a>"
                            msg_mm = f"🔻 **[{dong_name}] 가격인하!**\n📉 {format(old_price, ',')}원 ➡️ **{format(current_price, ',')}원**\n📦 {title}\n🔗 [바로가기]({full_link})"
                        else:
                            new_cnt += 1
                            msg_tg = f"{hidden_link}🥕 <b>[{dong_name}] {keyword}</b>\n📦 <b>{title}</b>\n💰 {price_str}\n🔗 <a href='{full_link}'>바로이동</a>"
                            msg_mm = f"🥕 **[{dong_name}] {keyword}**\n📦 **{title}**\n💰 {price_str}\n🔗 [바로가기]({full_link})"
                        
                        # 이미지가 있으면 매터모스트에만 추가
                        if img_url:
                            msg_mm += f"\n\n---\n![매물사진]({img_url})"
                        
                        # 텔레그램 전송
                        tg_success = await send_telegram(msg_tg)
                        
                        # 매터모스트 전송
                        mm_success = False
                        try:
                            mm_success = bot.send('danggn3', msg_mm)
                            if mm_success is None:
                                mm_success = True
                            print(f" [MM{'OK' if mm_success else 'FAIL'}]", end='')
                        except Exception as e_mm:
                            print(f"[MM실패: {e_mm}]", end='')
                        
                        # 성공하면 실시간 저장 (시간 포함)
                        if tg_success or mm_success:
                            # 기존 키가 있다면 삭제 후 추가 (최신순 정렬)
                            if article_id in sent_data:
                                del sent_data[article_id]
                            
                            # [시간 추가] 현재 시간 기록
                            from datetime import datetime
                            current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                            
                            # 딕셔너리 값도 dict 로 변경 (가격 + 시간)
                            sent_data[article_id] = {
                                'price': current_price,
                                'time': current_time
                            }
                            
                            # 실시간 저장
                            save_sent_data(sent_data)
                            print(f" [SAVE:{current_time}]", end='')
                        
                        time.sleep(1)

                    except Exception: continue

                if skip_cnt > 0:
                    print(f"✨{new_cnt} 🔻{drop_cnt} (💸{skip_cnt})")
                else:
                    print(f"✨{new_cnt} 🔻{drop_cnt}")
                
                # 수동이라도 최소한의 딜레이는 유지 (차단 방지)
                time.sleep(random.uniform(1.0, 2.5))

            except Exception as e:
                print(f"❌ {e}")
                time.sleep(5) 

    save_sent_data(sent_data)
    print("\n✅ [완료] 수동 실행 끝.")

if __name__ == "__main__":
    asyncio.run(check_daangn_local())