import requests
import json
import pymysql
import os
import base64
from datetime import datetime, timedelta

# --- 스크립트 시작 ---
now1 = datetime.now()
print(f"\n--- 재고 동기화 시작: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ---")

# --- 설정 변수 ---
token_file = "/var/www/html/cafe24_stock_update/tokens.json"
mallid = "granceed"
client_id = "ULC335SqJNpocfOXSN1JlH"
client_secret = "VdcQ5RWfEKkDDKPy6TmQkF"
version = "2025-06-01"

# --- 함수 정의 (기존과 동일) ---
def load_tokens():
    try:
        with open(token_file, "r") as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        return {}

def save_tokens(tokens):
    with open(token_file, "w") as f:
        json.dump(tokens, f, indent=4)
    print("✅ 새 토큰이 파일에 저장되었습니다.")

def refresh_access_token():
    print("⏳ Access Token 갱신을 시작합니다...")
    tokens = load_tokens()
    refresh_token = tokens.get("refresh_token")
    if not refresh_token:
        print("오류: Refresh Token이 없습니다. 토큰을 재발급 받아야 합니다.")
        return None
    url = f"https://{mallid}.cafe24api.com/api/v2/oauth/token"
    auth_header = base64.b64encode(f"{client_id}:{client_secret}".encode("utf-8")).decode("utf-8")
    headers = {
        'Authorization': f'Basic {auth_header}',
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    payload = {
        "grant_type": "refresh_token",
        "refresh_token": refresh_token
    }
    try:
        response = requests.post(url, headers=headers, data=payload)
        response.raise_for_status()
        new_tokens = response.json()
        expires_in = new_tokens.get("expires_in", 3600)
        new_tokens["expires_at"] = (datetime.utcnow() + timedelta(seconds=expires_in)).isoformat()
        save_tokens(new_tokens)
        return new_tokens
    except requests.exceptions.RequestException as e:
        print(f"오류: 토큰 갱신 중 에러 발생: {e}")
        return None

def api_request(method, url, headers, params=None, json_payload=None):
    """(json 대신 json_payload 사용)"""
    try:
        response = requests.request(method, url, headers=headers, params=params, json=json_payload)
        if response.status_code == 401:
            print("Access Token이 만료되었습니다. 갱신을 시도합니다.")
            new_tokens = refresh_access_token()
            if new_tokens:
                headers['Authorization'] = f"Bearer {new_tokens['access_token']}"
                print("새로운 토큰으로 API 요청을 재시도합니다.")
                response = requests.request(method, url, headers=headers, params=params, json=json_payload)
            else:
                print("오류: 토큰 갱신에 실패하여 API 요청을 중단합니다.")
                return None
        response.raise_for_status()
        # 내용이 없는 성공 응답(e.g., 204 No Content) 처리
        if response.status_code == 204:
            return {}
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"API 요청 중 에러 발생: {e}")
        return None

# --- 메인 로직 시작 ---

# 1. 모든 상품 목록 가져오기 (페이지네이션 적용)
tokens = load_tokens()
if "access_token" not in tokens:
    print("오류: access_token이 없습니다. 스크립트를 종료합니다.")
    exit()

access_token = tokens["access_token"]
product_list_url = f"https://{mallid}.cafe24api.com/api/v2/admin/products"
headers = {
    'Authorization': f"Bearer {access_token}",
    'Content-Type': "application/json",
    'X-Cafe24-Api-Version': version
}

product_info_list = []
limit = 100
offset = 0

print("🔍 전체 상품 목록 조회를 시작합니다 (페이지네이션 적용)...")
while True:
    params = {'limit': limit, 'offset': offset}
    response_data = api_request("GET", product_list_url, headers=headers, params=params)

    if not response_data or 'products' not in response_data:
        print("오류: 상품 목록을 가져오지 못했습니다. 조회를 중단합니다.")
        break

    products_on_page = response_data['products']
    if not products_on_page:
        print("✅ 모든 상품 페이지 조회를 완료했습니다.")
        break

    # `custom_product_code`가 있는 상품만 필터링하여 리스트에 추가
    for product in products_on_page:
        if product.get('custom_product_code'):
            product_info_list.append({
                'product_no': product.get('product_no'),
                'custom_product_code': product.get('custom_product_code')
            })

    print(f"  - {offset + len(products_on_page)}번 상품까지 스캔 완료...")
    offset += limit

