import io
import re
import base64
import streamlit as st
import streamlit.components.v1 as components
import numpy as np
from PIL import Image, ImageOps

try:
    import zxingcpp
except ImportError:
    zxingcpp = None

try:
    from pystrich.datamatrix import DataMatrixEncoder, DataMatrixData, FNC1
except ImportError:
    st.error("Папка pystrich не найдена. Поместите локальную копию библиотеки в корень проекта для работы на Python 3.12+.")
    DataMatrixEncoder, DataMatrixData, FNC1 = None, None, None

# ================= НАСТРОЙКИ ПЕЧАТИ =================
DPI = 203
LABEL_SIZE_MM = 20
TOTAL_DOTS = int((LABEL_SIZE_MM / 25.4) * DPI)  # 160 пикселей

def parse_cz_mark_to_parts(raw_string):
    raw_string = raw_string.replace(" ", "")

    if "(01)" in raw_string and "(21)" in raw_string:
        raw_string = raw_string.replace("(01)", "01", 1).replace("(21)", "21", 1)
        raw_string = raw_string.replace("(91)", "\x1d91", 1).replace("(92)", "\x1d92", 1).replace("(93)", "\x1d93", 1)

    if re.search(r'[А-Яа-яЁё]', raw_string):
        ru_lower = "йцукенгшщзхъфывапролджэячсмитьбюё"
        en_lower = "qwertyuiop[]asdfghjkl;'zxcvbnm,.`"
        ru_upper = "ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮЁ"
        en_upper = "QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>~"
        ru_spec = ".,\"№;:?"
        en_spec = "/?@#$^&"
        layout_map = str.maketrans(ru_lower + ru_upper + ru_spec, en_lower + en_upper + en_spec)
        raw_string = raw_string.translate(layout_map)
        
    raw_string = raw_string.replace('<GS>', '\x1d').replace('[GS]', '\x1d')
    
    if '\x1d' in raw_string or '\u001d' in raw_string:
        return raw_string.replace('\u001d', '\x1d').split('\x1d')
        
    if len(raw_string) < 16:
        return [raw_string]
        
    parts = []
    current_part = raw_string[:16] 
    remaining = raw_string[16:]
    
    i = 0
    while i < len(remaining):
        if i + 2 <= len(remaining):
            pair = remaining[i:i+2]
            if pair in ("91", "92", "93"):
                parts.append(current_part)
                current_part = pair
                i += 2
                continue
        current_part += remaining[i]
        i += 1
        
    parts.append(current_part)
    return parts

def create_duplicate_label(parts):
    args = [FNC1]
    for i, part in enumerate(parts):
        args.append(part)
        if i < len(parts) - 1:
            args.append(FNC1)
            
    payload = DataMatrixData(*args, encoding='ascii')
    encoder = DataMatrixEncoder(payload)
    dm_img = encoder.get_pilimage()
    
    img = Image.new('RGB', (TOTAL_DOTS, TOTAL_DOTS), 'white')
    PAD = 16 
    
    gray = dm_img.convert('L')
    inverted = ImageOps.invert(gray)
    bbox = inverted.getbbox()
    if bbox: 
        dm_img = dm_img.crop(bbox)
        
    pixels = dm_img.convert('L').load()
    module_size = next((x for x in range(1, dm_img.width) if pixels[x, 0] != pixels[0, 0]), 1)
            
    modules_x = dm_img.width // module_size
    modules_y = dm_img.height // module_size
    small_dm = dm_img.resize((modules_x, modules_y), Image.NEAREST)
    
    max_dm_space = TOTAL_DOTS - (PAD * 2)
    scale = max_dm_space // modules_x
    if scale < 1: scale = 1
    
    final_dm_size = modules_x * scale
    final_dm = small_dm.resize((final_dm_size, final_dm_size), Image.NEAREST)
    
    x_pos = (TOTAL_DOTS - final_dm_size) // 2
    y_pos = (TOTAL_DOTS - final_dm_size) // 2
    img.paste(final_dm, (x_pos, y_pos))

    return img.convert('1')

