from fastapi import FastAPI, File, UploadFile, HTTPException, Request
from fastapi.responses import HTMLResponse
from fastapi.middleware.cors import CORSMiddleware
from huggingface_hub import HfApi
import os
from dotenv import load_dotenv
import uvicorn
import requests
from io import BytesIO
import re
from urllib.parse import urlparse
from datetime import datetime
import os
import hashlib
import random
import string

load_dotenv()

app = FastAPI()

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

# 环境变量配置
hf_token = os.getenv("HF_TOKEN")
hf_dataset_id = os.getenv("HF_DATASET_ID")
ACCESS_PASSWORD = os.getenv("ACCESS_PASSWORD", "your_default_password")
PROXY_DOMAIN = os.getenv("PROXY_DOMAIN", "huggingface.co")

# 初始化API并添加token
api = HfApi(token=hf_token)

# 设置通用请求头
headers = {
    "Authorization": f"Bearer {hf_token}",
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}

def is_valid_image_url(url):
    try:
        parsed = urlparse(url)
        return bool(parsed.netloc) and bool(parsed.scheme)
    except:
        return False

def get_image_extension(content_type):
    content_type = content_type.lower()
    if 'jpeg' in content_type or 'jpg' in content_type:
        return 'jpg'
    elif 'png' in content_type:
        return 'png'
    elif 'gif' in content_type:
        return 'gif'
    elif 'webp' in content_type:
        return 'webp'
    return 'jpg'

def generate_random_string(length=6):
    """Generate a random string of fixed length"""
    letters = string.ascii_lowercase + string.digits
    return ''.join(random.choice(letters) for _ in range(length))

def generate_unique_filename(original_filename):
    current_date = datetime.now().strftime('%Y-%m-%d')
    
    ext = os.path.splitext(original_filename)[1]
    if not ext:
        ext = '.jpg'
    
    timestamp = datetime.now().strftime('%H%M%S')
    random_str = generate_random_string()
    content = f"{timestamp}{random_str}{original_filename}".encode('utf-8')
    hash_value = hashlib.md5(content).hexdigest()[:12]
    
    unique_filename = f"{hash_value}{ext}"
    
    return f"{current_date}/{unique_filename}"

@app.get("/", response_class=HTMLResponse)
async def root():
    return """
    <html>
        <head>
            <title>Login</title>
            <style>
                body {
                    background-color: #f0f2f5;
                    font-family: Arial, sans-serif;
                }
                .login-container {
                    width: 300px;
                    margin: 100px auto;
                    padding: 20px;
                    border: 1px solid #ccc;
                    border-radius: 10px;
                    background-color: #fff;
                    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
                    text-align: center;
                }
                .input-field {
                    width: 100%;
                    padding: 10px;
                    margin: 10px 0;
                    border: 1px solid #ddd;
                    border-radius: 5px;
                    font-size: 16px;
                }
                .submit-button {
                    width: 100%;
                    padding: 10px;
                    background-color: #007bff;
                    color: white;
                    border: none;
                    border-radius: 5px;
                    font-size: 16px;
                    cursor: pointer;
                    transition: background-color 0.3s;
                }
                .submit-button:hover {
                    background-color: #0056b3;
                }
                .error-message {
                    color: red;
                    margin-top: 10px;
                }
            </style>
        </head>
        <body>
            <div class="login-container">
                <h2>Enter Access Password</h2>
                <form id="loginForm">
                    <input type="password" name="password" class="input-field" placeholder="Enter Password" required>
                    <button type="submit" class="submit-button">Login</button>
                </form>
                <div id="error-message" class="error-message"></div>
            </div>
            <script>
                document.getElementById('loginForm').addEventListener('submit', async (e) => {
                    e.preventDefault();
                    const password = e.target.password.value;
                    try {
                        const response = await fetch('/verify', {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json',
                            },
                            body: JSON.stringify({ password })
                        });
                        if (response.ok) {
                            const html = await response.text();
                            document.open();
                            document.write(html);
                            document.close();
                        } else {
                            document.getElementById('error-message').textContent = 'Incorrect password, please try again';
                        }
                    } catch (error) {
                        document.getElementById('error-message').textContent = 'An error occurred, please try again';
                    }
                });
            </script>
        </body>
    </html>
    """

