#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
=============================================================================
THẬP NGŨ NIÊN (THE FIFTEEN SPRINGS) - DIAGNOSTIC PROMPT HEALER & 3-BEAT PACER
=============================================================================
Module: prompt_healer.py
Author: Nguyễn Sĩ Sơn / AI Cinema Autonomous Engineering
Reference: FlowKit Architectural Specification (Section 3 & 4)

1. Diagnostic Prompt Healing (Vòng lặp tự sửa lỗi tự hành):
   - Bắt các vector lỗi từ bộ danh mục 14 lỗi (Critical, High, Minor) của Critic Gate.
   - Bơm chính xác các chỉ thị khắc phục (anti-drift, locked camera, anatomy guard)
     vào motion_prompt trước khi chuyển sang chu trình render kế tiếp.
   - Giới hạn cứng tối đa 2 chu trình sửa đổi (MAX_HEALING_CYCLES = 2) để chống lặp vô hạn.
   - Bảo toàn nguyên vẹn Audio Guard (No-BGM) và Closed Lips Guard.

2. Cinematic 3-Beat Shot Formula & Camera Movement Separation:
   - Cấu trúc nhịp điệu thời gian 3 hồi chuẩn mực điện ảnh:
     * Beat 1 (0-3s): Thiết lập tư thế nhân vật, không gian và tâm thế ban đầu.
     * Beat 2 (3-6s): Đột biến động tác, biến chuyển vi biểu cảm (micro-expression), trọng tâm kịch tính.
     * Beat 3 (6-10s): Lắng đọng chuyển động, ổn định tư thế để khớp trơn tru với Tail Frame.
   - Tách bạch chuyển động máy quay (Camera Movement) thành câu lệnh độc lập,
     ngăn chặn AI Video Engine hòa trộn chuyển động nhân vật với chuyển động máy quay.