def render_copy_widget(label, text_to_copy):
    """
    Кастомный HTML-виджет для надежного копирования на iOS/Android.
    Большие сенсорные кнопки, статичное отображение без необходимости наведения.
    Каждый виджет изолирован в своем iframe.
    """
    safe_text = text_to_copy.replace('"', '&quot;').replace("'", "&#39;")
    html_code = f"""
    <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;">
        <div style="font-size: 14px; color: #31333F; margin-bottom: 6px; font-weight: 500;">{label}</div>
        <div style="display: flex; gap: 10px; align-items: flex-start;">
            <textarea id="val" readonly style="flex-grow: 1; padding: 12px; border: 1px solid #ccc; border-radius: 8px; font-size: 14px; resize: none; height: 60px; background-color: #f9f9fa; color: #262730; outline: none; box-shadow: inset 0 1px 2px rgba(0,0,0,0.05);">{safe_text}</textarea>
            <button onclick="copyToClipboard()" style="padding: 0 20px; background-color: #FF4B4B; color: white; border: none; border-radius: 8px; font-size: 15px; font-weight: 600; cursor: pointer; height: 60px; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 4px rgba(255,75,75,0.2); touch-action: manipulation; transition: transform 0.1s;">
                📋 Копировать
            </button>
        </div>
        <div id="toast" style="color: #00a400; font-size: 13px; margin-top: 6px; display: none; font-weight: 500;">✓ Успешно скопировано!</div>
        <script>
            function copyToClipboard() {{
                const el = document.getElementById('val');
                el.select();
                el.setSelectionRange(0, 99999); // Для мобильных
                
                navigator.clipboard.writeText(el.value).then(() => {{
                    showToast();
                }}).catch(err => {{
                    // Резервный способ для старых iOS
                    try {{
                        document.execCommand('copy');
                        showToast();
                    }} catch (e) {{
                        alert("Скопируйте текст вручную.");
                    }}
                }});
            }}
            function showToast() {{
                const toast = document.getElementById('toast');
                toast.style.display = 'block';
                setTimeout(() => {{ toast.style.display = 'none'; }}, 2000);
            }}
        </script>
    </div>
    """
    components.html(html_code, height=130)

def run(plugin_info):
    # CSS для центрирования кнопки загрузки внутри виджета file_uploader
    st.markdown("""
        <style>
            [data-testid="stFileUploader"] section {
                display: flex;
                flex-direction: column;
                align-items: center !important;
                justify-content: center !important;
                text-align: center !important;
            }
            [data-testid="stFileUploader"] button {
                margin: 0 auto;
            }
        </style>
    """, unsafe_allow_html=True)

    with st.container(border=True):
        tabs = st.tabs(["📸 Фото марки", "⌨️ Текст / Ручной сканер"])
        scanned_code = None
        
        with tabs[0]:
            uploaded_photo = st.file_uploader("Сделать фото или загрузить из галереи", type=["jpg", "jpeg", "png"])
            
            if uploaded_photo:
                if zxingcpp is None:
                    st.error("Библиотека zxing-cpp не установлена.")
                else:
                    with st.spinner("Анализ фото..."):
                        try:
                            img = Image.open(uploaded_photo).convert('RGB')
                            img_array = np.array(img)
                            barcodes = zxingcpp.read_barcodes(img_array)
                            
                            if barcodes:
                                scanned_code = barcodes[0].bytes.decode('utf-8', errors='ignore')
                            else:
                                st.error("✖ Штрихкод не найден. Попробуйте обрезать фото или улучшить освещение.")
                        except Exception as e:
                            st.error(f"Ошибка при чтении: {e}")
                            
        with tabs[1]:
            text_input = st.text_input("Отсканируйте марку или вставьте код:", key="scanned_text").strip()
            if text_input:
                scanned_code = text_input

        if scanned_code:
            st.divider()
            if DataMatrixEncoder is None:
                st.error("Генерация невозможна: отсутствует локальная папка pystrich.")
            else:
                try:
                    clean_parts = parse_cz_mark_to_parts(scanned_code)
                    
                    label_img = create_duplicate_label(clean_parts)
                    display_img = label_img.resize((label_img.width * 3, label_img.height * 3), Image.NEAREST)
                    
                    payload_string = '\x1d'.join(clean_parts)
                    b64_string = base64.b64encode(payload_string.encode('utf-8')).decode('utf-8')
                    visual_payload = payload_string.replace('\x1d', '<GS>')
                    
                    col1, col2 = st.columns([1, 2])
                    with col1:
                        st.image(display_img, caption="Дубликат марки")
                        
                        buf = io.BytesIO()
                        label_img.save(buf, format="PNG")
                        st.download_button(
                            label="📥 Скачать (PNG)",
                            data=buf.getvalue(),
                            file_name="duplicate_label.png",
                            mime="image/png",
                            use_container_width=True
                        )
                        
                    with col2:
                        # Используем наши новые большие виджеты копирования
                        render_copy_widget("Base64 (для Excel):", b64_string)
                        render_copy_widget("Распознанные данные (внутренний код):", visual_payload)
                        
                except Exception as e:
                    st.error(f"✖ Ошибка генерации: {e}")