@app.post("/verify")
async def verify_password(request: Request):
    try:
        data = await request.json()
        password = data.get("password")
        
        if password == ACCESS_PASSWORD:
            return HTMLResponse("""
    <html>
        <head>
            <title>HuggingFace Dataset Images</title>
            <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
            <style>
                * {
                    margin: 0;
                    padding: 0;
                    box-sizing: border-box;
                    font-family: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, Roboto, sans-serif;
                }
                body {
                    background-color: #f5f5f5;
                    color: #333;
                    line-height: 1.6;
                }
                .container {
                    max-width: 1200px;
                    margin: 0 auto;
                    padding: 2rem;
                }
                .header {
                    text-align: center;
                    margin-bottom: 2rem;
                    padding: 1rem;
                    background: white;
                    border-radius: 12px;
                    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
                }
                .header h1 {
                    color: #2d3748;
                    font-size: 1.8rem;
                    font-weight: 600;
                    margin-bottom: 0.5rem;
                }
                .upload-container {
                    background: white;
                    border-radius: 12px;
                    padding: 2rem;
                    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
                }
                .upload-area {
                    min-height: 200px;
                    padding: 2rem;
                    margin: 1rem 0;
                    background: #f8fafc;
                    border: 2px dashed #cbd5e0;
                    border-radius: 12px;
                    transition: all 0.3s ease;
                    display: flex;
                    flex-direction: column;
                    align-items: center;
                    justify-content: center;
                }
                .upload-area:hover {
                    border-color: #4299e1;
                    background: #ebf8ff;
                }
                .upload-methods {
                    text-align: center;
                    margin-bottom: 1.5rem;
                    color: #4a5568;
                }
                .upload-methods p {
                    margin: 0.5rem 0;
                    font-size: 0.95rem;
                }
                .url-input {
                    width: 80%;
                    padding: 0.75rem 1rem;
                    margin: 1rem 0;
                    border: 1px solid #e2e8f0;
                    border-radius: 8px;
                    font-size: 0.95rem;
                    transition: all 0.3s ease;
                }
                .url-input:focus {
                    outline: none;
                    border-color: #4299e1;
                    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.2);
                }
                .upload-button {
                    background-color: #4299e1;
                    color: white;
                    padding: 0.75rem 1.5rem;
                    border: none;
                    border-radius: 8px;
                    font-size: 0.95rem;
                    font-weight: 500;
                    cursor: pointer;
                    transition: all 0.3s ease;
                }
                .upload-button:hover {
                    background-color: #3182ce;
                    transform: translateY(-1px);
                }
                .file-list {
                    margin-top: 2rem;
                }
                .file-item {
                    background: #f8fafc;
                    border: 1px solid #e2e8f0;
                    border-radius: 8px;
                    padding: 1rem;
                    margin-bottom: 1rem;
                    transition: all 0.3s ease;
                }
                .file-item:hover {
                    border-color: #4299e1;
                    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
                }
                .file-name {
                    color: #2d3748;
                    font-weight: 500;
                    margin-bottom: 0.5rem;
                }
                .progress {
                    width: 100%;
                    height: 8px;
                    background-color: #edf2f7;
                    border-radius: 4px;
                    overflow: hidden;
                    margin: 0.5rem 0;
                }
                .progress-bar {
                    height: 100%;
                    background-color: #48bb78;
                    width: 0%;
                    transition: width 0.3s ease-in-out;
                }
                .copy-buttons {
                    display: flex;
                    gap: 0.5rem;
                    margin-top: 1rem;
                }
                .copy-button {
                    background-color: #edf2f7;
                    color: #4a5568;
                    padding: 0.5rem 1rem;
                    border: none;
                    border-radius: 6px;
                    font-size: 0.875rem;
                    cursor: pointer;
                    transition: all 0.3s ease;
                }
                .copy-button:hover {
                    background-color: #e2e8f0;
                    color: #2d3748;
                }
                .result {
                    margin: 0.5rem 0;
                }
                .result a {
                    color: #4299e1;
                    text-decoration: none;
                    word-break: break-all;
                }
                .result a:hover {
                    text-decoration: underline;
                }
                @media (max-width: 768px) {
                    .container {
                        padding: 1rem;
                    }
                    .upload-area {
                        padding: 1rem;
                    }
                    .url-input {
                        width: 100%;
                    }
                    .copy-buttons {
                        flex-wrap: wrap;
                    }
                }
            </style>
        </head>
        <body>
            <div class="container">
                <div class="header">
                    <h1>HuggingFace Dataset Images</h1>
                </div>
                
                <div class="upload-container">
                    <div class="upload-area" id="dropZone">
                        <div class="upload-methods">
                            <p>支持多种上传方式:</p>
                            <p>1. 拖拽图片到此处</p>
                            <p>2. 点击选择文件</p>
                            <p>3. 粘贴图片或图片URL</p>
                            <p>4. 输入图片URL后按回车</p>
                        </div>
                        <input type="text" id="urlInput" class="url-input" placeholder="输入linux.do启动">
                        <input type="file" id="fileInput" multiple accept="image/*" style="display: none;">
                        <button class="upload-button" onclick="document.getElementById('fileInput').click()">
                            选择文件
                        </button>
                    </div>
                    <div class="file-list" id="fileList"></div>
                </div>
            </div>
            <script>
                const MAX_CONCURRENT_UPLOADS = 10;
                let uploadQueue = [];
                let activeUploads = 0;
                async function processUrl(url) {
                    try {
                        const response = await fetch('/fetch-url/', {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json',
                            },
                            body: JSON.stringify({ url })
                        });
                        
                        if (!response.ok) {
                            throw new Error('获取图片失败');
                        }
                        
                        const data = await response.json();
                        return data;
                    } catch (error) {
                        throw error;
                    }
                }
function createFileElement(file) {
    const element = document.createElement('div');
    element.className = 'file-item';
    element.innerHTML = `
        <div class="file-name">文件名: ${file.name}</div>
        <div class="progress">
            <div class="progress-bar"></div>
        </div>
        <div class="result"></div>
        <div class="copy-buttons" style="display: none;">
            <button class="copy-button" onclick="copyText(this, 'markdown')">复制 Markdown</button>
            <button class="copy-button" onclick="copyText(this, 'html')">复制 HTML</button>
            <button class="copy-button" onclick="copyText(this, 'url')">复制 URL</button>
        </div>
    `;
    return element;
}
// 添加复制函数
async function copyText(button, type) {
    const fileItem = button.closest('.file-item');
    const url = fileItem.querySelector('.result a').href;
    const fileName = fileItem.querySelector('.file-name').textContent.split(': ')[1];
    
    let textToCopy = '';
    switch(type) {
        case 'markdown':
            textToCopy = `![${fileName}](${url})`;
            break;
        case 'html':
            textToCopy = `<img src="${url}" alt="${fileName}">`;
            break;
        case 'url':
            textToCopy = url;
            break;
    }
    try {
        await navigator.clipboard.writeText(textToCopy);
        const originalText = button.textContent;
        button.textContent = '已复制!';
        setTimeout(() => {
            button.textContent = originalText;
        }, 1000);
    } catch (err) {
        console.error('复制失败:', err);
        alert('复制失败,请手动复制');
    }
}
// 添加uploadFile函数中的复制按钮处理
async function uploadFile(file, element) {
    activeUploads++;
    const progressBar = element.querySelector('.progress-bar');
    const resultDiv = element.querySelector('.result');
    const copyButtons = element.querySelector('.copy-buttons');
    const formData = new FormData();
    formData.append('file', file);
    try {
        const response = await fetch('/upload/', {
            method: 'POST',
            body: formData
        });
        const data = await response.json();
        progressBar.style.width = '100%';
        resultDiv.innerHTML = `<a href="${data.url}" target="_blank">${data.url}</a>`;
        copyButtons.style.display = 'block';
    } catch (error) {
        resultDiv.innerHTML = `<p style="color: red;">上传失败:${error.message}</p>`;
    }
    activeUploads--;
    processUploadQueue();
}
                // URL输入框处理
                document.getElementById('urlInput').addEventListener('keypress', async (e) => {
                    if (e.key === 'Enter') {
                        e.preventDefault();
                        const url = e.target.value.trim();
                        if (url) {
                            const element = createFileElement({ name: url.split('/').pop() || 'image.jpg' });
                            document.getElementById('fileList').prepend(element);
                            
                            try {
                                const data = await processUrl(url);
                                element.querySelector('.progress-bar').style.width = '100%';
                                element.querySelector('.result').innerHTML = `<a href="${data.url}" target="_blank">${data.url}</a>`;
                                element.querySelector('.copy-buttons').style.display = 'block';
                                e.target.value = '';
                            } catch (error) {
                                element.querySelector('.result').innerHTML = `<p style="color: red;">上传失败:${error.message}</p>`;
                            }
                        }
                    }
                });
                // 处理拖拽上传
                const dropZone = document.getElementById('dropZone');
                dropZone.addEventListener('dragover', (e) => {
                    e.preventDefault();
                    dropZone.style.background = '#e1e1e1';
                });
                dropZone.addEventListener('dragleave', (e) => {
                    e.preventDefault();
                    dropZone.style.background = '#f9f9f9';
                });
                dropZone.addEventListener('drop', (e) => {
                    e.preventDefault();
                    dropZone.style.background = '#f9f9f9';
                    handleFiles(e.dataTransfer.files);
                });
                // 处理文件选择
                document.getElementById('fileInput').addEventListener('change', (e) => {
                    handleFiles(e.target.files);
                });
                // 处理粘贴上传
                document.addEventListener('paste', async (e) => {
                    const items = e.clipboardData.items;
                    for (let item of items) {
                        if (item.type.indexOf('image') !== -1) {
                            const file = item.getAsFile();
                            handleFiles([file]);
                        } else if (item.type === 'text/plain') {
                            item.getAsString(async text => {
                                text = text.trim();
                                if (text.match(/^https?:\/\/.+\.(jpg|jpeg|png|gif|webp)$/i)) {
                                    const element = createFileElement({ name: text.split('/').pop() || 'image.jpg' });
                                    document.getElementById('fileList').appendChild(element);
                                    
                                    try {
                                        const data = await processUrl(text);
                                        element.querySelector('.progress-bar').style.width = '100%';
                                        element.querySelector('.result').innerHTML = `<a href="${data.url}" target="_blank">${data.url}</a>`;
                                        element.querySelector('.copy-buttons').style.display = 'block';
                                    } catch (error) {
                                        element.querySelector('.result').innerHTML = `<p style="color: red;">上传失败:${error.message}</p>`;
                                    }
                                }
                            });
                        }
                    }
                });
                function handleFiles(files) {
                    const fileList = document.getElementById('fileList');
                    
                    for (let file of files) {
                        if (!file.type.startsWith('image/')) continue;
                        
                        const element = createFileElement(file);
                        fileList.prepend(element);
                        if (activeUploads < MAX_CONCURRENT_UPLOADS) {
                            uploadFile(file, element);
                        } else {
                            uploadQueue.push({ file, element });
                        }
                    }
                }
            </script>
        </body>
    </html>
            """)
        else:
            raise HTTPException(status_code=401, detail="Invalid password")
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

