import os
import json
import random
import smtplib
import uuid
import base64
import io
import time
from email.message import EmailMessage
from pathlib import Path
from tempfile import gettempdir

from flask import Flask, request, jsonify, render_template_string
from werkzeug.utils import secure_filename
from PIL import Image, ImageDraw, ImageFont

# -----------------------------
#  Конфигурация
# -----------------------------
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', 'dev-key-for-cloud')

ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}

# Папка для временных файлов – используем /tmp в Cloud Functions
TEMP_DIR = Path(gettempdir()) / 'collage_uploads'
TEMP_DIR.mkdir(exist_ok=True)

# Время жизни временных коллажей (секунды)
COLLAGE_TTL = 600  # 10 минут

# SMTP настройки (берутся из переменных окружения)
SMTP_SERVER = os.environ.get('SMTP_SERVER', 'smtp.yandex.ru')
SMTP_PORT = int(os.environ.get('SMTP_PORT', 587))
SMTP_USER = os.environ.get('SMTP_USER')
SMTP_PASSWORD = os.environ.get('SMTP_PASSWORD')

# Готовые тексты
DEFAULT_TEXTS = [
    "Спасибо за ваше терпение и мудрость!",
    "Вы дали нам крылья. Спасибо, учитель!",
    "Школьные годы пролетели, а ваша доброта останется навсегда.",
    "Благодарим за каждый урок, за веру в нас.",
    "Вы – наш путеводный свет. Спасибо!",
    "Спасибо за знания и заботу. Мы вас никогда не забудем.",
    "Лучший учитель – тот, кто вдохновляет. Вы – лучший!",
    "Пусть ваши ученики всегда вами гордятся. Спасибо!",
    "Вы сделали школу вторым домом. Огромное спасибо!",
    "Желаем вам талантливых учеников и здоровья!"
]

# Шаблоны коллажей (пути внутри функции)
COLLAGES_DIR = Path(__file__).parent / 'collages'
TEMPLATES = list(COLLAGES_DIR.glob('template*.png'))
if not TEMPLATES:
    # Fallback: создаём белый шаблон, если нет файлов
    fallback = COLLAGES_DIR / '_fallback.png'
    if not fallback.exists():
        img = Image.new('RGB', (800, 600), color='white')
        img.save(fallback)
    TEMPLATES = [fallback]

# -----------------------------
#  Вспомогательные функции
# -----------------------------
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def cleanup_old_collages():
    """Удаляем временные коллажи старше COLLAGE_TTL секунд."""
    now = time.time()
    for f in TEMP_DIR.glob('collage_*.png'):
        try:
            if now - f.stat().st_mtime > COLLAGE_TTL:
                f.unlink(missing_ok=True)
        except Exception:
            pass

# Дефолтный конфиг — используется если рядом с шаблоном нет JSON-файла
_DEFAULT_CONFIG = {
    "student": {"x": 50,  "y": 50, "width": 300, "height": 300},
    "teacher": {"x": 450, "y": 50, "width": 300, "height": 300},
    "text": {
        "x": 50, "y": 400, "max_width": 700,
        "font_file": "Roboto-Regular.ttf",
        "font_size": 24,
        "color": "#000000",
        "align": "center"
    }
}

def load_template_config(template_path: Path) -> dict:
    """Загружает JSON-конфиг рядом с PNG-шаблоном.
    Если файл не найден или содержит ошибки — возвращает дефолтный конфиг."""
    config_path = template_path.with_suffix('.json')
    if not config_path.exists():
        return _DEFAULT_CONFIG
    try:
        with open(config_path, encoding='utf-8') as f:
            data = json.load(f)
        # Мержим с дефолтом, чтобы отсутствующие ключи не вызывали KeyError
        cfg = {
            "student": {**_DEFAULT_CONFIG["student"], **data.get("student", {})},
            "teacher": {**_DEFAULT_CONFIG["teacher"], **data.get("teacher", {})},
            "text":    {**_DEFAULT_CONFIG["text"],    **data.get("text", {})},
        }
        return cfg
    except Exception:
        return _DEFAULT_CONFIG

