import random
import requests
import json
import os
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from openai import OpenAI


load_dotenv(dotenv_path='/var/www/.env')
app = FastAPI(title="Choboyeo AI Fixed-Start 3-Way Scan Engine with Road Risk Analytics")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 🔑 API 인증 키 설정
NAVER_CLIENT_ID = "a5ipux8gmz"
NAVER_CLIENT_SECRET = os.getenv("NAVER_CLIENT_SECRET")
KOROAD_AUTH_KEY = os.getenv("KOROAD_AUTH_KEY")

# 🔮 OpenAI 클라이언트 초기화
openai_client = OpenAI(api_key=os.getenv("OpenAI"))

# 📡 AI.php 서버 주소
AI_PHP_URL = "http://34.22.87.81/choboyeo/PHP/AI.php"  # ← 실제 주소로 교체

class ZoneRequest(BaseModel):
    zone_name: str
    city_name: str  # ← 추가


def fetch_coords_from_php(zone_name: str, city_name: str) -> dict:
    """AI.php에서 zone_name, city_name에 해당하는 좌표값을 받아옵니다."""
    try:
        res = requests.post(
            AI_PHP_URL,
            json={"zone_name": zone_name, "city_name": city_name},
            timeout=5
        )
        data = res.json()
        if not data.get("success"):
            raise ValueError(data.get("message", "AI.php 응답 오류"))
        return {
            "zone_coords": data["zone"]["coords"],  # min_lat, max_lat, min_lng, max_lng
            "city_coords": data["city"]["coords"],
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"AI.php 좌표 조회 실패: {e}")


def get_road_risk_score(lat: float, lng: float) -> float:
    """ 도로교통공단 오픈 API를 활용하여 지정된 좌표 반경 500m의 실시간 도로위험지수를 측정합니다. """
    lng_offset = 0.0025
    start_lng = lng - lng_offset
    end_lng = lng + lng_offset
    
    line_string = f"LineString({start_lng} {lat},{end_lng} {lat})"
    encoded_line = line_string.replace(" ", "%20").replace(",", "%2C")
    
    api_url = (
        f"https://opendata.koroad.or.kr/data/rest/road/dgdgr/link"
        f"?authKey={KOROAD_AUTH_KEY}"
        f"&searchLineString={encoded_line}"
        f"&vhctyCd=01&type=json"
    )
    try:
        res = requests.get(api_url, timeout=3)
        if res.status_code == 200:
            data = res.json()
            if data.get('resultCode') == "00" and data.get('items', {}).get('item'):
                return float(data['items']['item'][0].get('anals_value', 0.0))
    except Exception as e:
        print(f"⚠️ 도로위험지수 Open API 호출 실패 (서행 패스): {e}")
    return 0.0


def generate_risk_description(risk_score: float, context_guide: str) -> str:
    """ 공단 위험 점수와 네이버 내비 안내 문구를 융합하여 초보자용 안심 행동 지침 생성 """
    prompt = f"""
    당신은 친절하고 신뢰감 있는 초보 운전 전문 도로연수 강사입니다. 
    초보 운전자가 내비게이션 지도에서 '위험 스팟' 마커를 터치했을 때 보여줄 핵심 브리핑을 딱 1문장(50자 내외)으로 명확하게 작성해주세요.

    [도로 데이터]
    - 도로위험지수: {risk_score}점 (높을수록 복잡하고 사고율이 높은 구간)
    - 해당 지점 주변 안내문: "{context_guide}"

    [작성 규칙]
    1. 반드시 "~구간이므로 ~에 주의하세요" 또는 "~구역이라 위험도가 높으니 ~하세요" 등의 부드러운 행동 지침 포맷으로 완성할 것.
    2. "합류 차선", "비보호 우회전", "사각지대", "급감속", "꼬리물기" 등 초보자가 즉각 이해할 수 있는 직관적인 운전 어휘를 선택할 것.
    3. 주변 안내문 내용과 위험 점수를 인과관계로 매끄럽게 엮어서 그럴듯한 사유를 추론하여 조언할 것.
    """

    try:
        response = openai_client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": prompt}],
            max_tokens=150,
            temperature=0.6
        )
        return response.choices[0].message.content.strip()
    except Exception as e:
        print(f"⚠️ OpenAI API 호출 실패: {e}")
        return "차량 교차 및 유동적 변수가 존재하는 구역입니다. 전방을 주시하며 속도를 줄이세요."


