"""
Xử lý ký PDF với layout Premium Acrobat Style V13
- Border mảnh nhẹ (0.5pt)
- Logo rõ hơn (brightness +20%)
- Layout chuyên nghiệp: Công ty trên, Logo giữa, Thông tin ký dưới
- Full DN Extraction: MST, Email, Tổ chức từ chứng thư
"""

import os
import io
import sys
import json
import unicodedata
from pathlib import Path
from datetime import datetime
from PIL import Image, ImageDraw, ImageOps
from pyhanko.pdf_utils import generic
from pyhanko.pdf_utils.reader import PdfFileReader
from pyhanko.pdf_utils.writer import PdfFileWriter
from pyhanko.sign import fields


def _load_runtime_company_info():
    """Đọc thông tin công ty từ settings.json cạnh EXE/mã nguồn để dựng stamp chữ ký."""
    settings_path = ""
    try:
        from core.utils import get_app_data_path

        settings_path = get_app_data_path("settings.json")
    except Exception:
        script_dir = os.path.dirname(os.path.abspath(__file__))
        project_root = os.path.dirname(script_dir)
        runtime_root = os.path.dirname(sys.executable) if getattr(sys, "frozen", False) else project_root
        settings_path = os.path.join(runtime_root, "settings.json")

    if not settings_path or not os.path.exists(settings_path):
        return {}

    try:
        with open(settings_path, "r", encoding="utf-8") as file_obj:
            data = json.load(file_obj)
        company_info = data.get("company", {}) if isinstance(data, dict) else {}
        return company_info if isinstance(company_info, dict) else {}
    except Exception:
        return {}


def _signature_display_options():
    """Read visual signature options saved from Contract Settings."""
    company_info = _load_runtime_company_info()
    options = company_info.get("signature_options", {})
    if not isinstance(options, dict):
        options = {}
    return {
        "hide_logo": bool(options.get("hide_logo", False)),
        "hide_background": bool(options.get("hide_background", False)),
        "hide_border": bool(options.get("hide_border", False)),
        "hide_check": bool(options.get("hide_check", False)),
    }


def _rects_intersect(a, b):
    ax0, ay0, ax1, ay1 = a
    bx0, by0, bx1, by1 = b
    return ax0 < bx1 and ax1 > bx0 and ay0 < by1 and ay1 > by0


def _confirm_if_signature_covers_representative(pdf_path, page_num, sig_box, representative_name):
    """Warn before placing the visible signature over the representative name on the PDF."""
    representative_name = str(representative_name or "").strip()
    if not representative_name:
        return True

    try:
        from pdfminer.high_level import extract_pages
        from pdfminer.layout import LAParams, LTTextContainer

        target_norm = SignatureLayoutV13._remove_accents(representative_name).lower()
        for current_page, page_layout in enumerate(extract_pages(pdf_path, laparams=LAParams())):
            if current_page != page_num:
                continue
            for element in page_layout:
                if not isinstance(element, LTTextContainer):
                    continue
                text_norm = SignatureLayoutV13._remove_accents(element.get_text()).lower()
                if target_norm and target_norm in text_norm:
                    text_box = (element.x0, element.y0, element.x1, element.y1)
                    if _rects_intersect(sig_box, text_box):
                        import ctypes
                        message = (
                            "Khung chữ ký số đang đè lên tên người đại diện trên file hợp đồng:\n\n"
                            f"{representative_name}\n\n"
                            "Bạn có muốn tiếp tục ký không?"
                        )
                        result = ctypes.windll.user32.MessageBoxW(
                            0,
                            message,
                            "Cảnh báo vị trí chữ ký số",
                            0x00000004 | 0x00000030 | 0x00000100,
                        )
                        return result == 6
            break
        return True
    except Exception as exc:
        print(f"   [Warn] Không kiểm tra được vùng tên người đại diện: {exc}")
        return True