def resize_and_crop(image_path, target_size):
    with Image.open(image_path) as img:
        if img.mode in ('RGBA', 'LA', 'P'):
            img = img.convert('RGB')
        width, height = img.size
        new_size = min(width, height)
        left = (width - new_size) / 2
        top = (height - new_size) / 2
        right = (width + new_size) / 2
        bottom = (height + new_size) / 2
        img = img.crop((left, top, right, bottom))
        img = img.resize(target_size, Image.Resampling.LANCZOS)
        return img

def hex_to_rgb(hex_color: str) -> tuple:
    """Конвертирует '#rrggbb' в (r, g, b)."""
    hex_color = hex_color.lstrip('#')
    return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))

def create_collage(student_photo_path, teacher_photo_path, text, template_path):
    """Создаёт коллаж согласно конфигу шаблона (JSON рядом с PNG).
    Параметры из конфига:
      student / teacher: x, y, width, height  — положение и размер фото
      text: x, y, max_width, font_file, font_size, color, align (left|center|right)
    """
    cfg = load_template_config(Path(template_path))

    template = Image.open(template_path).convert('RGB')
    w, h = template.size

    # --- Фото ученика ---
    s = cfg['student']
    student_size = (s['width'], s['height'])
    student_img = resize_and_crop(student_photo_path, student_size)
    template.paste(student_img, (s['x'], s['y']))

    # --- Фото учителя ---
    t = cfg['teacher']
    teacher_size = (t['width'], t['height'])
    teacher_img = resize_and_crop(teacher_photo_path, teacher_size)
    template.paste(teacher_img, (t['x'], t['y']))

    # --- Текст ---
    tc = cfg['text']
    static_dir = Path(__file__).parent / 'static'
    font_file = tc.get('font_file', 'Roboto-Regular.ttf')
    font_size = int(tc.get('font_size', 24))
    text_color = hex_to_rgb(tc.get('color', '#000000'))
    text_align = tc.get('align', 'center')   # 'left' | 'center' | 'right'
    text_x = int(tc.get('x', 50))
    text_y = int(tc.get('y', 400))
    max_width = int(tc.get('max_width', w - 100))

    try:
        font_path = static_dir / font_file
        font = ImageFont.truetype(str(font_path), font_size) if font_path.exists() else ImageFont.load_default()
    except Exception:
        font = ImageFont.load_default()

    draw = ImageDraw.Draw(template)

    # Перенос слов по max_width
    lines = []
    words = text.split()
    line = ''
    for word in words:
        test_line = line + word + ' '
        bbox = draw.textbbox((0, 0), test_line, font=font)
        if bbox[2] - bbox[0] <= max_width:
            line = test_line
        else:
            if line:
                lines.append(line.rstrip())
            line = word + ' '
    if line:
        lines.append(line.rstrip())

    # Отрисовка строк с учётом выравнивания
    y_offset = text_y
    for line in lines:
        bbox = draw.textbbox((0, 0), line, font=font)
        line_width = bbox[2] - bbox[0]
        line_height = bbox[3] - bbox[1]

        if text_align == 'center':
            x = text_x + (max_width - line_width) // 2
        elif text_align == 'right':
            x = text_x + max_width - line_width
        else:  # left
            x = text_x

        draw.text((x, y_offset), line, fill=text_color, font=font)
        y_offset += line_height + 5

    return template

def send_email_with_attachment(recipient_email, collage_image, student_name="ученика", teacher_name="учителя"):
    if not SMTP_USER or not SMTP_PASSWORD:
        raise RuntimeError("SMTP не настроен: задайте переменные окружения SMTP_USER и SMTP_PASSWORD")
    msg = EmailMessage()
    msg['Subject'] = f"Открытка-коллаж для {teacher_name} от {student_name}"
    msg['From'] = SMTP_USER
    msg['To'] = recipient_email
    msg.set_content("Здравствуйте!\n\nВаш коллаж готов.\n\nС уважением,\nКоманда Выпускник")
    img_bytes = io.BytesIO()
    collage_image.save(img_bytes, format='PNG')
    img_bytes = img_bytes.getvalue()
    msg.add_attachment(img_bytes, maintype='image', subtype='png', filename='collage.png')
    with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
        server.starttls()
        server.login(SMTP_USER, SMTP_PASSWORD)
        server.send_message(msg)

