#!/usr/bin/env python3
"""
MLink Backend Application
Flask 기반 백엔드 API 서버
"""

import os
import sys
import logging
from datetime import datetime, timedelta
from typing import Dict, Any

# 경로 설정 (중요: 순서 유지)
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))

from flask import Flask, send_from_directory, jsonify, abort
from flask_cors import CORS
from dotenv import load_dotenv
from src.config.security import SecurityConfig

# 로깅 설정 (환경 변수 로드 전에 기본 로깅 설정)
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[logging.StreamHandler(sys.stdout)]
)
logger = logging.getLogger(__name__)

# 환경 변수 로드
# .env 파일에서 ENVIRONMENT만 읽고, 해당 환경 파일(.env.development or .env.production) 로드
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
main_env_file = os.path.join(project_root, '.env')

if os.path.exists(main_env_file):
    # .env 파일에서 ENVIRONMENT만 읽기
    load_dotenv(main_env_file, override=False)
    environment = os.getenv('ENVIRONMENT', 'development').lower()
    
    # ENVIRONMENT 값에 따라 실제 환경 파일 선택
    if environment == 'production':
        env_file = os.path.join(project_root, '.env.production')
    else:
        env_file = os.path.join(project_root, '.env.development')
    
    # 선택된 환경 파일 로드
    if os.path.exists(env_file):
        load_dotenv(env_file, override=True)
        logger.info(f"환경: {environment}, 환경 변수 파일 로드: {env_file}")
    else:
        logger.warning(f"환경 변수 파일을 찾을 수 없음: {env_file}")
else:
    logger.warning(f"메인 환경 변수 파일을 찾을 수 없음: {main_env_file}, 기본 .env 파일 로드 시도")
    load_dotenv()

# Flask 앱 생성
app = Flask(__name__, static_folder=os.path.join(os.path.dirname(__file__), 'static'))
# 앱 설정
app.config.update({
    'SQLALCHEMY_DATABASE_URI': os.getenv('DATABASE_URL', 'mysql+pymysql://mlink_user:1111@dw.kdjsystem.com:3306/mlink_db'),
    'SQLALCHEMY_TRACK_MODIFICATIONS': False,
    'SECRET_KEY': os.getenv('SECRET_KEY', 'your-secret-key-here'),
    'CELERY_BROKER_URL': os.getenv('REDIS_URL', 'redis://localhost:6379/0'),
    'CELERY_RESULT_BACKEND': os.getenv('REDIS_URL', 'redis://localhost:6379/0'),
    'JWT_SECRET_KEY': os.getenv('JWT_SECRET_KEY', ''),
    # 파일 업로드 설정 (50MB 제한)
    'MAX_CONTENT_LENGTH': 50 * 1024 * 1024,  # 50MB
    # 세션 설정
    'PERMANENT_SESSION_LIFETIME': timedelta(hours=24),
    'SESSION_COOKIE_HTTPONLY': True,
    'SESSION_COOKIE_SAMESITE': 'Lax',
})

# CORS 설정
from src.config.env_loader import env

# 환경별 CORS 설정
if env.is_development:
    # 개발 환경 (PC)
    CORS(app, origins=[
        'http://localhost:3000', 
        'http://localhost:3001',
        'http://127.0.0.1:3000',
        'http://127.0.0.1:3001'
    ], supports_credentials=True)
else:
    # 프로덕션 환경 (서버)
    CORS(app, origins=[
        'https://mlink.sellmall.co.kr ',
        'https://sellmall.co.kr '
    ], supports_credentials=True)

# 새로운 보안 설정 적용
SecurityConfig.configure_cors(app)
SecurityConfig.configure_security_headers(app)

from src.models.db import db
db.init_app(app)

# 모델 임포트 (테이블 생성을 위해)
from src.models.user import User
from src.models.subscription import Product, Subscription, Payment, SubscriptionStatus, PaymentStatus
from src.models.post import Post, Comment
from src.models.chat import ChatRoom, ChatMessage, ChatParticipant
from src.models.sns import SnsPost, SnsLike, SnsComment, Follow
from src.models.download import DownloadFile, DownloadLog
from src.models.gif_feedback import GifFeedback, GifUsage  # GIF 피드백 모델 추가
from src.models.credit import CreditTransaction, CreditBalance, CreditType, EventCreditLot  # 크레딧 모델 추가