class SignatureLayoutV13:
    """Layout chữ ký Premium Acrobat Style V13"""
    
    # Khôi phục kích thước ngang 65 theo bản thiết kế được khen đẹp
    # Tăng nhẹ chiều cao lên 38 để chữ Ngày Tháng dưới cùng không bị cắt (do Logo dòng trên ép xuống)
    SIGNATURE_WIDTH_MM = 65
    SIGNATURE_HEIGHT_MM = 38
    WIDTH = int(SIGNATURE_WIDTH_MM * 72 / 25.4)
    HEIGHT = int(SIGNATURE_HEIGHT_MM * 72 / 25.4)
    
    # Màu sắc
    COLOR_BORDER = (147, 197, 253, 255)
    COLOR_TEXT = (17, 24, 39, 255)
    COLOR_COMPANY = (29, 78, 216, 255)
    COLOR_BG_TOP = (248, 250, 252, 245)
    COLOR_BG_BOTTOM = (239, 246, 255, 250)
    COLOR_PANEL = (255, 255, 255, 206)
    COLOR_VALID = (22, 163, 74, 225)
    CORNER_RADIUS = 18
    
    # Độ dày border (mảnh nhẹ - dùng integer tối thiểu 1)
    BORDER_WIDTH = 0.5
    
    # Kích thước logo hiển thị (px) — cố định 140px cho tất cả hình
    LOGO_DISPLAY_SIZE = 140  # px, cứng — không nhân SCALE
    # Tỷ lệ watermark so với box (0..1)
    WATERMARK_SCALE = 0.6
    WATERMARK_OPACITY = 0.15

    def __init__(self, logo_path="assets/logo.png", background_path="assets/signature_bg.png"):
        """Khởi tạo layout với đường dẫn logo và nền chữ ký."""
        self.logo_path = logo_path
        self.background_path = background_path
        self.logo_image = None
        self.background_image = None
        self._load_logo()
        self._load_background()

    @staticmethod
    def _unique_paths(paths):
        unique = []
        seen = set()
        for path in paths:
            if not path:
                continue
            normalized = os.path.normcase(os.path.abspath(path))
            if normalized in seen:
                continue
            seen.add(normalized)
            unique.append(path)
        return unique

    @staticmethod
    def _runtime_root(script_dir: str) -> str:
        if getattr(sys, "frozen", False):
            return os.path.dirname(sys.executable)
        return os.path.dirname(script_dir)
    
    def _load_logo(self):
        """
        Tải logo từ file. Thứ tự ưu tiên:
        1. Logo tùy chỉnh do người dùng chọn (bền vững, nằm cạnh EXE/project)
        2. Legacy custom logo trong source
        3. Logo mặc định của phần mềm
        4. Đường dẫn truyền trực tiếp/fallback tương đối
        """
        try:
            # Thư mục chứa sign_pdf.py (luôn là sign/)
            script_dir = os.path.dirname(os.path.abspath(__file__))
            # Thư mục gốc project (parent của sign/)
            project_root = os.path.dirname(script_dir)
            runtime_root = self._runtime_root(script_dir)

            candidates = []

            # 1. Tuyệt đối từ logo_path được truyền vào
            if os.path.isabs(self.logo_path):
                candidates.append(self.logo_path)

            # 2. Logo tùy chỉnh do người dùng chọn
            candidates.append(os.path.join(runtime_root, "sign", "assets", "logo.png"))
            candidates.append(os.path.join(project_root, "sign", "assets", "logo.png"))

            # 3. Logo mặc định của phần mềm
            candidates.append(os.path.join(runtime_root, "gui", "assets", "logofull.png"))
            candidates.append(os.path.join(runtime_root, "gui", "assets", "app_icon.png"))
            candidates.append(os.path.join(project_root, "src", "gui", "assets", "logofull.png"))
            candidates.append(os.path.join(project_root, "src", "gui", "assets", "app_icon.png"))
            candidates.append(os.path.join(project_root, "gui", "assets", "logofull.png"))
            candidates.append(os.path.join(project_root, "gui", "assets", "app_icon.png"))

            # 4. Legacy logo cũ trong source (chỉ dùng làm fallback tương thích)
            candidates.append(os.path.join(project_root, "src", "sign", "assets", "logo.png"))

            # 5. Tương đối từ script_dir (sign/)
            candidates.append(os.path.join(script_dir, self.logo_path))

            # 6. Tương đối từ CWD
            candidates.append(os.path.abspath(self.logo_path))

            candidates = self._unique_paths(candidates)

            for path in candidates:
                if os.path.exists(path):
                    self.logo_image = Image.open(path).convert("RGBA")
                    self.logo_image = self._brighten_image(self.logo_image, brightness=1.2)
                    print(f"   [Logo] ✅ Đã tải logo từ: {path}")
                    return

            print(f"   [Logo] ⚠️  Không tìm thấy logo. Đã tìm tại:")
            for p in candidates:
                print(f"          {p}")
            self.logo_image = None

        except Exception as e:
            print(f"   [Logo] Lỗi khi tải logo: {e}")
            self.logo_image = None

    def _load_background(self):
        """Tải nền chữ ký tùy chỉnh nếu người dùng đã cấu hình."""
        try:
            script_dir = os.path.dirname(os.path.abspath(__file__))
            project_root = os.path.dirname(script_dir)
            runtime_root = self._runtime_root(script_dir)

            candidates = []
            if os.path.isabs(self.background_path):
                candidates.append(self.background_path)

            candidates.extend(
                [
                    os.path.join(runtime_root, "sign", "assets", "signature_bg.png"),
                    os.path.join(project_root, "sign", "assets", "signature_bg.png"),
                    os.path.join(project_root, "src", "sign", "assets", "signature_bg.png"),
                    os.path.join(script_dir, self.background_path),
                    os.path.abspath(self.background_path),
                ]
            )

            for path in self._unique_paths(candidates):
                if os.path.exists(path):
                    self.background_image = Image.open(path).convert("RGBA")
                    print(f"   [SignatureBG] ✅ Đã tải nền chữ ký từ: {path}")
                    return

            self.background_image = None

        except Exception as e:
            print(f"   [SignatureBG] Lỗi khi tải nền chữ ký: {e}")
            self.background_image = None
    
    @staticmethod
    def _brighten_image(img, brightness=1.2):
        """Tăng độ sáng hình ảnh"""
        from PIL import ImageEnhance
        enhancer = ImageEnhance.Brightness(img)
        return enhancer.enhance(brightness)

    def _build_background_layer(self, canvas_w, canvas_h, scale, hide_background=False, hide_check=False, hide_border=False):
        radius = int(self.CORNER_RADIUS * scale)
        if hide_background:
            background = Image.new("RGBA", (canvas_w, canvas_h), (255, 255, 255, 235))
        elif self.background_image:
            background = self._build_custom_background(canvas_w, canvas_h, radius)
        else:
            background = self._build_default_background(canvas_w, canvas_h, radius, scale)

        overlay = Image.new("RGBA", (canvas_w, canvas_h), (255, 255, 255, 0))
        overlay_draw = ImageDraw.Draw(overlay)
        inset = int(5 * scale)
        overlay_draw.rounded_rectangle(
            (inset, inset, canvas_w - inset, canvas_h - inset),
            radius=max(radius - int(4 * scale), 0),
            fill=self.COLOR_PANEL,
        )
        background = Image.alpha_composite(background, overlay)

        bg_draw = ImageDraw.Draw(background)
        if not hide_border:
            bg_draw.rounded_rectangle(
                (0, 0, canvas_w - 1, canvas_h - 1),
                radius=radius,
                outline=self.COLOR_BORDER,
                width=max(1, int(round(self.BORDER_WIDTH * scale))),
            )
        if not hide_check:
            self._draw_valid_badge(bg_draw, canvas_w, scale)
        return background

    def _build_default_background(self, canvas_w, canvas_h, radius, scale):
        gradient = Image.new("RGBA", (canvas_w, canvas_h), (255, 255, 255, 0))
        gradient_draw = ImageDraw.Draw(gradient)
        for y_pos in range(canvas_h):
            ratio = y_pos / max(canvas_h - 1, 1)
            line_color = tuple(
                int(self.COLOR_BG_TOP[index] * (1 - ratio) + self.COLOR_BG_BOTTOM[index] * ratio)
                for index in range(4)
            )
            gradient_draw.line((0, y_pos, canvas_w, y_pos), fill=line_color)

        mask = Image.new("L", (canvas_w, canvas_h), 0)
        ImageDraw.Draw(mask).rounded_rectangle((0, 0, canvas_w - 1, canvas_h - 1), radius=radius, fill=255)

        background = Image.new("RGBA", (canvas_w, canvas_h), (255, 255, 255, 0))
        background.paste(gradient, (0, 0), mask)

        return background

    def _build_custom_background(self, canvas_w, canvas_h, radius):
        fitted = ImageOps.fit(self.background_image.copy(), (canvas_w, canvas_h), Image.Resampling.LANCZOS)
        softened = Image.new("RGBA", (canvas_w, canvas_h), (255, 255, 255, 188))
        fitted = Image.alpha_composite(fitted, softened)

        mask = Image.new("L", (canvas_w, canvas_h), 0)
        ImageDraw.Draw(mask).rounded_rectangle((0, 0, canvas_w - 1, canvas_h - 1), radius=radius, fill=255)

        background = Image.new("RGBA", (canvas_w, canvas_h), (255, 255, 255, 0))
        background.paste(fitted, (0, 0), mask)
        return background

    def _draw_valid_badge(self, draw, canvas_w, scale):
        badge_r = max(20, int(8 * scale))
        badge_margin = max(16, int(6 * scale))
        center_x = canvas_w - badge_margin - badge_r
        center_y = badge_margin + badge_r

        draw.ellipse(
            (center_x - badge_r, center_y - badge_r, center_x + badge_r, center_y + badge_r),
            fill=self.COLOR_VALID,
        )
        check_width = max(4, int(2 * scale))
        check_points = [
            (center_x - int(0.38 * badge_r), center_y + int(0.02 * badge_r)),
            (center_x - int(0.08 * badge_r), center_y + int(0.32 * badge_r)),
            (center_x + int(0.42 * badge_r), center_y - int(0.32 * badge_r)),
        ]
        draw.line(check_points, fill=(255, 255, 255, 255), width=check_width)
    
    @staticmethod
    def _remove_accents(text):
        """Loại bỏ dấu từ text"""
        if not text:
            return ""
        return ''.join(
            c for c in unicodedata.normalize('NFD', text)
            if unicodedata.category(c) != 'Mn'
        )
    
    def draw_signature(self, signer_info):
        """
        Vẽ con dấu chữ ký với layout Premium (Hi-Res & Auto Font Fit)
        """
        SCALE = 4
        canvas_w = self.WIDTH * SCALE
        canvas_h = self.HEIGHT * SCALE
        options = _signature_display_options()
        hide_logo = bool(signer_info.get("hide_logo", options["hide_logo"]))
        hide_background = bool(signer_info.get("hide_background", options["hide_background"]))
        hide_border = bool(signer_info.get("hide_border", options["hide_border"]))
        hide_check = bool(signer_info.get("hide_check", options["hide_check"]))
        
        # Tạo nền chữ ký theo brand app hoặc nền tùy chỉnh của người dùng.
        img = self._build_background_layer(canvas_w, canvas_h, SCALE, hide_background, hide_check, hide_border)
        draw = ImageDraw.Draw(img)

        # Các thông số layout
        margin = 8 * SCALE
        logo_size = self.LOGO_DISPLAY_SIZE  # px cố định 140, không nhân SCALE

        # Watermark (mờ, nằm giữa khung)
        if self.logo_image and not hide_logo:
            try:
                wm = self.logo_image.copy()
                wm_w = int(canvas_w * self.WATERMARK_SCALE)
                wm_h = int(canvas_h * self.WATERMARK_SCALE)
                wm.thumbnail((wm_w, wm_h), Image.Resampling.LANCZOS)
                alpha = int(255 * self.WATERMARK_OPACITY)
                if wm.mode != 'RGBA':
                    wm = wm.convert('RGBA')
                wm_mask = wm.split()[3].point(lambda p: alpha)
                wm_pos = ((canvas_w - wm.width) // 2, (canvas_h - wm.height) // 2)
                img.paste(wm, wm_pos, wm_mask)
            except Exception as e:
                print(f"Lỗi khi dán watermark: {e}")

        # Logo trên cùng, căn giữa
        logo_y = 6 * SCALE
        if self.logo_image and not hide_logo:
            try:
                logo = self.logo_image.copy()
                # Tăng kích để rõ hơn nếu cần
                logo.thumbnail((logo_size, logo_size), Image.Resampling.LANCZOS)
                logo_x = (canvas_w - logo.width) // 2
                img.paste(logo, (logo_x, logo_y), logo)
                content_start_y = logo_y + logo.height + 2 * SCALE  # cách logo 2px
            except Exception as e:
                print(f"Lỗi khi dán logo: {e}")
                content_start_y = logo_y + 6 * SCALE
        else:
            content_start_y = logo_y + 6 * SCALE

        # Thông tin người ký và công ty
        company = signer_info.get("company", "CÔNG TY CỔ PHẦN")

        try:
            from PIL import ImageFont
            
            # Hàm tự động co font cho vừa chiều rộng ảnh
            def get_fitting_font(text, initial_sz, max_w):
                if not text: return ImageFont.load_default()
                sz = initial_sz * SCALE
                # Hạ sâu hơn để các tên siêu dài vẫn có chỗ
                while sz > 2 * SCALE:
                    try:
                        f = ImageFont.truetype("arial.ttf", sz)
                        try:
                            w = draw.textlength(text, font=f)
                        except AttributeError:
                            w = f.getsize(text)[0]
                        if w <= max_w:
                            return f
                    except Exception:
                        return ImageFont.load_default()
                    sz -= int(0.5 * SCALE)
                try:
                    return ImageFont.truetype("arial.ttf", sz)
                except Exception:
                    return ImageFont.load_default()

            def get_text_height(font, text, _draw=draw):
                try:
                    return int(_draw.textbbox((0, 0), text, font=font)[3] - _draw.textbbox((0, 0), text, font=font)[1])
                except AttributeError:
                    return int(font.getsize(text)[1])

            # Giới hạn chiều rộng chữ thêm padding an toàn
            max_text_width = canvas_w - (margin * 2) - int(8 * SCALE)
            y = content_start_y
            
            raw_comp = company.strip()
            if not raw_comp:
                # Nếu không có tên công ty thì fallback lấy tên người ký
                raw_comp = signer_info.get("name", "Người ký").strip()

            # CHỈ VẼ TÊN CÔNG TY (TỐI ĐA 2 DÒNG: TRÊN NGẮN - DƯỚI DÀI)
            if raw_comp:
                # Tìm điểm cắt tốt nhất~1/3 tổng ký tự để dòng trên ngắn hơn
                words = raw_comp.split()
                best_split = 1  # mặc định cắt sau từ đầu tiên
                target_len = len(raw_comp) * 0.38  # dòng 1 chiếm ~38% tổng chiều dài
                cum = 0
                for i, w in enumerate(words):
                    cum += len(w) + (1 if i > 0 else 0)
                    if cum >= target_len:
                        best_split = i + 1
                        break
                line1 = " ".join(words[:best_split])
                line2 = " ".join(words[best_split:])

                # font dùng chung cho cả 2 dòng: fit theo dòng DÀI HƠN
                longer_line = line2 if len(line2) >= len(line1) else line1
                font_company = get_fitting_font(longer_line, 12, max_text_width)
                
                line_h = get_text_height(font_company, line1)

                # Căn giữa từng dòng
                try:
                    w1 = draw.textlength(line1, font=font_company)
                    w2 = draw.textlength(line2, font=font_company)
                except AttributeError:
                    w1 = font_company.getsize(line1)[0]
                    w2 = font_company.getsize(line2)[0]

                x1 = (canvas_w - w1) // 2
                x2 = (canvas_w - w2) // 2
                draw.text((x1, y), line1, fill=self.COLOR_COMPANY, font=font_company)
                y += line_h + int(1 * SCALE)
                draw.text((x2, y), line2, fill=self.COLOR_COMPANY, font=font_company)
                y += get_text_height(font_company, line2) + int(3 * SCALE)

                # Dòng Mã số thuế (MST) - nếu có
                raw_uid = signer_info.get("user_id", "")
                if raw_uid:
                    # Trích xuất số MST từ 'MST:0311942758' hoặc '0311942758'
                    mst_num = raw_uid.replace("MST:", "").replace("mst:", "").strip()
                    mst_txt = f"Mã số thuế: {mst_num}"
                    font_mst = get_fitting_font(mst_txt, 7, max_text_width)
                    try:
                        wm = draw.textlength(mst_txt, font=font_mst)
                    except AttributeError:
                        wm = font_mst.getsize(mst_txt)[0]
                    xm = (canvas_w - wm) // 2
                    draw.text((xm, y), mst_txt, fill=(40, 40, 40, 255), font=font_mst)
                    y += get_text_height(font_mst, mst_txt) + int(2 * SCALE)

                # Dòng Ngày ký: in nghiêng (italic), nhỏ, căn giữa
                timestamp = signer_info.get("timestamp")
                if timestamp:
                    if isinstance(timestamp, datetime):
                        date_str = timestamp.strftime("%d/%m/%Y %H:%M:%S")
                    else:
                        date_str = str(timestamp)
                    date_txt = f"Ngày ký: {date_str}"
                    date_sz = 6 * SCALE
                    # Dùng đường dẫn tuyệt đối đến font Arial Italic trên Windows
                    italic_font_paths = [
                        "C:/Windows/Fonts/ariali.ttf",
                        "C:/Windows/Fonts/ARIALI.TTF",
                        "ariali.ttf",
                    ]
                    font_date = None
                    for fp in italic_font_paths:
                        try:
                            font_date = ImageFont.truetype(fp, date_sz)
                            break
                        except Exception:
                            continue
                    if font_date is None:
                        try:
                            font_date = ImageFont.truetype("arial.ttf", date_sz)
                        except Exception:
                            font_date = ImageFont.load_default()
                    try:
                        wd = draw.textlength(date_txt, font=font_date)
                    except AttributeError:
                        wd = font_date.getsize(date_txt)[0]
                    xd = (canvas_w - wd) // 2
                    draw.text((xd, y), date_txt, fill=(100, 100, 100, 255), font=font_date)

        except Exception as e:
            print(f"Lỗi vẽ text: {e}")

        return img
    
    def image_to_pdf_bytes(self, img):
        """Chuyển ảnh thành PDF bytes"""
        try:
            pdf_bytes = io.BytesIO()
            img.save(pdf_bytes, format="PDF")
            pdf_bytes.seek(0)
            return pdf_bytes.getvalue()
        except Exception as e:
            print(f"Lỗi chuyển ảnh sang PDF: {e}")
            return None


def find_keyword_position(pdf_path, keyword):
    """
    Tìm vị trí của từ khóa trong PDF.
    Trả về: (x0, x1, y, page_num) hoặc None nếu không tìm thấy.
    x0, x1 là cạnh trái và phải của text box chứa từ khóa.
    """
    try:
        from pdfminer.high_level import extract_pages
        from pdfminer.layout import LTTextBox, LAParams
        
        keyword_norm = SignatureLayoutV13._remove_accents(keyword).lower()
        
        for page_num, page_layout in enumerate(extract_pages(pdf_path, laparams=LAParams())):
            for element in page_layout:
                if isinstance(element, LTTextBox):
                    text = element.get_text()
                    text_norm = SignatureLayoutV13._remove_accents(text).lower()
                    
                    if keyword_norm in text_norm:
                        x0 = element.x0
                        x1 = element.x1
                        # Chuẩn PDF bottom-up (y0 là cạnh dưới của text box)
                        y  = element.y0
                        return (x0, x1, y, page_num)
        
        return None
    except Exception as e:
        print(f"Lỗi tìm từ khóa: {e}")
        return None


def create_signature_field(pdf_path, keyword, signer_info, output_path=None):
    """
    Tạo và ký chữ ký vào PDF tại vị trí từ khóa
    
    Args:
        pdf_path: Đường dẫn PDF gốc
        keyword: Từ khóa tìm vị trí ký (ví dụ: "Bên A", "Bên B")
        signer_info: Dict thông tin người ký
        output_path: Đường dẫn file output (nếu None thì overwrite)
    
    Returns:
        Đường dẫn file đã ký hoặc None nếu lỗi
    """
    try:
        # Tìm vị trí từ khóa
        pos_info = find_keyword_position(pdf_path, keyword)
        if not pos_info:
            print(f"Không tìm thấy từ khóa '{keyword}' trong PDF")
            return None
        
        x, y, page_num = pos_info
        
        # Vẽ con dấu layout Premium
        layout_engine = SignatureLayoutV13()
        sig_image = layout_engine.draw_signature(signer_info)
        
        # Chuyển ảnh sang PDF bytes
        sig_pdf_bytes = layout_engine.image_to_pdf_bytes(sig_image)
        if not sig_pdf_bytes:
            return None
        
        # Đặt offset dưới từ khóa
        x_offset = x
        y_offset = y - 60  # 60 points dưới từ khóa
        
        print(f"✓ Chữ ký '{keyword}' sẽ được đặt tại: ({x_offset}, {y_offset}) - Trang {page_num + 1}")
        
        # TODO: Gọi win_signer.py để ký thực tế
        # Tạm thời trả về thông tin vị trí
        return {
            'pdf_path': pdf_path,
            'position': (x_offset, y_offset, page_num),
            'signature_bytes': sig_pdf_bytes,
            'signer_info': signer_info
        }
    
    except Exception as e:
        print(f"Lỗi tạo chữ ký: {e}")
        return None


class PDFSigner:
    """Class wrapper for compatibility with main.py"""
    def __init__(self, in_file, out_file):
        self.in_file = in_file
        self.out_file = out_file
        self.layout_engine = SignatureLayoutV13()

    def get_signature_position(self, keyword):
        """Tìm vị trí từ khóa trong PDF"""
        return find_keyword_position(self.in_file, keyword)

    def draw_signature_appearance(self, signer_info):
        """Tạo layout chữ ký và trả về bytes PDF"""
        img = self.layout_engine.draw_signature(signer_info)
        if img:
            return self.layout_engine.image_to_pdf_bytes(img)
        return None
        
    def sign_pdf(self, cert_info, keyword="Bên A"):
        """Tích hợp để main.py có thể gọi sign_pdf"""
        try:
            pass
        except Exception as e:
            print(f"Lỗi: {e}")

    @staticmethod
    def _check_cert_validity(signer):
        """
        Kiểm tra chứng thư số trước khi ký:
        1. Còn hạn sử dụng không?
        2. Chưa bị thu hồi (OCSP query)?

        Raise Exception nếu cert hết hạn hoặc bị thu hồi.
        Chỉ cảnh báo (không block) nếu OCSP server không trả lời.
        """
        from datetime import datetime, timezone
        from asn1crypto import x509 as asn1_x509

        cert = signer.signing_cert
        try:
            # ── 1. Kiểm tra ngày hiệu lực ────────────────────────────────────
            validity = cert['tbs_certificate']['validity']
            not_before = validity['not_before'].native
            not_after  = validity['not_after'].native
            now = datetime.now(timezone.utc)

            if now < not_before:
                raise Exception(
                    f"⛔ Chứng thư chưa có hiệu lực!\n"
                    f"   Hiệu lực từ: {not_before.strftime('%d/%m/%Y %H:%M')}"
                )

            if now > not_after:
                raise Exception(
                    f"⛔ Chứng thư ĐÃ HẾT HẠN!\n"
                    f"   Hết hạn lúc: {not_after.strftime('%d/%m/%Y %H:%M')}\n"
                    f"   Vui lòng gia hạn chứng thư tại CA cấp phát."
                )

            days_left = (not_after - now).days
            print(f"   [OCSP] Chứng thư còn hiệu lực: {days_left} ngày"
                  f" (hết hạn {not_after.strftime('%d/%m/%Y')})")
            if days_left <= 30:
                print(f"   [OCSP] ⚠️  CẢNH BÁO: Chứng thư sắp hết hạn trong {days_left} ngày!")

        except Exception as e:
            if "⛔" in str(e):
                raise   # Lỗi hết hạn → block ký
            print(f"   [OCSP] Cảnh báo: Không đọc được ngày hiệu lực: {e}")

        # ── 2. OCSP query ─────────────────────────────────────────────────────
        try:
            import urllib.request, urllib.error

            # Lấy OCSP URL từ AIA extension
            ocsp_url = None
            try:
                for ext in cert['tbs_certificate']['extensions']:
                    if ext['extn_id'].native == 'authority_information_access':
                        for ad in ext['extn_value'].parsed:
                            if ad['access_method'].native == 'ocsp':
                                ocsp_url = ad['access_location'].native
                                break
                        if ocsp_url:
                            break
            except Exception:
                pass

            if not ocsp_url:
                print("   [OCSP] Không tìm thấy OCSP URL trong cert — bỏ qua kiểm tra thu hồi.")
                return

            print(f"   [OCSP] Đang kiểm tra tình trạng thu hồi tại: {ocsp_url} ...")

            # Tạo OCSP request đơn giản bằng cryptography library
            from cryptography import x509 as crypto_x509
            from cryptography.hazmat.primitives import hashes, serialization
            from cryptography.x509 import ocsp as crypto_ocsp

            # Load cert và issuer cert từ chain
            cert_der = signer.signing_cert.dump()
            crypto_cert = crypto_x509.load_der_x509_certificate(cert_der)

            # Cần issuer cert để tạo OCSP request; lấy từ other_certs nếu có
            issuer_cert = None
            other_certs = getattr(signer, '_cert_chain', []) or []
            issuer_dn = signer.signing_cert.issuer
            for ca in other_certs:
                if ca.subject == issuer_dn:
                    issuer_cert = crypto_x509.load_der_x509_certificate(ca.dump())
                    break

            if not issuer_cert:
                print("   [OCSP] Không tìm thấy issuer cert trong chain — bỏ qua OCSP.")
                return

            # Build OCSP request
            builder = crypto_ocsp.OCSPRequestBuilder()
            builder = builder.add_certificate(crypto_cert, issuer_cert, hashes.SHA256())
            req = builder.build()
            req_data = req.public_bytes(serialization.Encoding.DER)

            # Gửi HTTP POST đến OCSP server (timeout 6s)
            http_req = urllib.request.Request(
                ocsp_url,
                data=req_data,
                headers={"Content-Type": "application/ocsp-request"}
            )
            with urllib.request.urlopen(http_req, timeout=6) as resp:
                resp_data = resp.read()

            # Parse OCSP response
            ocsp_resp = crypto_ocsp.load_der_ocsp_response(resp_data)
            status = ocsp_resp.response_status

            from cryptography.x509.ocsp import OCSPResponseStatus
            from cryptography.x509.ocsp import OCSPCertStatus as CertificateStatus
            if status != OCSPResponseStatus.SUCCESSFUL:
                print(f"   [OCSP] ⚠️  OCSP trả về trạng thái không thành công: {status}")
                return

            cert_status = ocsp_resp.certificate_status
            if cert_status == CertificateStatus.GOOD:
                print("   [OCSP] ✅ Chứng thư CHƯA bị thu hồi — an toàn để ký.")
            elif cert_status == CertificateStatus.REVOKED:
                revoke_time = ocsp_resp.revocation_time
                raise Exception(
                    f"⛔ Chứng thư ĐÃ BỊ THU HỒI!\n"
                    f"   Thời điểm thu hồi: {revoke_time.strftime('%d/%m/%Y %H:%M')}\n"
                    f"   Liên hệ CA để được hỗ trợ."
                )
            else:
                print(f"   [OCSP] ⚠️  Trạng thái không xác định: {cert_status}")

        except Exception as e:
            if "⛔" in str(e):
                raise   # Cert bị thu hồi → block ký
            # OCSP server không trả lời → chỉ cảnh báo, không block
            print(f"   [OCSP] ⚠️  Không kiểm tra được OCSP (tiếp tục ký): {e}")

    def sign_pdf_native(self, in_file, out_file, keyword="Bên A", signer=None, offsets=(0, -100), ts_url=None, use_logo=True):
        """
        Ký PDF thực tế sử dụng WindowsStoreSigner hoặc PKCS11Signer từ win_signer.py

        Args:
            in_file:   Đường dẫn tệp PDF đầu vào
            out_file:  Đường dẫn tệp PDF đầu ra (đã ký)
            keyword:   Từ khóa xác định vị trí ký (vd: 'Bên A')
            signer:    Đối tượng signer từ win_signer (WindowsStoreSigner hoặc PKCS11Signer)
            offsets:   (x_offset, y_offset) tương đối so với vị trí từ khóa (points)
            ts_url:    URL của TSA (Time Stamping Authority). Nếu None, không nhúng timestamp.
            use_logo:  Có chèn hình ảnh logo vào chữ ký không. Nếu False, lấy giao diện tối giản.
        """
        import asyncio
        from pyhanko.sign import signers, fields
        from pyhanko.sign.fields import SigSeedSubFilter
        from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter
        from pyhanko.sign.signers.pdf_signer import PdfSignatureMetadata
        from datetime import datetime

        if signer is None:
            raise ValueError("Cần truyền đối tượng signer (WindowsStoreSigner)")

        # ── PRE-CHECK: Tạm tắt — bật lại sau khi hoàn thiện phần mềm ──────────
        # self._check_cert_validity(signer)

        # Vẽ cấu trúc con dấu để lấy kích thước trước (cần w_pt để căn giữa)
        layout_args = {} if use_logo else {"logo_path": "NON_EXISTENT_LOGO_FORCE_FALLBACK"}
        layout = SignatureLayoutV13(**layout_args)
        w_pt, h_pt = layout.WIDTH, layout.HEIGHT

        # 1. Tìm vị trí từ khóa trong PDF
        pos_info = find_keyword_position(in_file, keyword)
        if not pos_info:
            print(f"   [Warn] Không tìm thấy từ khóa '{keyword}'. Ký tại vị trí mặc định.")
            x0, x1, y, page_num = 350, 550, 600, 0
        else:
            x0, x1, y, page_num = pos_info

        # Căn giữa chữ ký theo chiều ngang của text box từ khóa
        box_center = (x0 + x1) / 2
        sig_x = max(box_center - w_pt / 2, 10)
        print(f"   [Log] Text box Keyword: X=[{x0:.1f},{x1:.1f}] | Tâm={box_center:.1f} | Sig_X={sig_x:.1f}")
        
        
        # y đang là cạnh dưới của từ khóa (chuẩn PDF bottom-up).
        # Nếu offset âm, ta trừ đi để đỉnh của khung chữ ký nằm dưới từ khóa.
        # Ở main.py có truyền y_off = -100, tức là muốn hạ xuống 100 pt. (Tuy nhiên 100pt hơi sâu, ta tự động giới hạn nếu quá tay hoặc cứ theo thông số)
        # Thông thường h_pt khoảng 141.2pt.
        x_off, y_off = offsets
        sig_top = y + y_off  
        sig_y = max(sig_top - h_pt, 10)
        
        print(f"   [Log] Vị trí ký: Gốc từ khóa Y={y:.1f}. Khung chữ ký Y_bottom={sig_y:.1f} → Y_top={sig_top:.1f}")

        # 2. Thông tin người ký cho visual stamp
        company_info = _load_runtime_company_info()

        _mst = getattr(signer, 'mst', '') or str(company_info.get("tax_id") or "").strip()
        if _mst:
            _user_id = f"MST:{_mst}"
        else:
            _user_id = getattr(signer, 'user_id', getattr(signer, 'serial_number', ''))
        print(f"   [Debug] signer.mst={repr(_mst)} | _user_id={repr(_user_id)}")

        company_name = str(company_info.get("legal_name") or getattr(signer, 'org_name', '') or "").strip()
        signer_display_name = str(
            getattr(signer, 'common_name', getattr(signer, 'common_name_clean', 'Người ký')) or "Người ký"
        ).strip()
        signer_info = {
            "name":      signer_display_name,
            "company":   company_name,
            "email":     getattr(signer, 'email', ''),
            "reason":    "Ký số tài liệu",
            "timestamp": datetime.now(),
            "user_id":   _user_id,
        }

        # 3. Vẽ ảnh con dấu chữ ký
        sig_image = layout.draw_signature(signer_info)
        box = (sig_x, sig_y, sig_x + w_pt, sig_y + h_pt)
        if not _confirm_if_signature_covers_representative(
            in_file,
            page_num,
            box,
            company_info.get("representative", ""),
        ):
            raise Exception("Người dùng đã hủy ký vì khung chữ ký số che tên người đại diện.")

        # 4. Metadata chữ ký PAdES
        # Dùng UUID ngắn để field_name luôn unique (tránh lỗi đặt trùng field khi ký nhiều lần)
        import uuid
        uid_short = uuid.uuid4().hex[:6]
        field_name = f"Sig_{keyword.replace(' ', '_')}_{page_num}_{uid_short}"

        # embed_validation_info cần ValidationContext — thử khởi tạo trước
        _embed_val = False
        _vc = None
        try:
            from pyhanko_certvalidator import ValidationContext
            _vc = ValidationContext(allow_fetching=True, revocation_mode="soft-fail")
            _embed_val = True
        except Exception as _e_vc:
            print(f"   [LTV] Không thể khởi tạo ValidationContext: {_e_vc} — tắt embed_validation_info")

        sig_meta = PdfSignatureMetadata(
            field_name=field_name,
            reason="Ký số tài liệu",
            location="Việt Nam",
            certify=False,
            subfilter=SigSeedSubFilter.PADES,
            embed_validation_info=_embed_val,
            use_pades_lta=bool(ts_url) and _embed_val,
        )

        # 5. Đọc toàn bộ nội dung vào bytes trước để tránh giữ file handle trên Windows
        #    (vấn đề hay gặp khi ký nhiều bước: step_output của bước trước = input của bước sau)
        with open(in_file, "rb") as _f:
            in_bytes = _f.read()
        in_stream = io.BytesIO(in_bytes)

        # 6. Thêm signature field
        writer = IncrementalPdfFileWriter(in_stream, strict=False)
        fields.append_signature_field(
            writer,
            sig_field_spec=fields.SigFieldSpec(
                sig_field_name=field_name,
                on_page=page_num,
                box=box,
            ),
        )

        # 7. Tạo stamp appearance từ ảnh PIL
        stamp_style = self._make_image_stamp_style(sig_image)

        # 8. Khởi tạo Timestamper cho LTV (TSA chính — thường là TSA VN theo CA)
        timestamper = None
        if ts_url:
            try:
                from pyhanko.sign.timestamps import HTTPTimeStamper
                timestamper = HTTPTimeStamper(ts_url)
                print(f"   [LTV] Sử dụng TSA chính: {ts_url}")
            except Exception as e:
                print(f"   [LTV] Cảnh báo: Không khởi tạo được timestamper: {e}")

        # 9. Ký PDF
        pdf_signer_obj = signers.PdfSigner(
            signature_meta=sig_meta,
            signer=signer,
            stamp_style=stamp_style,
            timestamper=timestamper,
        )

        with open(out_file, "wb") as f_out:
            loop = asyncio.new_event_loop()
            try:
                # Thử truyền validation_context vào async_sign_pdf nếu pyhanko hỗ trợ
                import inspect
                _sign_kwargs = {}
                _sign_sig = inspect.signature(pdf_signer_obj.async_sign_pdf)
                if "validation_context" in _sign_sig.parameters and _vc:
                    _sign_kwargs["validation_context"] = _vc

                loop.run_until_complete(
                    pdf_signer_obj.async_sign_pdf(writer, output=f_out, **_sign_kwargs)
                )
            except Exception as _e_sign:
                # Fallback: tắt embed_validation_info và ký lại
                err_str = str(_e_sign)
                err_lower = err_str.lower()
                if "timestamp" in err_lower or "timestamper" in err_lower or "tsa" in err_lower or "response [404]" in err_lower or "malformed" in err_lower:
                    print(f"   [LTV] ⚠️  Timestamp lỗi ({_e_sign}) — đang ký lại không dùng TSA...")
                    try:
                        f_out.seek(0)
                        f_out.truncate(0)
                    except Exception:
                        pass
                    loop2 = asyncio.new_event_loop()
                    try:
                        sig_meta_plain = PdfSignatureMetadata(
                            field_name=field_name + "_nts",
                            reason="Ký số tài liệu",
                            location="Việt Nam",
                            certify=False,
                            subfilter=SigSeedSubFilter.PADES,
                        )
                        pdf_signer_plain = signers.PdfSigner(
                            signature_meta=sig_meta_plain,
                            signer=signer,
                            stamp_style=stamp_style,
                            timestamper=None,
                        )
                        _w2 = IncrementalPdfFileWriter(io.BytesIO(in_bytes), strict=False)
                        fields.append_signature_field(
                            _w2,
                            sig_field_spec=fields.SigFieldSpec(
                                sig_field_name=sig_meta_plain.field_name,
                                on_page=page_num,
                                box=box,
                            ),
                        )
                        loop2.run_until_complete(
                            pdf_signer_plain.async_sign_pdf(_w2, output=f_out)
                        )
                        print("   [Ký] ✅ Ký thành công (bỏ qua TSA bị lỗi).")
                    finally:
                        loop2.close()
                elif "validation" in err_lower or "context" in err_lower:
                    print(f"   [LTV] ⚠️  OCSP/LTV lỗi ({_e_sign}) — đang ky lại không embed LTV...")
                    try:
                        f_out.seek(0)
                        f_out.truncate(0)
                    except Exception:
                        pass
                    loop2 = asyncio.new_event_loop()
                    try:
                        # Tạo lại sig_meta không embed
                        sig_meta_plain = PdfSignatureMetadata(
                            field_name=field_name + "_r",
                            reason="Ký số tài liệu",
                            location="Việt Nam",
                            certify=False,
                            subfilter=SigSeedSubFilter.PADES,
                        )
                        pdf_signer_plain = signers.PdfSigner(
                            signature_meta=sig_meta_plain,
                            signer=signer,
                            stamp_style=stamp_style,
                            timestamper=timestamper,
                        )
                        # Tạo lại writer từ bytes gốc
                        _w2 = IncrementalPdfFileWriter(io.BytesIO(in_bytes), strict=False)
                        from pyhanko.sign import fields as _fields2
                        _fields2.append_signature_field(
                            _w2,
                            sig_field_spec=fields.SigFieldSpec(
                                sig_field_name=sig_meta_plain.field_name,
                                on_page=page_num,
                                box=box,
                            ),
                        )
                        loop2.run_until_complete(
                            pdf_signer_plain.async_sign_pdf(_w2, output=f_out)
                        )
                        print("   [Ký] ✅ Ký thành công (không có OCSP/LTV).")
                    finally:
                        loop2.close()
                else:
                    raise  # Lỗi khác thì throw lại bình thường
            finally:
                loop.close()

        print(f"   [Log] Ký xong → {out_file}")

        # 10. Timestamp thứ 2: DigiCert (Archive DocTimeStamp — LTA lớp 2)
        #     Ghi vào buffer trước — chỉ replace out_file khi thành công
        _ARCHIVE_TSA = "http://timestamp.digicert.com"
        try:
            from pyhanko.sign.timestamps import HTTPTimeStamper
            from pyhanko.sign.signers.pdf_signer import PdfTimeStamper
            from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter

            arch_tsa = HTTPTimeStamper(_ARCHIVE_TSA)
            # Đọc file đã ký vào memory
            with open(out_file, "rb") as _f2:
                _b2 = _f2.read()
            _s2 = io.BytesIO(_b2)
            _w2 = IncrementalPdfFileWriter(_s2, strict=False)

            # ⚠️ Ghi vào buffer — KHÔNG mở out_file trong "wb" trước khi chắc chắn thành công
            _buf2 = io.BytesIO()
            archiver = PdfTimeStamper(timestamper=arch_tsa)
            loop2 = asyncio.new_event_loop()
            try:
                loop2.run_until_complete(
                    archiver.async_timestamp_pdf(_w2, md_algorithm="sha256", output=_buf2)
                )
            finally:
                loop2.close()

            # Chỉ ghi ra file khi buffer có nội dung
            _result = _buf2.getvalue()
            if _result:
                with open(out_file, "wb") as _fout2:
                    _fout2.write(_result)
                print(f"   [LTA] ✅ Archive timestamp thứ 2 nhúng thành công ({_ARCHIVE_TSA})")
            else:
                print("   [LTA] ⚠️  Buffer archive timestamp trống — giữ nguyên file đã ký.")

        except Exception as _e_arch:
            # Không block — file đã ký vẫn còn nguyên
            print(f"   [LTA] ⚠️  Không nhúng được archive timestamp thứ 2: {_e_arch}")



    def sign_pdf_native_box(self, in_file, out_file, page_num, box, signer=None, ts_url=None, use_logo=True, archive_ts=False):
        """
        Ky PDF tai mot khung chu ky do nguoi dung chon truc tiep tren trang PDF.

        Args:
            in_file: Duong dan PDF dau vao.
            out_file: Duong dan PDF dau ra.
            page_num: Chi so trang bat dau tu 0.
            box: Tuple toa do PDF bottom-up (x1, y1, x2, y2).
            signer: Doi tuong signer tu win_signer.
            ts_url: URL TSA neu can nhung timestamp.
            use_logo: Giu layout logo/nen mac dinh cua chu ky hop dong.
            archive_ts: Nhung archive timestamp phu sau khi ky.
        """
        import asyncio
        import inspect
        import uuid
        from pyhanko.sign import signers, fields
        from pyhanko.sign.fields import SigSeedSubFilter
        from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter
        from pyhanko.sign.signers.pdf_signer import PdfSignatureMetadata
        from datetime import datetime

        if signer is None:
            raise ValueError("Can truyen doi tuong signer (WindowsStoreSigner)")

        if not isinstance(box, (tuple, list)) or len(box) != 4:
            raise ValueError("Khung ky khong hop le.")

        x1, y1, x2, y2 = [float(v) for v in box]
        if x2 <= x1 or y2 <= y1:
            raise ValueError("Khung ky khong hop le.")
        box = (x1, y1, x2, y2)

        layout_args = {} if use_logo else {"logo_path": "NON_EXISTENT_LOGO_FORCE_FALLBACK"}
        layout = SignatureLayoutV13(**layout_args)
        box_width = max(1.0, x2 - x1)
        box_height = max(1.0, y2 - y1)
        # Match the visual stamp canvas to the user-selected PDF field. This keeps
        # the logo/background/text scaled inside the drawn rectangle instead of
        # using the default contract stamp dimensions for every custom area.
        layout.WIDTH = max(90, int(round(box_width)))
        layout.HEIGHT = max(48, int(round(box_height)))
        layout.LOGO_DISPLAY_SIZE = max(32, min(120, int(layout.HEIGHT * 4 * 0.22)))
        layout.CORNER_RADIUS = max(6, min(18, int(min(layout.WIDTH, layout.HEIGHT) * 0.12)))

        company_info = _load_runtime_company_info()
        _mst = getattr(signer, 'mst', '') or str(company_info.get("tax_id") or "").strip()
        if _mst:
            _user_id = f"MST:{_mst}"
        else:
            _user_id = getattr(signer, 'user_id', getattr(signer, 'serial_number', ''))

        company_name = str(company_info.get("legal_name") or getattr(signer, 'org_name', '') or "").strip()
        signer_display_name = str(
            getattr(signer, 'common_name', getattr(signer, 'common_name_clean', 'Nguoi ky')) or "Nguoi ky"
        ).strip()
        signer_info = {
            "name": signer_display_name,
            "company": company_name,
            "email": getattr(signer, 'email', ''),
            "reason": "Ky so tai lieu",
            "timestamp": datetime.now(),
            "user_id": _user_id,
        }

        sig_image = layout.draw_signature(signer_info)

        uid_short = uuid.uuid4().hex[:6]
        field_name = f"Sig_CustomArea_{int(page_num)}_{uid_short}"

        _embed_val = False
        _vc = None
        try:
            from pyhanko_certvalidator import ValidationContext
            _vc = ValidationContext(allow_fetching=True, revocation_mode="soft-fail")
            _embed_val = True
        except Exception as _e_vc:
            print(f"   [LTV] Khong the khoi tao ValidationContext: {_e_vc} - tat embed_validation_info")

        sig_meta = PdfSignatureMetadata(
            field_name=field_name,
            reason="Ky so tai lieu",
            location="Viet Nam",
            certify=False,
            subfilter=SigSeedSubFilter.PADES,
            embed_validation_info=_embed_val,
            use_pades_lta=bool(ts_url) and _embed_val,
        )

        with open(in_file, "rb") as _f:
            in_bytes = _f.read()
        writer = IncrementalPdfFileWriter(io.BytesIO(in_bytes), strict=False)
        fields.append_signature_field(
            writer,
            sig_field_spec=fields.SigFieldSpec(
                sig_field_name=field_name,
                on_page=int(page_num),
                box=box,
            ),
        )

        stamp_style = self._make_image_stamp_style(sig_image)

        timestamper = None
        if ts_url:
            try:
                from pyhanko.sign.timestamps import HTTPTimeStamper
                timestamper = HTTPTimeStamper(ts_url)
                print(f"   [LTV] Su dung TSA chinh: {ts_url}")
            except Exception as e:
                print(f"   [LTV] Canh bao: Khong khoi tao duoc timestamper: {e}")

        pdf_signer_obj = signers.PdfSigner(
            signature_meta=sig_meta,
            signer=signer,
            stamp_style=stamp_style,
            timestamper=timestamper,
        )

        with open(out_file, "wb") as f_out:
            loop = asyncio.new_event_loop()
            try:
                _sign_kwargs = {}
                _sign_sig = inspect.signature(pdf_signer_obj.async_sign_pdf)
                if "validation_context" in _sign_sig.parameters and _vc:
                    _sign_kwargs["validation_context"] = _vc
                loop.run_until_complete(
                    pdf_signer_obj.async_sign_pdf(writer, output=f_out, **_sign_kwargs)
                )
            except Exception as _e_sign:
                err_str = str(_e_sign)
                err_lower = err_str.lower()
                if "timestamp" in err_lower or "timestamper" in err_lower or "tsa" in err_lower or "response [404]" in err_lower or "malformed" in err_lower:
                    print(f"   [LTV] Timestamp loi ({_e_sign}) - ky lai khong dung TSA...")
                    try:
                        f_out.seek(0)
                        f_out.truncate(0)
                    except Exception:
                        pass
                    loop2 = asyncio.new_event_loop()
                    try:
                        sig_meta_plain = PdfSignatureMetadata(
                            field_name=field_name + "_nts",
                            reason="Ky so tai lieu",
                            location="Viet Nam",
                            certify=False,
                            subfilter=SigSeedSubFilter.PADES,
                        )
                        pdf_signer_plain = signers.PdfSigner(
                            signature_meta=sig_meta_plain,
                            signer=signer,
                            stamp_style=stamp_style,
                            timestamper=None,
                        )
                        _w2 = IncrementalPdfFileWriter(io.BytesIO(in_bytes), strict=False)
                        fields.append_signature_field(
                            _w2,
                            sig_field_spec=fields.SigFieldSpec(
                                sig_field_name=sig_meta_plain.field_name,
                                on_page=int(page_num),
                                box=box,
                            ),
                        )
                        loop2.run_until_complete(
                            pdf_signer_plain.async_sign_pdf(_w2, output=f_out)
                        )
                        print("   [Ky] Ky thanh cong (bo qua TSA bi loi).")
                    finally:
                        loop2.close()
                elif "validation" in err_lower or "context" in err_lower:
                    print(f"   [LTV] OCSP/LTV loi ({_e_sign}) - ky lai khong embed LTV...")
                    try:
                        f_out.seek(0)
                        f_out.truncate(0)
                    except Exception:
                        pass
                    loop2 = asyncio.new_event_loop()
                    try:
                        sig_meta_plain = PdfSignatureMetadata(
                            field_name=field_name + "_r",
                            reason="Ky so tai lieu",
                            location="Viet Nam",
                            certify=False,
                            subfilter=SigSeedSubFilter.PADES,
                        )
                        pdf_signer_plain = signers.PdfSigner(
                            signature_meta=sig_meta_plain,
                            signer=signer,
                            stamp_style=stamp_style,
                            timestamper=timestamper,
                        )
                        _w2 = IncrementalPdfFileWriter(io.BytesIO(in_bytes), strict=False)
                        fields.append_signature_field(
                            _w2,
                            sig_field_spec=fields.SigFieldSpec(
                                sig_field_name=sig_meta_plain.field_name,
                                on_page=int(page_num),
                                box=box,
                            ),
                        )
                        loop2.run_until_complete(
                            pdf_signer_plain.async_sign_pdf(_w2, output=f_out)
                        )
                        print("   [Ky] Ky thanh cong (khong co OCSP/LTV).")
                    finally:
                        loop2.close()
                else:
                    raise
            finally:
                loop.close()

        print(f"   [Log] Ky xong tai khung chon -> {out_file}")

        if not archive_ts:
            print("   [LTA] Bo qua archive timestamp cho che do ky vung chon.")
            return

        _ARCHIVE_TSA = "http://timestamp.digicert.com"
        try:
            from pyhanko.sign.timestamps import HTTPTimeStamper
            from pyhanko.sign.signers.pdf_signer import PdfTimeStamper
            from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter

            arch_tsa = HTTPTimeStamper(_ARCHIVE_TSA)
            with open(out_file, "rb") as _f2:
                _b2 = _f2.read()
            _w2 = IncrementalPdfFileWriter(io.BytesIO(_b2), strict=False)

            _buf2 = io.BytesIO()
            archiver = PdfTimeStamper(timestamper=arch_tsa)
            loop2 = asyncio.new_event_loop()
            try:
                loop2.run_until_complete(
                    archiver.async_timestamp_pdf(_w2, md_algorithm="sha256", output=_buf2)
                )
            finally:
                loop2.close()

            _result = _buf2.getvalue()
            if _result:
                with open(out_file, "wb") as _fout2:
                    _fout2.write(_result)
                print(f"   [LTA] Archive timestamp thu 2 nhung thanh cong ({_ARCHIVE_TSA})")
            else:
                print("   [LTA] Buffer archive timestamp trong - giu nguyen file da ky.")
        except Exception as _e_arch:
            print(f"   [LTA] Khong nhung duoc archive timestamp thu 2: {_e_arch}")


    @staticmethod
    def _make_image_stamp_style(pil_image):
        """
        Tạo stamp style từ ảnh PIL để nhúng vào chữ ký PDF.
        Nhúng trực tiếp bằng PdfImage vào background của TextStampStyle 
        ngăn lỗi PIL sinh ra PDF xref table không chuẩn (Illegal generation 65536).
        """
        try:
            from pyhanko.stamp import TextStampStyle
            from pyhanko.pdf_utils.images import PdfImage

            # Tạo PdfImage trực tiếp từ ảnh PIL
            pdf_img = PdfImage(pil_image)
            
            # Gắn ảnh làm background cho stamp style trống chữ
            # border_width=0 để vô hiệu hóa viền đen của PyHanko
            return TextStampStyle(
                stamp_text="", 
                background=pdf_img, 
                background_opacity=1.0,
                border_width=0
            )
        except Exception as e:
            print(f"   [Debug] ImageStamp lỗi fallback: {e}")
            pass

        # Fallback: TextStampStyle trống - ký có field nhưng không có hình ảnh
        try:
            from pyhanko.stamp import TextStampStyle
            return TextStampStyle(stamp_text="")
        except Exception:
            pass

        # Fallback cuối cùng: None - ký hoàn toàn không có visual appearance
        return None