# -----------------------------
#  HTML-шаблон (два шага)
# -----------------------------
HTML_TEMPLATE = '''
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Выпускной коллаж для учителя</title>
    <style>
        body { font-family: 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); margin: 0; padding: 20px; min-height: 100vh; display: flex; justify-content: center; align-items: center; }
        .container { max-width: 700px; width: 100%; background: white; border-radius: 32px; box-shadow: 0 20px 35px rgba(0,0,0,0.1); padding: 30px; box-sizing: border-box; }
        h1 { text-align: center; color: #2c3e66; margin-top: 0; }
        .step { display: none; }
        .step.active { display: block; }
        .flash { background: #e8f0fe; border-left: 5px solid #2c7da0; padding: 12px; margin-bottom: 20px; border-radius: 12px; color: #1f3a4b; }
        label { font-weight: 600; display: block; margin-top: 18px; margin-bottom: 6px; color: #1e2f41; }
        input[type="file"], input[type="email"], textarea { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 20px; font-size: 1rem; box-sizing: border-box; }
        textarea { resize: vertical; }
        button { background: #2c7da0; color: white; border: none; padding: 12px 24px; font-size: 1.1rem; border-radius: 40px; cursor: pointer; width: 100%; margin-top: 25px; font-weight: bold; transition: 0.2s; }
        button:hover { background: #1f5e7a; transform: scale(0.98); }
        button.secondary { background: #6c757d; }
        .preview-img { max-width: 100%; border-radius: 20px; margin-top: 20px; box-shadow: 0 5px 15px rgba(0,0,0,0.1); }
        .hint { font-size: 0.8rem; color: gray; margin-top: 5px; }
        footer { text-align: center; margin-top: 30px; font-size: 0.75rem; color: #6c757d; }
        .spinner { display: inline-block; width: 20px; height: 20px; border: 3px solid rgba(255,255,255,0.3); border-radius: 50%; border-top-color: white; animation: spin 1s ease-in-out infinite; vertical-align: middle; margin-left: 10px; }
        @keyframes spin { to { transform: rotate(360deg); } }
        @media (max-width: 500px) { .container { padding: 20px; } }
    </style>
</head>
<body>
<div class="container">
    <h1>🎓 Выпускная открытка учителю</h1>

    <!-- Шаг 1: загрузка данных -->
    <div id="step1" class="step active">
        <p style="text-align:center; color:#4a627a;">Загрузите фото и получите предпросмотр коллажа</p>
        <form id="previewForm" enctype="multipart/form-data">
            <label>📸 Фото ученика (или классa):</label>
            <input type="file" name="student_photo" id="studentPhotoInput" accept="image/png, image/jpeg, image/jpg" required>
            <div class="hint">PNG, JPG — большие файлы будут сжаты автоматически. Лицо должно быть в центре кадра; идеально подходит формат 1:1 (квадрат).</div>

            <label>🍎 Фото учителя:</label>
            <input type="file" name="teacher_photo" id="teacherPhotoInput" accept="image/png, image/jpeg, image/jpg" required>
            <div class="hint">Постарайтесь выбрать фото крупным планом. Лицо должно быть в центре кадра; идеально подходит формат 1:1 (квадрат).</div>

            <label>💬 Текст поздравления (необязательно):</label>
            <textarea name="custom_text" rows="3" placeholder="Если оставить пустым, выберем случайную тёплую фразу..."></textarea>

            <button type="submit" id="previewBtn">🎨 Создать предпросмотр</button>
        </form>
        <div id="previewError" class="flash" style="display:none;"></div>
    </div>

    <!-- Шаг 2: предпросмотр и отправка -->
    <div id="step2" class="step">
        <h3 style="text-align:center;">Вот что получилось:</h3>
        <img id="collagePreview" class="preview-img" alt="Предпросмотр коллажа">
        <form id="sendForm">
            <label>✉️ Ваш email:</label>
            <input type="email" name="email" placeholder="example@mail.ru" required>
            <button type="submit" id="sendBtn">📧 Отправить открытку</button>
        </form>
        <button id="backBtn" class="secondary" style="margin-top:15px;">← Назад, изменить данные</button>
        <div id="sendError" class="flash" style="display:none;"></div>
        <div id="sendSuccess" class="flash" style="display:none; background:#d4edda; border-left-color:#28a745;"></div>
    </div>
    <footer>
        Сначала создайте предпросмотр, затем введите email и отправьте.<br>
        Проверьте папку «Спам», если письма нет.
    </footer>
</div>

<script>
    // -------------------------------------------------------
    //  Утилита: сжать изображение до размера < maxBytes
    //  Лимит API Gateway Yandex Cloud = 3.5 МБ на весь запрос.
    //  Два фото + overhead формы → каждое фото сжимаем до 1.2 МБ.
    // -------------------------------------------------------
    const MAX_PHOTO_BYTES = 1.2 * 1024 * 1024; // 1.2 МБ на одно фото

    function resizeImageIfNeeded(file) {
        return new Promise((resolve) => {
            if (file.size <= MAX_PHOTO_BYTES) {
                resolve(file);
                return;
            }

            const img = new Image();
            const url = URL.createObjectURL(file);

            img.onload = () => {
                URL.revokeObjectURL(url);

                // Коэффициент масштабирования по площади
                const ratio = Math.sqrt(MAX_PHOTO_BYTES / file.size) * 0.95;
                const newW = Math.max(1, Math.round(img.width * ratio));
                const newH = Math.max(1, Math.round(img.height * ratio));

                const canvas = document.createElement('canvas');
                canvas.width = newW;
                canvas.height = newH;
                canvas.getContext('2d').drawImage(img, 0, 0, newW, newH);

                // Первая попытка: качество 0.85
                canvas.toBlob((blob) => {
                    if (blob && blob.size <= MAX_PHOTO_BYTES) {
                        resolve(new File([blob], file.name.replace(/\.[^.]+$/, '.jpg'), { type: 'image/jpeg' }));
                        return;
                    }
                    // Вторая попытка: качество 0.65
                    canvas.toBlob((blob2) => {
                        if (blob2 && blob2.size <= MAX_PHOTO_BYTES) {
                            resolve(new File([blob2], file.name.replace(/\.[^.]+$/, '.jpg'), { type: 'image/jpeg' }));
                            return;
                        }
                        // Третья попытка: уменьшаем размер ещё в 2 раза + качество 0.5
                        const canvas2 = document.createElement('canvas');
                        canvas2.width = Math.round(newW / 2);
                        canvas2.height = Math.round(newH / 2);
                        canvas2.getContext('2d').drawImage(img, 0, 0, canvas2.width, canvas2.height);
                        canvas2.toBlob((blob3) => {
                            resolve(new File([blob3 || blob2 || blob], file.name.replace(/\.[^.]+$/, '.jpg'), { type: 'image/jpeg' }));
                        }, 'image/jpeg', 0.5);
                    }, 'image/jpeg', 0.65);
                }, 'image/jpeg', 0.85);
            };

            img.onerror = () => {
                URL.revokeObjectURL(url);
                resolve(file);
            };
            img.src = url;
        });
    }

    // -------------------------------------------------------
    //  Хранилище: collage_id из ответа /preview
    //  Сервер сохраняет коллаж во временный файл,
    //  клиент передаёт только ID — никакого base64 туда-обратно
    // -------------------------------------------------------
    let currentCollageId = null;

    // -------------------------------------------------------
    //  Шаг 1: создание превью
    // -------------------------------------------------------
    document.getElementById('previewForm').addEventListener('submit', async (e) => {
        e.preventDefault();
        const btn = document.getElementById('previewBtn');
        const originalText = btn.innerText;
        btn.innerHTML = 'Создаётся... <span class="spinner"></span>';
        btn.disabled = true;
        document.getElementById('previewError').style.display = 'none';

        try {
            const studentFile = document.getElementById('studentPhotoInput').files[0];
            const teacherFile  = document.getElementById('teacherPhotoInput').files[0];

            if (!studentFile || !teacherFile) {
                showError('previewError', 'Выберите оба фото');
                return;
            }

            // Параллельное сжатие обоих фото если нужно
            const [studentResized, teacherResized] = await Promise.all([
                resizeImageIfNeeded(studentFile),
                resizeImageIfNeeded(teacherFile)
            ]);

            const formData = new FormData(e.target);
            formData.set('student_photo', studentResized, studentResized.name);
            formData.set('teacher_photo', teacherResized, teacherResized.name);

            const response = await fetch('/preview', { method: 'POST', body: formData });
            const data = await response.json();

            if (data.success) {
                // Сохраняем ID коллажа (не сам коллаж!)
                currentCollageId = data.collage_id;
                document.getElementById('collagePreview').src = 'data:image/png;base64,' + data.image;
                document.getElementById('step1').classList.remove('active');
                document.getElementById('step2').classList.add('active');
                document.getElementById('sendError').style.display = 'none';
                document.getElementById('sendSuccess').style.display = 'none';
            } else {
                console.error('Ответ сервера:', data);
                showError('previewError', data.error || JSON.stringify(data));
            }
        } catch (err) {
            showError('previewError', 'Ошибка сети: ' + err.message);
        } finally {
            btn.innerHTML = originalText;
            btn.disabled = false;
        }
    });

    // -------------------------------------------------------
    //  Шаг 2: отправка письма
    //  Передаём только email + collage_id (маленький JSON)
    // -------------------------------------------------------
    document.getElementById('sendForm').addEventListener('submit', async (e) => {
        e.preventDefault();
        if (!currentCollageId) {
            showError('sendError', 'Данные не найдены, вернитесь назад и создайте предпросмотр заново');
            return;
        }
        const emailInput = document.querySelector('#sendForm input[name="email"]');
        const email = emailInput.value.trim();
        if (!email || !email.includes('@')) {
            showError('sendError', 'Введите корректный email');
            return;
        }

        const btn = document.getElementById('sendBtn');
        const originalText = btn.innerText;
        btn.innerHTML = 'Отправляется... <span class="spinner"></span>';
        btn.disabled = true;
        document.getElementById('sendError').style.display = 'none';
        document.getElementById('sendSuccess').style.display = 'none';

        try {
            const response = await fetch('/send', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ email: email, collage_id: currentCollageId })
            });
            const data = await response.json();
            if (data.success) {
                document.getElementById('sendSuccess').innerText =
                    '✅ Готово! Открытка отправлена на ' + email + '. Проверьте почту (и спам).';
                document.getElementById('sendSuccess').style.display = 'block';
                document.getElementById('previewForm').reset();
                currentCollageId = null;
                setTimeout(() => {
                    document.getElementById('step2').classList.remove('active');
                    document.getElementById('step1').classList.add('active');
                }, 3000);
            } else {
                showError('sendError', data.error || 'Ошибка при отправке письма');
            }
        } catch (err) {
            showError('sendError', 'Ошибка сети: ' + err.message);
        } finally {
            btn.innerHTML = originalText;
            btn.disabled = false;
        }
    });

    document.getElementById('backBtn').addEventListener('click', () => {
        document.getElementById('step2').classList.remove('active');
        document.getElementById('step1').classList.add('active');
        currentCollageId = null;
        document.getElementById('sendError').style.display = 'none';
        document.getElementById('sendSuccess').style.display = 'none';
    });

    function showError(elementId, message) {
        const el = document.getElementById(elementId);
        el.innerText = message;
        el.style.display = 'block';
        setTimeout(() => { el.style.display = 'none'; }, 5000);
    }
</script>
</body>
</html>
'''