def sample_route_risk_points(path_coords: list, guide_texts: list) -> list:
    """ 
    전체 경로 좌표 데이터 리스트에서 등간격으로 10개의 지점을 샘플링합니다.
    위험지수가 10점 이상인 지점만 OpenAI 분석 엔진을 가동합니다.
    """
    total_points = len(path_coords)
    if total_points < 10:
        return []
        
    total_guides = len(guide_texts)
    sampled_points = []

    for i in range(10):
        idx = int(i * (total_points - 1) / 9)
        lng, lat = path_coords[idx]
        
        guide_idx = int(i * (total_guides - 1) / 9) if total_guides > 0 else 0
        current_guide = guide_texts[guide_idx] if total_guides > 0 else "직진 구간 주행"

        risk_score = get_road_risk_score(lat, lng)
        
        if risk_score >= 10.0:
            ai_desc = generate_risk_description(risk_score, current_guide)
        else:
            ai_desc = None

        sampled_points.append({
            "index": i + 1,
            "lat": lat,
            "lng": lng,
            "risk_score": risk_score,
            "desc": ai_desc
        })
    return sampled_points


@app.post("/api/analyze-zone")
def analyze_zone_loop(req: ZoneRequest):
    zone = req.zone_name
    city = req.city_name
    print(f"\n🎲 [AI 위험분석 융합 엔진] 출발지: {zone} ➡️ 목적지: {city} 전역 (장거리 & 위험도 연산 시작)")

    # ✅ AI.php에서 동적으로 좌표 수신 (하드코딩 대체)
    coords = fetch_coords_from_php(zone, city)
    start_bounds = coords["zone_coords"]   # min_lat, max_lat, min_lng, max_lng
    goal_bounds  = coords["city_coords"]

    scored_routes = []
    
    naver_url = "https://maps.apigw.ntruss.com/map-direction-15/v1/driving"
    headers = {
        "X-NCP-APIGW-API-KEY-ID": NAVER_CLIENT_ID,
        "X-NCP-APIGW-API-KEY": NAVER_CLIENT_SECRET
    }
    
    DANGER_KEYWORDS = {
        "HIGH": {"words": ["비보호", "유턴", "합류", "연속"], "penalty": 50},
        "MEDIUM": {"words": ["로터리", "회전교차로", "고가도로", "지하차도"], "penalty": 30},
        "LOW": {"words": ["이름 없는 도로", "급커브"], "penalty": 15}
    }
    
    for i in range(15):
        start_lat = random.uniform(start_bounds["min_lat"], start_bounds["max_lat"])
        start_lng = random.uniform(start_bounds["min_lng"], start_bounds["max_lng"])
        goal_lat = random.uniform(goal_bounds["min_lat"], goal_bounds["max_lat"])
        goal_lng = random.uniform(goal_bounds["min_lng"], goal_bounds["max_lng"])
        
        params = {"start": f"{start_lng},{start_lat}", "goal": f"{goal_lng},{goal_lat}", "option": "trafast"}
        
        try:
            res = requests.get(naver_url, headers=headers, params=params)
            res_data = res.json()
            
            if res_data.get("code") != 0 or "route" not in res_data:
                continue
                
            route_data = res_data["route"]["trafast"][0]
            summary = route_data.get("summary", {})
            distance_km = summary.get("distance", 0) / 1000.0
            duration_minutes = summary.get("duration", 0) / 60000.0
            
            if distance_km < 5.0 or distance_km > 15.0:
                continue
                
            path = route_data.get("path", [])
            guides = route_data.get("guide", [])
            guide_texts = [g.get("instructions", "") for g in guides]
            full_guide_string = " ".join(guide_texts)
            
            score = 300
            detected_penalties = []
            
            for level, data in DANGER_KEYWORDS.items():
                for word in data["words"]:
                    if word in full_guide_string:
                        score -= data["penalty"]
                        detected_penalties.append(word)
                        
            speed = 35.0
            if duration_minutes > 0:
                speed = (distance_km / (duration_minutes / 60.0))
                
            if speed < 25.0:
                score -= 40
            elif speed > 45.0:
                score += 30
                
            scored_routes.append({
                "path": path,
                "guide": guide_texts,
                "score": int(score),
                "distance": distance_km,
                "speed": speed,
                "penalties": list(set(detected_penalties))
            })
        except Exception:
            continue
            
    scored_routes.sort(key=lambda x: (x["score"], -x["distance"]), reverse=True)
    
    result_routes = {}
    MAX_SCORE = 300
    
    if len(scored_routes) > 0:
        top = scored_routes[0]
        print(" -> [분석 중] 1위 코스 실시간 10개 지점 도로위험지수 및 AI 가이드 연산...")
        risk_points = sample_route_risk_points(top["path"], top["guide"])
        p_msg = " (주의 요소 없음)" if not top["penalties"] else f" (기피 요소: {', '.join(top['penalties'])} 우회 판단)"
        result_routes["traoptimal"] = {
            "name": f"🏆 안심 1위 트랙 ({top['distance']:.1f}km)",
            "path": top["path"],
            "guide": top["guide"],
            "score": min(top["score"], MAX_SCORE),
            "risk_points": risk_points,
            "reason": f"✨ {_current_zone_name_clean(zone)}에서 안전하게 출발하여 {city} 외곽으로 뻗어 나가는 {top['distance']:.1f}km 안심 연수 코스입니다. 시속 {top['speed']:.1f}km/h로 쾌적하게 롱-런 직진 연습이 가능합니다.{p_msg}"
        }
        
    if len(scored_routes) > 1:
        sub = scored_routes[1]
        print(" -> [분석 중] 2위 코스 실시간 10개 지점 도로위험지수 및 AI 가이드 연산...")
        risk_points = sample_route_risk_points(sub["path"], sub["guide"])
        result_routes["tracomfort"] = {
            "name": f"🥈 안심 2위 트랙 ({sub['distance']:.1f}km)",
            "path": sub["path"],
            "guide": sub["guide"],
            "score": min(sub["score"], MAX_SCORE),
            "risk_points": risk_points,
            "reason": f"🚗 출발지 주변 복잡한 상습 정체 차선을 피해 설계된 {sub['distance']:.1f}km 대안 루트입니다. 광역 도로 적응 훈련에 최적화되어 있습니다."
        }
        
    if len(scored_routes) > 2:
        third = scored_routes[2]
        print(" -> [분석 중] 3위 코스 실시간 10개 지점 도로위험지수 및 AI 가이드 연산...")
        risk_points = sample_route_risk_points(third["path"], third["guide"])
        result_routes["trafast"] = {
            "name": f"🥉 안심 도전 트랙 ({third['distance']:.1f}km)",
            "path": third["path"],
            "guide": third["guide"],
            "score": min(third["score"], MAX_SCORE),
            "risk_points": risk_points,
            "reason": f"⏱️ 거리가 {third['distance']:.1f}km로 길게 뻗어 있으며, 연속 직진 차선 유지와 타이밍에 맞춘 부드러운 차선 변경을 본격적으로 연습하기 좋습니다."
        }
        
    if not result_routes:
        raise HTTPException(status_code=500, detail="조건에 맞는 경로 연산 실패")
        
    print(f"[AI 융합 분석 최종 완료] 3대 코스 및 각 10구역 위험 스코어링 결합 데이터 반환.")
    return {"success": True, "routes": result_routes}


def _current_zone_name_clean(zone):
    return zone


if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main3:app", host="0.0.0.0", port=8000, reload=True)