# Celery 앱 초기화
from src.celery_app import celery, init_celery_with_app
celery = init_celery_with_app(app)

# 블루프린트 등록
from src.routes.auth import auth_bp
from src.routes.subscription import subscription_bp
from src.routes.chatbot import chatbot_bp
from src.routes.chatbot import chatbot_bp2
from src.routes.post import post_bp
from src.routes.chat import chat_bp
from src.routes.sns import sns_bp
from src.routes.download import download_bp
from src.routes.feedback import feedback_bp
from src.routes.common import common_bp
from src.routes.payments_v2 import payments_v2_bp
from src.routes.portone_webhook import portone_webhook_bp
from src.routes.gif import gif_bp  # GIF 라우트 추가
from src.routes.user import user_bp  # 사용자 라우트 추가

BLUEPRINTS = [
    (post_bp, ''),
    (chat_bp, ''),
    (sns_bp, ''),
    (subscription_bp, ''),
    (download_bp, ''),
    (auth_bp, ''),
    (chatbot_bp, ''),
    (chatbot_bp2, ''),
    (feedback_bp, ''),
    (common_bp, ''),
    (payments_v2_bp, ''),
    (portone_webhook_bp, ''),
    (gif_bp, ''),  # GIF 라우트 추가
    (user_bp, ''),  # 사용자 라우트 추가
]

from src.config.deployment import filter_blueprints, is_server_role, is_local_role, MLINK_APP_ROLE

BLUEPRINTS = filter_blueprints(BLUEPRINTS)
if is_local_role():
    logger.warning(
        "MLINK_APP_ROLE=local: 인증/구독/결제 등 DB API 블루프린트는 비활성입니다. "
        "프론트는 Vite proxy(VITE_API_PROXY_TARGET)로 원격 서버 API를 사용하세요."
    )
else:
    logger.info("MLINK_APP_ROLE=%s — 전체 API 블루프린트 등록", MLINK_APP_ROLE)