# -----------------------------
#  Маршруты Flask
# -----------------------------
@app.route('/debug-env', methods=['GET'])
def debug_env():
    """Временный маршрут для проверки переменных окружения в Cloud Function.
    УДАЛИТЕ после отладки!"""
    import os
    return jsonify({
        'SMTP_SERVER':   os.environ.get('SMTP_SERVER', '(не задан)'),
        'SMTP_PORT':     os.environ.get('SMTP_PORT',   '(не задан)'),
        'SMTP_USER':     os.environ.get('SMTP_USER',   '(не задан)'),
        'SMTP_PASSWORD': ('*' * len(os.environ.get('SMTP_PASSWORD', '')))
                         + f" ({len(os.environ.get('SMTP_PASSWORD', ''))} символов)",
        'SECRET_KEY':    '(задан)' if os.environ.get('SECRET_KEY') else '(не задан)',
    })

@app.route('/', methods=['GET'])
def index():
    return render_template_string(HTML_TEMPLATE)

@app.route('/preview', methods=['POST'])
def preview():
    """Принимает фото и текст, создаёт коллаж, сохраняет во временный файл.
    Возвращает base64 для отображения в браузере + collage_id для последующей отправки."""
    student_photo = request.files.get('student_photo')
    teacher_photo = request.files.get('teacher_photo')
    user_text = request.form.get('custom_text', '').strip()

    # Валидация
    if not student_photo or not allowed_file(student_photo.filename):
        return jsonify({'success': False, 'error': 'Загрузите корректное фото ученика'})
    if not teacher_photo or not allowed_file(teacher_photo.filename):
        return jsonify({'success': False, 'error': 'Загрузите корректное фото учителя'})

    if not user_text:
        user_text = random.choice(DEFAULT_TEXTS)

    # Чистим старые временные коллажи
    cleanup_old_collages()

    unique_id = uuid.uuid4().hex
    student_path = TEMP_DIR / f"student_{unique_id}.jpg"
    teacher_path = TEMP_DIR / f"teacher_{unique_id}.jpg"
    student_photo.save(student_path)
    teacher_photo.save(teacher_path)

    try:
        template_path = random.choice(TEMPLATES)
        collage = create_collage(student_path, teacher_path, user_text, template_path)

        # Сохраняем коллаж во временный файл — он будет использован при /send
        collage_path = TEMP_DIR / f"collage_{unique_id}.png"
        collage.save(collage_path, format='PNG')

        # Конвертируем в base64 для предпросмотра в браузере
        img_bytes = io.BytesIO()
        collage.save(img_bytes, format='PNG')
        img_base64 = base64.b64encode(img_bytes.getvalue()).decode('utf-8')

        return jsonify({'success': True, 'image': img_base64, 'collage_id': unique_id})
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)})
    finally:
        student_path.unlink(missing_ok=True)
        teacher_path.unlink(missing_ok=True)

