#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
밍글 스튜디오 웹사이트 개발 서버
Python 기반 HTTP 서버로 개발, 테스트 및 API 지원
"""

import http.server
import socketserver
import webbrowser
import os
import sys
import socket
import json
from pathlib import Path
from datetime import datetime
from urllib.parse import parse_qs, urlparse

# 한글 출력을 위한 인코딩 설정
if os.name == 'nt':  # Windows
    import locale
    import codecs
    # UTF-8 강제 설정
    try:
        sys.stdout.reconfigure(encoding='utf-8')
        sys.stderr.reconfigure(encoding='utf-8')
    except:
        # Python 3.6 이하 버전 호환
        sys.stdout = codecs.getwriter('utf-8')(sys.stdout.detach())
        sys.stderr = codecs.getwriter('utf-8')(sys.stderr.detach())

# 서버 설정
PORT = 8001
HOST = '0.0.0.0'  # 모든 인터페이스에서 접근 가능

class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
    """커스텀 HTTP 요청 핸들러"""

    # API 데이터 파일 경로
    BACKGROUNDS_DATA_FILE = 'data/backgrounds.json'
    PROPS_DATA_FILE = 'data/props.json'

    def end_headers(self):
        # CORS 헤더 추가 (개발용)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
        self.send_header('Access-Control-Allow-Headers', 'Content-Type, X-API-Key')
        # 캐시 비활성화 (개발용)
        self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
        self.send_header('Pragma', 'no-cache')
        self.send_header('Expires', '0')
        super().end_headers()

    def do_OPTIONS(self):
        """OPTIONS 요청 처리 (CORS preflight)"""
        self.send_response(200)
        self.end_headers()

    def do_GET(self):
        """GET 요청 처리"""
        parsed_path = urlparse(self.path)

        # API 엔드포인트: GET /api/backgrounds
        if parsed_path.path == '/api/backgrounds':
            self.handle_get_backgrounds()
            return

        # API 엔드포인트: GET /api/props
        if parsed_path.path == '/api/props':
            self.handle_get_props()
            return

        # 기본 파일 처리
        if self.path == '/':
            self.path = '/index.html'

        # .html 확장자 없이 접근 시 자동으로 .html 추가
        elif not self.path.endswith('/') and '.' not in os.path.basename(self.path):
            html_path = self.path + '.html'
            if os.path.exists(html_path.lstrip('/')):
                self.path = html_path

        super().do_GET()

    def do_POST(self):
        """POST 요청 처리"""
        parsed_path = urlparse(self.path)

        # API 엔드포인트: POST /api/backgrounds
        if parsed_path.path == '/api/backgrounds':
            self.handle_post_backgrounds()
            return

        # API 엔드포인트: POST /api/props
        if parsed_path.path == '/api/props':
            self.handle_post_props()
            return

        # 그 외 POST 요청은 405 반환
        self.send_error(405, 'Method Not Allowed')

    def handle_get_backgrounds(self):
        """배경 데이터 조회 API"""
        try:
            if os.path.exists(self.BACKGROUNDS_DATA_FILE):
                with open(self.BACKGROUNDS_DATA_FILE, 'r', encoding='utf-8') as f:
                    data = json.load(f)
            else:
                data = {
                    'lastUpdated': datetime.now().isoformat(),
                    'backgrounds': []
                }

            self.send_response(200)
            self.send_header('Content-Type', 'application/json; charset=utf-8')
            self.end_headers()
            self.wfile.write(json.dumps(data, ensure_ascii=False, indent=2).encode('utf-8'))

        except Exception as e:
            self.send_error_json(500, f'데이터 조회 실패: {str(e)}')

    def handle_post_backgrounds(self):
        """배경 데이터 업데이트 API"""
        try:
            # Content-Length 확인
            content_length = int(self.headers.get('Content-Length', 0))
            if content_length == 0:
                self.send_error_json(400, '요청 본문이 비어있습니다')
                return

            # JSON 데이터 파싱
            post_data = self.rfile.read(content_length)
            try:
                data = json.loads(post_data.decode('utf-8'))
            except json.JSONDecodeError as e:
                self.send_error_json(400, f'잘못된 JSON 형식: {str(e)}')
                return

            # 데이터 유효성 검사
            if 'backgrounds' not in data:
                self.send_error_json(400, 'backgrounds 필드가 필요합니다')
                return

            # lastUpdated 자동 설정
            data['lastUpdated'] = datetime.now().isoformat()

            # 데이터 디렉토리 확인 및 생성
            data_dir = os.path.dirname(self.BACKGROUNDS_DATA_FILE)
            if data_dir and not os.path.exists(data_dir):
                os.makedirs(data_dir)

            # 파일 저장
            with open(self.BACKGROUNDS_DATA_FILE, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)

            # 성공 응답
            response = {
                'success': True,
                'message': f'{len(data["backgrounds"])}개의 배경이 저장되었습니다',
                'lastUpdated': data['lastUpdated'],
                'count': len(data['backgrounds'])
            }

            self.send_response(200)
            self.send_header('Content-Type', 'application/json; charset=utf-8')
            self.end_headers()
            self.wfile.write(json.dumps(response, ensure_ascii=False).encode('utf-8'))

            print(f"[API] 배경 데이터 업데이트: {len(data['backgrounds'])}개 항목")

        except Exception as e:
            self.send_error_json(500, f'데이터 저장 실패: {str(e)}')

    def handle_get_props(self):
        """프랍 데이터 조회 API"""
        try:
            if os.path.exists(self.PROPS_DATA_FILE):
                with open(self.PROPS_DATA_FILE, 'r', encoding='utf-8') as f:
                    data = json.load(f)
            else:
                data = {
                    'lastUpdated': datetime.now().isoformat(),
                    'props': []
                }

            self.send_response(200)
            self.send_header('Content-Type', 'application/json; charset=utf-8')
            self.end_headers()
            self.wfile.write(json.dumps(data, ensure_ascii=False, indent=2).encode('utf-8'))

        except Exception as e:
            self.send_error_json(500, f'데이터 조회 실패: {str(e)}')

    def handle_post_props(self):
        """프랍 데이터 업데이트 API"""
        try:
            # Content-Length 확인
            content_length = int(self.headers.get('Content-Length', 0))
            if content_length == 0:
                self.send_error_json(400, '요청 본문이 비어있습니다')
                return

            # JSON 데이터 파싱
            post_data = self.rfile.read(content_length)
            try:
                data = json.loads(post_data.decode('utf-8'))
            except json.JSONDecodeError as e:
                self.send_error_json(400, f'잘못된 JSON 형식: {str(e)}')
                return

            # 데이터 유효성 검사
            if 'props' not in data:
                self.send_error_json(400, 'props 필드가 필요합니다')
                return

            # lastUpdated 자동 설정
            data['lastUpdated'] = datetime.now().isoformat()

            # 데이터 디렉토리 확인 및 생성
            data_dir = os.path.dirname(self.PROPS_DATA_FILE)
            if data_dir and not os.path.exists(data_dir):
                os.makedirs(data_dir)

            # 파일 저장
            with open(self.PROPS_DATA_FILE, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)

            # 성공 응답
            response = {
                'success': True,
                'message': f'{len(data["props"])}개의 프랍이 저장되었습니다',
                'lastUpdated': data['lastUpdated'],
                'count': len(data['props'])
            }

            self.send_response(200)
            self.send_header('Content-Type', 'application/json; charset=utf-8')
            self.end_headers()
            self.wfile.write(json.dumps(response, ensure_ascii=False).encode('utf-8'))

            print(f"[API] 프랍 데이터 업데이트: {len(data['props'])}개 항목")

        except Exception as e:
            self.send_error_json(500, f'데이터 저장 실패: {str(e)}')

    def send_error_json(self, code, message):
        """JSON 형식 에러 응답"""
        self.send_response(code)
        self.send_header('Content-Type', 'application/json; charset=utf-8')
        self.end_headers()
        error_response = {
            'success': False,
            'error': message
        }
        self.wfile.write(json.dumps(error_response, ensure_ascii=False).encode('utf-8'))

    def log_message(self, format, *args):
        """로그 메시지 포맷팅"""
        print(f"[{self.log_date_time_string()}] {format % args}")

def find_available_port(start_port=8001):
    """사용 가능한 포트 찾기"""
    for port in range(start_port, start_port + 20):  # 더 많은 포트 시도
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 주소 재사용
            sock.bind(('localhost', port))  # 실제로 바인드 테스트
            sock.close()
            print(f"Available port found: {port}")
            return port  # 성공하면 해당 포트 반환
        except OSError:
            continue  # 포트가 사용 중이면 다음 포트 시도
    
    print(f"No available port found in range {start_port}-{start_port+19}")
    return start_port  # 찾지 못하면 기본값 반환

def create_self_signed_cert():
    """자체 서명 인증서 생성"""
    cert_file = 'server.crt'
    key_file = 'server.key'

    # 인증서가 이미 존재하면 재사용
    if os.path.exists(cert_file) and os.path.exists(key_file):
        print(f"Using existing SSL certificate: {cert_file}")
        return cert_file, key_file

    print("Generating self-signed SSL certificate...")
    try:
        import subprocess
        # OpenSSL을 사용하여 자체 서명 인증서 생성
        subprocess.run([
            'openssl', 'req', '-x509', '-newkey', 'rsa:4096',
            '-keyout', key_file, '-out', cert_file,
            '-days', '365', '-nodes',
            '-subj', '/CN=localhost'
        ], check=True, capture_output=True)
        print(f"SSL certificate created: {cert_file}, {key_file}")
        return cert_file, key_file
    except (subprocess.CalledProcessError, FileNotFoundError):
        print("Warning: OpenSSL not found. Cannot create SSL certificate.")
        print("Install OpenSSL or use the caddy server for HTTPS.")
        return None, None

def main():
    """메인 서버 실행 함수"""
    # 현재 디렉토리가 프로젝트 루트인지 확인
    if not os.path.exists('index.html'):
        print("Error: index.html file not found.")
        print("Please run from project root directory.")
        sys.exit(1)

    # 사용 가능한 포트 찾기
    available_port = find_available_port(PORT)

    # Force HTTP mode (Caddy handles HTTPS)
    use_https = False
    protocol = "http"

    try:
        # 서버 시작
        httpd = socketserver.TCPServer((HOST, available_port), CustomHTTPRequestHandler)
        httpd.allow_reuse_address = True  # 주소 재사용 허용

        with httpd:
            # 로컬 및 외부 접근 주소 표시
            import socket
            local_ip = socket.gethostbyname(socket.gethostname())

            print("Mingle Studio Development Server Started!")
            print("="*60)
            print(f"Local Access: {protocol}://localhost:{available_port}")
            print(f"Network Access: {protocol}://{local_ip}:{available_port}")
            print(f"Root Directory: {os.getcwd()}")
            if use_https:
                print("⚠️  Using self-signed certificate - browser will show security warning")
                print("   Click 'Advanced' → 'Proceed' to continue")
            print("="*60)
            print("Main Pages:")
            print(f"   Home:        {protocol}://localhost:{available_port}/")
            print(f"   About:       {protocol}://localhost:{available_port}/about")
            print(f"   Services:    {protocol}://localhost:{available_port}/services")
            print(f"   Portfolio:   {protocol}://localhost:{available_port}/portfolio")
            print(f"   Gallery:     {protocol}://localhost:{available_port}/gallery")
            print(f"   Contact:     {protocol}://localhost:{available_port}/contact")
            print(f"   Q&A:         {protocol}://localhost:{available_port}/qna")
            print(f"   Backgrounds: {protocol}://localhost:{available_port}/backgrounds")
            print(f"   Props:       {protocol}://localhost:{available_port}/props")
            print(f"   Partner:     {protocol}://localhost:{available_port}/partner")
            print("="*60)
            print("API Endpoints:")
            print(f"   GET  /api/backgrounds  - 배경 데이터 조회")
            print(f"   POST /api/backgrounds  - 배경 데이터 업데이트")
            print(f"   GET  /api/props        - 프랍 데이터 조회")
            print(f"   POST /api/props        - 프랍 데이터 업데이트")
            print("="*60)
            print("External Access Setup:")
            print(f"   1. Setup port forwarding for port {available_port} on router")
            print(f"   2. Access via public IP: {protocol}://[PUBLIC_IP]:{available_port}")
            print(f"   3. Same network: {protocol}://{local_ip}:{available_port}")
            print("="*60)
            print("Tip: Press Ctrl+C to stop the server.")
            print("Opening browser automatically...")

            # 기본 브라우저에서 자동으로 열기 (로컬호스트로)
            webbrowser.open(f"{protocol}://localhost:{available_port}")
            
            # 서버 실행
            httpd.serve_forever()
            
    except KeyboardInterrupt:
        print("\n\nServer stopped.")
        print("Goodbye!")
    except OSError as e:
        if e.errno == 10048:  # WinError 10048
            print(f"\nError: Port {available_port} is already in use.")
            print("Try different port or check the following:")
            print("1. Check if another server is running")
            print("2. Kill Python process in Task Manager")
            print(f"3. Run in command prompt: netstat -ano | findstr :{available_port}")
        else:
            print(f"Server error: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()