# 초기 상품 데이터
INITIAL_PRODUCTS = [
    {
        'name': 'Free',
        'description': '무료 플랜',
        'price': 0,
        'currency': 'KRW',
        'billing_cycle': 'monthly',
        'trial_days': 0,
        'features': '["매일 300 갱신 크레딧 제공", "엠링크 기본 기능"]',
        'is_active': True
    },
    {
        'name': 'Basic',
        'description': '개인 셀러를 위한 기본 플랜',
        'price': 29000,
        'currency': 'KRW',
        'billing_cycle': 'monthly',
        'trial_days': 15,
        'features': '["매일 300 갱신 크레딧 제공", "매월 1,900 크레딧 자세히 알아보기", "매달 +1,900 추가 크레딧 제공 한정 제안", "엠링크 기본 기능", "엘림 기본 기능", "기본 고객지원"]',
        'is_active': True
    },
    {
        'name': 'Plus',
        'description': '성장하는 비즈니스를 위한 플랜',
        'price': 59000,
        'currency': 'KRW',
        'billing_cycle': 'monthly',
        'trial_days': 15,
        'features': '["매일 300 갱신 크레딧 제공", "매월 3,900 크레딧 자세히 알아보기", "매달 +3,900 추가 크레딧 제공 한정 제안", "엠링크 모든 기능", "엘림 고급 기능", "우선 고객지원", "자동화 스케쥴러", "상세 분석 리포트"]',
        'is_active': True
    },
    {
        'name': 'Pro',
        'description': '대규모 비즈니스를 위한 최고급 플랜',
        'price': 99000,
        'currency': 'KRW',
        'billing_cycle': 'monthly',
        'trial_days': 15,
        'features': '["매일 300 갱신 크레딧 제공", "매월 19,900 크레딧 자세히 알아보기", "매달 +19,900 추가 크레딧 제공 한정 제안", "엠링크 모든 기능 + 추가 기능", "엘림 프리미엄 기능", "PC2대 동시 작업", "전담 고객지원", "고급 자동화 기능", "AI 접근 권한", "맞춤형 분석 대시보드", "베타 기능 얼리 액세스"]',
        'is_active': True
    },
    # 연단위 상품들 (17% 할인 적용)
    {
        'name': 'Basic',
        'description': '개인 셀러를 위한 기본 플랜',
        'price': 288840,  # 29000 * 12 * 0.83
        'currency': 'KRW',
        'billing_cycle': 'yearly',
        'trial_days': 15,
        'features': '["매일 300 갱신 크레딧 제공", "매월 1,900 크레딧 자세히 알아보기", "매달 +1,900 추가 크레딧 제공 한정 제안", "엠링크 기본 기능", "엘림 기본 기능", "기본 고객지원"]',
        'is_active': True
    },
    {
        'name': 'Plus',
        'description': '성장하는 비즈니스를 위한 플랜',
        'price': 587640,  # 59000 * 12 * 0.83
        'currency': 'KRW',
        'billing_cycle': 'yearly',
        'trial_days': 15,
        'features': '["매일 300 갱신 크레딧 제공", "매월 3,900 크레딧 자세히 알아보기", "매달 +3,900 추가 크레딧 제공 한정 제안", "엠링크 모든 기능", "엘림 고급 기능", "우선 고객지원", "자동화 스케쥴러", "상세 분석 리포트"]',
        'is_active': True
    },
    {
        'name': 'Pro',
        'description': '대규모 비즈니스를 위한 최고급 플랜',
        'price': 986040,  # 99000 * 12 * 0.83
        'currency': 'KRW',
        'billing_cycle': 'yearly',
        'trial_days': 15,
        'features': '["매일 300 갱신 크레딧 제공", "매월 19,900 크레딧 자세히 알아보기", "매달 +19,900 추가 크레딧 제공 한정 제안", "엠링크 모든 기능 + 추가 기능", "엘림 프리미엄 기능", "PC2대 동시 작업", "전담 고객지원", "고급 자동화 기능", "AI 접근 권한", "맞춤형 분석 대시보드", "베타 기능 얼리 액세스"]',
        'is_active': True
    }
]

# 블루프린트 등록 (모든 API는 Blueprint 내부에서 관리)
for blueprint, url_prefix in BLUEPRINTS:
    app.register_blueprint(blueprint, url_prefix=url_prefix)

from src.config.logging_config import LoggingConfig, StructuredLogger   

# 413 에러 핸들러 (요청 크기 초과)
from werkzeug.exceptions import RequestEntityTooLarge

@app.errorhandler(RequestEntityTooLarge)
def handle_request_entity_too_large(e):
    """요청 크기 초과 에러 처리"""
    return jsonify({
        'error': '요청 크기가 너무 큽니다. 파일 크기를 줄이거나 파일 개수를 줄여주세요. (최대 50MB)'
    }), 413

# 헬스체크 엔드포인트
@app.route("/health")
def health_check():
    """헬스체크 엔드포인트"""
    return jsonify({"status": "healthy", "timestamp": datetime.now().isoformat()}), 200
# 로깅 설정 초기화
LoggingConfig.setup_logging()

# 구조화된 로거 생성
logger = StructuredLogger(__name__) 

# 정적 파일 서빙 (API 라우트 이후에 정의하여 우선순위 보장)
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def serve_static(path: str):
    """정적 파일 서빙 - API 경로는 제외"""
    # API 경로는 정적 파일로 처리하지 않음
    if path.startswith('api/'):
        # API 경로가 블루프린트에서 처리되지 않은 경우 404 반환
        abort(404, description="API endpoint not found")
    
    # 정적 파일이 존재하는 경우에만 서빙
    full_path = os.path.join(app.static_folder, path)
    if path and os.path.exists(full_path):
        return send_from_directory(app.static_folder, path)
    
    # SPA를 위한 index.html 서빙
    try:
        return app.send_static_file('index.html')
    except FileNotFoundError:
        abort(404, "index.html not found")