@app.route('/send', methods=['POST'])
def send():
    """Принимает email + collage_id, читает сохранённый коллаж с диска и отправляет письмо.
    Коллаж не генерируется заново — гарантируется идентичность с превью."""
    data = request.get_json(silent=True)
    if not data:
        return jsonify({'success': False, 'error': 'Неверный формат запроса'})

    email = (data.get('email') or '').strip()
    collage_id = (data.get('collage_id') or '').strip()

    # Валидация
    if not email or '@' not in email:
        return jsonify({'success': False, 'error': 'Введите корректный email'})
    if not collage_id:
        return jsonify({'success': False, 'error': 'Коллаж не найден, создайте предпросмотр заново'})

    # Защита от path traversal
    if not collage_id.isalnum():
        return jsonify({'success': False, 'error': 'Неверный идентификатор коллажа'})

    collage_path = TEMP_DIR / f"collage_{collage_id}.png"
    if not collage_path.exists():
        return jsonify({'success': False, 'error': 'Коллаж устарел или не найден, создайте предпросмотр заново'})

    try:
        collage_image = Image.open(collage_path)
        send_email_with_attachment(email, collage_image)
        return jsonify({'success': True})
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)})
    finally:
        # Удаляем коллаж после отправки
        collage_path.unlink(missing_ok=True)

# -----------------------------
#  Для локального теста (не используется в Cloud Functions)
# -----------------------------
if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)