@app.post("/upload/")
async def upload_image(file: UploadFile = File(...)):
    if not file:
        raise HTTPException(status_code=400, detail="No file uploaded")
    
    if not file.content_type.startswith('image/'):
        raise HTTPException(status_code=400, detail="File must be an image")

    try:
        contents = await file.read()
        
        # Generate unique path
        unique_path = generate_unique_filename(file.filename)

        # Upload to HuggingFace
        response = api.upload_file(
            path_or_fileobj=contents,
            path_in_repo=f"images/{unique_path}",
            repo_id=hf_dataset_id,
            repo_type="dataset",
            token=hf_token
        )

        # 修改返回URL格式
        image_url = f"https://{PROXY_DOMAIN}/datasets/{hf_dataset_id}/resolve/main/images/{unique_path}"
        return {"url": image_url}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/fetch-url/")
async def fetch_url(request: Request):
    try:
        data = await request.json()
        url = data.get("url")
        if not url:
            raise HTTPException(status_code=400, detail="No URL provided")
        
        if not is_valid_image_url(url):
            raise HTTPException(status_code=400, detail="Invalid image URL")

        response = requests.get(url, headers=headers, timeout=10)
        if not response.ok:
            raise HTTPException(status_code=400, detail="Failed to fetch image")

        content_type = response.headers.get('content-type', '')
        if not content_type.startswith('image/'):
            raise HTTPException(status_code=400, detail="URL does not point to an image")

        original_filename = url.split('/')[-1]
        if not original_filename or '.' not in original_filename:
            ext = get_image_extension(content_type)
            original_filename = f"downloaded_image.{ext}"

        unique_path = generate_unique_filename(original_filename)

        response = api.upload_file(
            path_or_fileobj=response.content,
            path_in_repo=f"images/{unique_path}",
            repo_id=hf_dataset_id,
            repo_type="dataset",
            token=hf_token
        )

        # 修改返回URL格式
        image_url = f"https://{PROXY_DOMAIN}/datasets/{hf_dataset_id}/resolve/main/images/{unique_path}"
        return {"url": image_url, "filename": original_filename}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=7860)