def initialize_database() -> None:
    """데이터베이스 초기화 및 초기 데이터 생성"""
    try:
        # 테이블 생성
        db.create_all()
        logger.info("✅ 데이터베이스 테이블 생성 완료")
        
        # 초기 상품 데이터 생성 (없는 경우에만)
        if not Product.query.first():
            for product_data in INITIAL_PRODUCTS:
                product = Product(**product_data)
                db.session.add(product)
            
            db.session.commit()
            logger.info("✅ 초기 상품 데이터 생성 완료")
        else:
            logger.info("ℹ️ 상품 데이터가 이미 존재합니다.")
            
    except Exception as e:
        logger.error(f"❌ 데이터베이스 초기화 실패: {e}")
        raise

# 외부 서비스 연결 예외 처리 (server 역할만 원격 DB 검증)
if is_server_role():
    try:
        from sqlalchemy import create_engine
        db_url = app.config['SQLALCHEMY_DATABASE_URI']
        engine = create_engine(db_url)
        conn = engine.connect()
        conn.close()
        logger.info(f"DB 연결 성공: {db_url}")
    except Exception as e:
        logger.error(f"DB 연결 실패: {e}")
else:
    logger.info("MLINK_APP_ROLE=local — DATABASE_URL 연결 테스트 생략")

try:
    # Redis 연결 테스트 (선택적)
    import redis
    redis_url = app.config['CELERY_BROKER_URL']
    r = redis.Redis.from_url(redis_url)
    r.ping()
    logger.info(f"Redis 연결 성공: {redis_url}")
except Exception as e:
    logger.warning(f"Redis 연결 실패 (선택적): {e}")
    logger.info("Redis 없이 애플리케이션을 계속 실행합니다.")

# API 단독 테스트용 기본 엔드포인트
@app.route("/api/test")
def api_test():
    return jsonify({"message": "API 정상 동작", "timestamp": datetime.now().isoformat()}), 200

# 볼륨 마운트 경로 점검 (환경별 .env에서 로드된 ENVIRONMENT 기준, OUTPUT_PATH로 덮어쓰기 가능)
output_path = os.getenv('OUTPUT_PATH')
if not output_path:
    if environment == 'production':
        output_path = '../../output_final0922'
    else:
        output_path = '/home/kdj-ubuntu1/output_final0922'
if not os.path.exists(output_path):
    logger.warning(f"output_final0922 경로가 존재하지 않습니다: {output_path}")
else:
    logger.info(f"output_final0922 경로 확인: {output_path}")

# 보안 설정 점검
if app.config['SECRET_KEY'] == 'your-secret-key-here':
    logger.warning("SECRET_KEY가 기본값입니다. 운영 환경에서는 반드시 변경하세요.")
if not app.config.get('JWT_SECRET_KEY'):
    logger.warning("JWT_SECRET_KEY가 설정되지 않았습니다.")

TOSS_API_KEY = os.environ.get("TOSS_API_KEY")
if is_server_role() and not TOSS_API_KEY:
    raise RuntimeError("TOSS_API_KEY 환경 변수가 필요합니다.")

def main():
    """메인 실행 함수"""
    # 개발 환경으로 설정
    # os.environ['ENVIRONMENT'] = 'production'
    try:
        logger.info("MLink AI 애플리케이션 시작", event_type='app_start')
        # 데이터베이스 초기화 (원장 API 서버 역할만)
        with app.app_context():
            if is_server_role():
                initialize_database()
                logger.info("데이터베이스 초기화 완료", event_type='db_init')
            else:
                logger.info("MLINK_APP_ROLE=local — initialize_database 생략", event_type='db_init_skipped')
        
        # 개발 서버 실행
        logger.info("서버 시작", host='0.0.0.0', port=8010, event_type='server_start')
        port = int(os.getenv('BACK_PORT', 8010))
        app.run(host='0.0.0.0', port=port, debug=True, use_reloader=False)
        
    except Exception as e:
        logger.error(f"애플리케이션 시작 실패: {e}", event_type='app_start_failed', error=str(e))
        sys.exit(1)

if __name__ == '__main__':
    main()