=============================================================================
"""

import re
import sys
from pathlib import Path
from typing import List, Dict, Tuple, Optional, Any

# Reconfigure console UTF-8 on Windows
if sys.platform == "win32":
    try:
        sys.stdout.reconfigure(encoding="utf-8")
        sys.stderr.reconfigure(encoding="utf-8")
    except Exception:
        pass

MAX_HEALING_CYCLES = 2

# Bản đồ phác đồ điều trị cho 14 lỗi video AI chuẩn FlowKit
ERROR_THERAPY_CATALOG: Dict[str, Dict[str, str]] = {
    # 5 Critical Errors
    "character_drift": {
        "tier": "critical",
        "therapy": "strictly preserves identical facial bone structure, facial features, hair styling, and costume of {character} from reference turnaround sheet",
        "vi_therapy": "khóa chặt cấu trúc khung xương mặt, thần thái và trang phục của {character} chuẩn 100% theo Character Bible"
    },
    "breed_swap": {
        "tier": "critical",
        "therapy": "strictly maintains character identity as {character}, no role swapping or face bleeding into other cast members",
        "vi_therapy": "duy trì đúng diện mạo {character}, tuyệt đối không bị nhiễm mặt hoặc tráo vai với nhân vật khác"
    },
    "role_reversal": {
        "tier": "critical",
        "therapy": "strictly preserves character hierarchical roles, posture, status, and gaze direction as scripted",
        "vi_therapy": "giữ đúng vị thế, tư thế và hướng tương tác của nhân vật theo kịch bản"
    },
    "brand_logo_text": {
        "tier": "critical",
        "therapy": "clean photorealistic cinematic plate, zero text, zero watermark, zero logos, zero artificial artifacts",
        "vi_therapy": "khung hình thuần điện ảnh, không watermark, không chữ lạ, không logo"
    },
    "character_count_drift": {
        "tier": "critical",
        "therapy": "strictly maintains exact character count, zero extra background characters, zero vanishing subjects",
        "vi_therapy": "giữ nguyên số lượng nhân vật chính xác, không nhân bản thừa hoặc biến mất nhân vật"
    },

    # 5 High Errors
    "camera_drift": {
        "tier": "high",
        "therapy": "locked-off static camera, subtle smooth cinematic tracking, perfectly stabilized gimbal motion",
        "vi_therapy": "chuyển động camera ổn định mượt mà, triệt tiêu rung giật bất thường, giữ vững trục máy"
    },
    "object_morph": {
        "tier": "high",
        "therapy": "rigid consistent prop geometry, static physical objects maintain exact shape and texture throughout",
        "vi_therapy": "đạo cụ và vật thể giữ nguyên hình khối vật lý ổn định xuyên suốt cú máy"
    },
    "reverse_motion": {
        "tier": "high",
        "therapy": "continuous progressive forward motion, fluid steady trajectory without backward hitch or jitter",
        "vi_therapy": "quỹ đạo chuyển động tịnh tiến liên tục về phía trước, không giật lùi, không khựng hình"
    },
    "human_hands_limbs": {
        "tier": "high",
        "therapy": "anatomically accurate five-fingered hands, natural finger articulation, symmetrical eyes and facial geometry",
        "vi_therapy": "bàn tay chuẩn 5 ngón giải phẫu tự nhiên, mắt cân đối, không dị tật thừa chi"
    },
    "scale_break": {
        "tier": "high",
        "therapy": "natural realistic human proportions relative to architecture and environment",
        "vi_therapy": "tỷ lệ cơ thể người thực tế so với không gian kiến trúc xung quanh"
    },

    # 4 Minor Errors
    "lighting_jitter": {
        "tier": "minor",
        "therapy": "consistent cinematic three-point lighting, constant exposure and color temperature throughout entire shot",
        "vi_therapy": "ánh sáng điện ảnh đồng nhất, phơi sáng và nhiệt độ màu ổn định từ đầu đến cuối"
    },
    "micro_blur": {
        "tier": "minor",
        "therapy": "pin-sharp focal plane, crisp edge micro-contrast, high physical fidelity",
        "vi_therapy": "mặt phẳng nét căng rõ, độ tương phản vi mô sắc cạnh, chi tiết điện ảnh cao"
    },
    "background_warp": {
        "tier": "minor",
        "therapy": "rock-solid environmental architecture, stable perspective geometry",
        "vi_therapy": "hậu cảnh và phối cảnh kiến trúc vững chãi, không méo biến dạng"
    },
    "color_shift": {
        "tier": "minor",
        "therapy": "locked color grade, uniform palette and tonal balance from head to tail",
        "vi_therapy": "tông màu chuẩn xác, bảo toàn gam màu điện ảnh liền mạch xuyên suốt cú máy"
    }
}


def extract_prompt_guards(raw_prompt: str) -> Tuple[str, str, str]:
    """
    Tách biệt chính xác thân prompt và 2 tầng Guard bảo vệ:
    - closed_lips_guard: [KHÓA KHẨU HÌNH ...]
    - audio_guard: Quy tắc âm thanh: ...
    Trả về (clean_body, closed_lips_guard, audio_guard).
    """
    clean_body = raw_prompt.strip()
    closed_lips_guard = ""
    audio_guard = ""

    # 1. Trích xuất Audio Guard (hỗ trợ cả dấu cách, xuống dòng và variations)
    m_audio = re.search(r"(?:[\r\n\s]+|^)(Quy tắc âm thanh:[^\r\n]+.*)$", clean_body, re.IGNORECASE)
    if not m_audio:
        m_audio = re.search(r"(?:[\r\n\s]+|^)(Quy tắc âm thanh:.*)$", clean_body, re.IGNORECASE | re.DOTALL)
    if m_audio:
        audio_guard = m_audio.group(1).strip()
        clean_body = clean_body[:m_audio.start()].strip()

    # 2. Trích xuất Closed Lips Guard
    m_lips = re.search(r"(?:[\r\n\s]+|^)(\[KHÓA KHẨU HÌNH[^\]]*\].*)$", clean_body, re.IGNORECASE)
    if not m_lips:
        m_lips = re.search(r"(?:[\r\n\s]+|^)(\[KHÓA KHẨU HÌNH.*)$", clean_body, re.IGNORECASE | re.DOTALL)
    if m_lips:
        closed_lips_guard = m_lips.group(1).strip()
        clean_body = clean_body[:m_lips.start()].strip()

    # 3. Chống trường hợp Closed Lips Guard bị kẹp bên trong Audio Guard do thứ tự lộn xộn ban đầu
    if "[KHÓA KHẨU HÌNH" in audio_guard:
        m_nested = re.search(r"(\[KHÓA KHẨU HÌNH.*)", audio_guard, re.IGNORECASE)
        if m_nested:
            if not closed_lips_guard:
                closed_lips_guard = m_nested.group(1).strip()
            audio_guard = audio_guard[:m_nested.start()].strip()

    return clean_body, closed_lips_guard, audio_guard


def assemble_guarded_prompt(body: str, closed_lips: str = "", audio: str = "") -> str:
    """Ghép nối thân prompt với các Guard theo đúng thứ tự chuẩn điện ảnh: [Body] [Closed Lips] [Audio Guard]."""
    res = body.strip()
    if closed_lips:
        res = f"{res} {closed_lips.strip()}"
    if audio:
        res = f"{res} {audio.strip()}"
    return res.strip()


class DiagnosticPromptHealer:
    """
    Bộ chẩn đoán và điều trị Prompt tự hành (Self-Correction Loop).
    """

    @classmethod
    def heal_prompt(
        cls,
        original_prompt: str,
        detected_errors: List[str],
        character_name: str = "",
        cycle: int = 1
    ) -> Tuple[str, List[str]]:
        """
        Tiêm các chỉ thị kháng lỗi vào prompt dựa trên danh sách lỗi phát hiện được.
        - cycle: số thứ tự chu trình sửa (1 đến 2).
        - Trả về: (healed_prompt, applied_therapies_list).
        """
        if not detected_errors or cycle > MAX_HEALING_CYCLES:
            return original_prompt, []

        char_clean = character_name.strip() if character_name else "nhân vật chính"

        # Tách biệt tuyệt đối các Guard bảo vệ
        prompt_body, closed_lips_guard, audio_guard = extract_prompt_guards(original_prompt)

        applied_therapies: List[str] = []
        injections: List[str] = []

        # Phân loại và ưu tiên lỗi Critical trước, sau đó tới High và Minor
        ordered_errors = sorted(
            detected_errors,
            key=lambda e: 0 if ERROR_THERAPY_CATALOG.get(e, {}).get("tier") == "critical" else (1 if ERROR_THERAPY_CATALOG.get(e, {}).get("tier") == "high" else 2)
        )

        for err in ordered_errors:
            err_norm = err.lower().strip()
            matched_key = None
            for k in ERROR_THERAPY_CATALOG:
                if k in err_norm or err_norm in k:
                    matched_key = k
                    break

            if matched_key:
                info = ERROR_THERAPY_CATALOG[matched_key]
                therapy_text = info["therapy"].format(character=char_clean)
                vi_therapy_text = info["vi_therapy"].format(character=char_clean)

                # Tránh trùng lặp nếu prompt đã có nội dung tương tự
                if therapy_text.lower() not in prompt_body.lower() and vi_therapy_text.lower() not in prompt_body.lower():
                    # Đóng gói chỉ thị dưới dạng thẻ điều trị của FlowKit
                    tag = f" [HEALING-{info['tier'].upper()}: {therapy_text}. {vi_therapy_text}]"
                    injections.append(tag)
                    applied_therapies.append(f"{matched_key} ({info['tier']})")

        if not injections:
            return original_prompt, []

        # Ghép các chỉ thị vào thân prompt trước guards
        healed_body = prompt_body + " " + " ".join(injections)
        healed_prompt = assemble_guarded_prompt(healed_body, closed_lips_guard, audio_guard)

        return healed_prompt, applied_therapies


class CinematicShotPacer:
    """
    Công cụ cấu trúc nhịp điệu cú máy 3 Hồi (Cinematic 3-Beat Formula)
    kết hợp tách biệt câu lệnh chuyển động camera (Camera Movement Separation).
    """

    @classmethod
    def format_3beat_prompt(
        cls,
        raw_prompt: str,
        camera_motion: str = "",
        beat_1_desc: Optional[str] = None,
        beat_2_desc: Optional[str] = None,
        beat_3_desc: Optional[str] = None,
        character_name: str = ""
    ) -> str:
        """
        Định dạng prompt thành 3 nhịp thời gian chuẩn điện ảnh:
        [Beat 1 (0-3s) - Thiết lập bối cảnh & Trạng thái]: ...
        [Beat 2 (3-6s) - Cao trào & Biến chuyển cảm xúc]: ...
        [Beat 3 (6-10s) - Lắng đọng & Nối tiếp tiếp biên]: ...
        [Chuyển động Camera]: ...
        """
        # Trích xuất guards chuẩn hóa
        prompt_clean, closed_lips_guard, audio_guard = extract_prompt_guards(raw_prompt)

        # Nếu đã có beat chi tiết từ người dùng / kịch bản
        if beat_1_desc and beat_2_desc and beat_3_desc:
            b1 = beat_1_desc.strip()
            b2 = beat_2_desc.strip()
            b3 = beat_3_desc.strip()
        else:
            # Tự động phân đoạn câu từ prompt_clean
            sentences = [s.strip() for s in re.split(r"[.!?;\n]+", prompt_clean) if len(s.strip()) > 3]
            if len(sentences) >= 3:
                b1 = sentences[0]
                b2 = " ".join(sentences[1:-1])
                b3 = sentences[-1]
            elif len(sentences) == 2:
                b1 = sentences[0]
                b2 = sentences[1]
                b3 = "Chuyển động chậm dần, tư thế lắng đọng hòa vào không gian."
            elif len(sentences) == 1:
                b1 = f"Khung cảnh mở ra: {sentences[0]}."
                b2 = "Diễn biến cảm xúc tự nhiên, ánh mắt và cử chỉ vi mô tinh tế."
                b3 = "Khung hình ổn định, lắng đọng nhịp thở trước khi chuyển cảnh."
            else:
                b1 = "Nhân vật tĩnh lặng trong không gian, tà áo lay động nhẹ theo gió."
                b2 = "Cử chỉ tao nhã, ánh mắt biểu lộ nội tâm sâu lắng."
                b3 = "Tư thế dừng ở thế cân bằng hoàn hảo, chuẩn bị cho cú nối cảnh tiếp theo."

        # Trích xuất và chuẩn hóa camera motion
        cam_text = camera_motion.strip() if camera_motion else ""
        if not cam_text:
            # Tìm xem trong prompt có nhắc tới từ khóa camera không
            cam_match = re.search(r"(camera\s*[^.,;]+|góc máy\s*[^.,;]+|quay cận\s*[^.,;]+|quay toàn\s*[^.,;]+|slow push-in[^.,;]*)", prompt_clean, re.IGNORECASE)
            if cam_match:
                cam_text = cam_match.group(1).strip()
            else:
                cam_text = "Góc quay ngang tầm mắt (Eye-level), ống kính điện ảnh 50mm, chuyển động trượt êm ái (Smooth gimbal push-in)"

        lines = [
            f"[Beat 1 (0-3s) - Thiết lập]: {b1}",
            f"[Beat 2 (3-6s) - Kịch tính & Cảm xúc]: {b2}",
            f"[Beat 3 (6-10s) - Lắng đọng & Nối tiếp]: {b3}",
            f"[Chuyển động Camera]: {cam_text}"
        ]

        formatted_body = "\n".join(lines)
        return assemble_guarded_prompt(formatted_body, closed_lips_guard, audio_guard)


if __name__ == "__main__":
    test_prompt = "Nàng Kiều ngồi bên song cửa ngắm trăng. Tay nâng chén trà nguội. Tiếng thở dài nhẹ trong đêm."
    pacer_result = CinematicShotPacer.format_3beat_prompt(test_prompt, camera_motion="Slow cinematic push-in 50mm")
    print("--- 3-BEAT PACED PROMPT ---")
    print(pacer_result)

    print("\n--- DIAGNOSTIC PROMPT HEALING ---")
    healed, actions = DiagnosticPromptHealer.heal_prompt(
        test_prompt,
        detected_errors=["camera_drift", "character_drift"],
        character_name="Thúy Kiều",
        cycle=1
    )
    print(f"Applied therapies: {actions}")
    print(f"Healed prompt: {healed}")
