"""
QR Claim Token 생성 모듈
후향적 적립을 위한 1회성 토큰 생성
"""

import hashlib
import secrets
from datetime import datetime, timedelta
import sys
import os

# DB 연결
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from db.dbsetup import DatabaseManager

# 설정값
MILEAGE_RATE = 0.03  # 3% 적립
TOKEN_EXPIRY_DAYS = 30  # 30일 유효기간
QR_BASE_URL = "https://mile.0bin.in/claim"


def generate_claim_token(transaction_id, total_amount, pharmacy_id="YANGGU001"):
    """
    Claim Token 생성 (SHA256 해시 기반)

    Args:
        transaction_id (str): POS 거래 ID (SALE_MAIN.SL_NO_order)
        total_amount (float): 총 판매 금액
        pharmacy_id (str): 약국 코드

    Returns:
        dict: {
            'qr_url': QR 코드 URL,
            'token_raw': 토큰 원문 (QR에만 포함),
            'token_hash': SHA256 해시 (DB 저장용),
            'claimable_points': 적립 가능 포인트,
            'expires_at': 만료일시
        }

    보안 정책:
    - token_raw는 DB에 저장하지 않음 (QR 코드에만 포함)
    - token_hash만 DB 저장 (SHA256, 64자)
    - nonce 사용으로 동일 거래도 매번 다른 토큰

    URL 최적화:
    - nonce: 64자 -> 12자로 단축 (6바이트 = 충분한 보안)
    - 타임스탬프: 제거 (DB에만 저장)
    - 결과: 약 80자 -> 빠른 QR 인식
    """
    # 1. 랜덤 nonce 생성 (6바이트 = 12자 hex) - 대폭 단축!
    nonce = secrets.token_hex(6)

    # 2. 타임스탬프 (DB 저장용)
    timestamp = datetime.now().isoformat()

    # 3. 토큰 원문 생성 (검증용 - DB 저장 X)
    token_raw = f"{transaction_id}:{nonce}:{timestamp}"

    # 4. SHA256 해시 생성 (DB 저장용)
    token_hash = hashlib.sha256(token_raw.encode('utf-8')).hexdigest()

    # 5. QR URL 생성 (짧게!) - 타임스탬프 제외
    qr_token_short = f"{transaction_id}:{nonce}"
    qr_url = f"{QR_BASE_URL}?t={qr_token_short}"

    # 6. 적립 포인트 계산
    claimable_points = calculate_claimable_points(total_amount)

    # 7. 만료일 계산 (30일 후)
    expires_at = datetime.now() + timedelta(days=TOKEN_EXPIRY_DAYS)

    return {
        'qr_url': qr_url,
        'token_raw': token_raw,  # 전체 토큰 (해시 생성용)
        'token_hash': token_hash,
        'claimable_points': claimable_points,
        'expires_at': expires_at,
        'pharmacy_id': pharmacy_id,
        'transaction_id': transaction_id,
        'total_amount': total_amount
    }


def calculate_claimable_points(total_amount):
    """
    적립 포인트 계산 (3% 정책)

    Args:
        total_amount (float): 판매 금액

    Returns:
        int: 적립 포인트 (정수, 소수점 절사)
    """
    return int(total_amount * MILEAGE_RATE)


def save_token_to_db(transaction_id, token_hash, total_amount, claimable_points,
                     expires_at, pharmacy_id):
    """
    생성된 토큰을 SQLite DB에 저장

    Args:
        transaction_id (str): 거래 ID
        token_hash (str): SHA256 해시
        total_amount (float): 판매 금액
        claimable_points (int): 적립 포인트
        expires_at (datetime): 만료일시
        pharmacy_id (str): 약국 코드

    Returns:
        tuple: (성공 여부, 에러 메시지 or None)

    중복 방지:
    - transaction_id가 이미 존재하면 실패
    - token_hash가 이미 존재하면 실패 (UNIQUE 제약)
    """
    try:
        from db.dbsetup import db_manager as _db_manager
        conn = _db_manager.get_sqlite_connection()
        cursor = conn.cursor()

        # 중복 체크 (transaction_id)
        cursor.execute("""
            SELECT id FROM claim_tokens WHERE transaction_id = ?
        """, (transaction_id,))

        if cursor.fetchone():
            return (False, f"이미 QR이 생성된 거래입니다: {transaction_id}")

        # INSERT
        cursor.execute("""
            INSERT INTO claim_tokens (
                transaction_id, pharmacy_id, token_hash,
                total_amount, claimable_points, expires_at
            ) VALUES (?, ?, ?, ?, ?, ?)
        """, (
            transaction_id,
            pharmacy_id,
            token_hash,
            int(total_amount),  # INTEGER 타입
            claimable_points,
            expires_at.strftime('%Y-%m-%d %H:%M:%S')
        ))

        conn.commit()

        return (True, None)

    except Exception as e:
        return (False, f"DB 저장 실패: {str(e)}")


# 테스트 코드
if __name__ == "__main__":
    # 테스트
    test_tx_id = "20251024000042"
    test_amount = 50000.0

    print("=" * 80)
    print("Claim Token 생성 테스트")
    print("=" * 80)

    token_info = generate_claim_token(test_tx_id, test_amount)

    print(f"거래 ID: {test_tx_id}")
    print(f"판매 금액: {test_amount:,}원")
    print(f"적립 포인트: {token_info['claimable_points']}P")
    print(f"토큰 원문: {token_info['token_raw'][:80]}...")
    print(f"토큰 해시: {token_info['token_hash']}")
    print(f"QR URL: {token_info['qr_url'][:80]}...")
    print(f"만료일: {token_info['expires_at']}")
    print("=" * 80)

    # DB 저장 테스트
    print("\nDB 저장 테스트...")
    success, error = save_token_to_db(
        test_tx_id,
        token_info['token_hash'],
        test_amount,
        token_info['claimable_points'],
        token_info['expires_at'],
        token_info['pharmacy_id']
    )

    if success:
        print("[OK] DB 저장 성공")
        print(f"\nSQLite DB 경로: backend/db/mileage.db")
        print("다음 명령으로 확인:")
        print("  sqlite3 backend/db/mileage.db \"SELECT * FROM claim_tokens\"")
    else:
        print(f"[ERROR] {error}")