print(f"총 {len(product_info_list)}개의 상품을 대상으로 재고 동기화를 시작합니다.")


# 2. 각 상품의 재고 업데이트 (DB 연결 최적화 및 보안 강화)
db_connection = None
try:
    # DB에 한 번만 연결
    db_connection = pymysql.connect(host='119.205.233.11', user='changgi', password='vkdnjgus13', db='versatile', charset='utf8')

    for product_info in product_info_list:
        product_no = product_info['product_no']
        custom_product_code = product_info['custom_product_code']

        # 재고 규칙 추출 (오류 방지)
        try:
            stock_law = custom_product_code.split('_')[1]
        except IndexError:
            print(f"⚠️ 경고: 상품번호 {product_no}의 custom_product_code ('{custom_product_code}') 형식이 잘못되었습니다. 건너뜁니다.")
            continue

        print(f"\n--- 상품번호: {product_no} (규칙: {stock_law}) 처리 중 ---")
        variants_url = f"https://{mallid}.cafe24api.com/api/v2/admin/products/{product_no}/variants"
        variants_data = api_request("GET", variants_url, headers=headers)

        if not variants_data or 'variants' not in variants_data:
            print(f"⚠️ 경고: 상품번호 {product_no}의 옵션 정보를 가져올 수 없습니다. 건너뜁니다.")
            continue

        requests_list = []
        with db_connection.cursor() as cur:
            for option in variants_data['variants']:
                option_variant_code = option.get('variant_code')
                option_custom_code = option.get('custom_variant_code')

                if not option_custom_code:
                    print(f"  - 경고: 옵션({option_variant_code})에 자체품목코드가 없어 건너뜁니다.")
                    continue

                if stock_law == "본사재고반영":
                    sql = "SELECT IFNULL(inven_1, 0) + IFNULL(inven_2, 0) as total_stock FROM `product_table` WHERE `p_suppler_code` = %s;"
                elif stock_law == "통제반영":
                    sql = "SELECT IFNULL(inven_1, 0) + IFNULL(inven_7, 0) + IFNULL(inven_2, 0) as total_stock FROM `product_table` WHERE `p_suppler_code` = %s;"
                elif stock_law == "사입":
                    sql = "SELECT IFNULL(inven_2, 0) as total_stock FROM `product_table` WHERE `p_suppler_code` = %s;"
                else:
                    print(f"  - 경고: 알 수 없는 재고 규칙 '{stock_law}' 입니다. 이 옵션을 건너뜁니다.")
                    continue

                # SQL Injection 방지를 위한 파라미터화된 쿼리 실행
                cur.execute(sql, (option_custom_code,))
                row = cur.fetchone()

                quantity = 0
                if row and row[0] is not None:
                    quantity = int(max(0, row[0]))
                else:
                    print(f"  - 경고: DB에서 '{option_custom_code}' 재고를 찾지 못해 0으로 처리합니다.")

                requests_list.append({
                    "variant_code": option_variant_code,
                    "quantity": quantity
                })

        # 재고 업데이트 API 일괄 호출
        if requests_list:
            update_payload = {"requests": requests_list}
            update_response = api_request("PUT", variants_url, headers=headers, json_payload=update_payload)
            if update_response is not None:
                print(f"  => 🚀 상품번호 {product_no} 재고 업데이트 완료!")
            else:
                print(f"  => ❌ 상품번호 {product_no} 재고 업데이트 실패.")

except pymysql.MySQLError as e:
    print(f"!! 데이터베이스 오류 발생: {e}")
except Exception as e:
    print(f"!! 알 수 없는 치명적 오류 발생: {e}")
finally:
    # 모든 작업 완료 후 DB 연결 해제
    if db_connection:
        db_connection.close()
        print("\n데이터베이스 연결이 해제되었습니다.")


# --- 스크립트 종료 ---
now2 = datetime.now()
duration = now2 - now1
print(f"\n--- 재고 동기화 종료: {now2.strftime('%Y-%m-%d %H:%M:%S')} ---")
print(f"--- 총 소요 시간: {duration} ---")
