import re
import json
import io
import zipfile
import pandas as pd
import xml.etree.ElementTree as ET
from datetime import datetime
import xml.dom.minidom
import streamlit as st

def clean_id(val):
    if pd.isna(val):
        return ""
    s = str(val).strip()
    if '.' in s and s.split('.')[1].isdigit() and int(s.split('.')[1]) == 0:
        return s.split('.')[0]
    if 'E' in s.upper():
        try:
            return str(int(float(s)))
        except:
            pass
    return s

def parse_decimal(val):
    if pd.isna(val):
        return 0.0
    if isinstance(val, (int, float)):
        return float(val)
    s = str(val).replace('\xa0', '').replace(' ', '').replace(',', '.')
    try:
        return float(s)
    except:
        return 0.0

def parse_date(val):
    if pd.isna(val):
        return ""
    if isinstance(val, (datetime, pd.Timestamp)):
        return val.strftime("%Y-%m-%d")
    try:
        dt = datetime.strptime(str(val).strip()[:10], "%d.%m.%Y")
        return dt.strftime("%Y-%m-%d")
    except:
        return str(val)[:10]

def parse_datetime(val):
    if pd.isna(val):
        return ""
    if isinstance(val, (datetime, pd.Timestamp)):
        return val.strftime("%Y-%m-%dT%H:%M:%S")
    try:
        dt = datetime.strptime(str(val).strip()[:19], "%d.%m.%Y %H:%M:%S")
        return dt.strftime("%Y-%m-%dT%H:%M:%S")
    except:
        return str(val)

def load_term_mapping_memory(mapping_bytes):
    mapping = {}
    if not mapping_bytes:
        return mapping
    try:
        data = json.loads(mapping_bytes.getvalue().decode("utf-8"))
        for item in data:
            orig = str(item.get("original", ""))
            repl = str(item.get("replaced", ""))
            if orig and repl:
                mapping[orig] = repl
    except Exception as e:
        st.error(f"Ошибка при загрузке файла маппинга: {e}")
    return mapping

def apply_term_mapping(term_number, mapping):
    if not mapping:
        return term_number
    return mapping.get(term_number, term_number)

def create_xml_for_pp_memory(df_group, pp_number, pp_date, bik_code, term_mapping):
    org_name = str(df_group['Наименование юридического лица'].iloc[0])
    org_inn = clean_id(df_group['ИНН'].iloc[0])
    org_account = str(df_group['Номер расчётного счёта'].iloc[0])

    root = ET.Element("ДанныеПоЗапросу")
    root.set("ДатаФормирования", datetime.now().strftime("%Y-%m-%dT%H:%M:%S"))
    root.set("НаименованиеОрганизации", org_name)
    root.set("ИНН", org_inn)
    root.set("БИК", bik_code)
    root.set("РасчетныйСчетОрганизации", org_account)
    root.set("ДатаПлатежногоПоручения", parse_date(pp_date))
    root.set("НомерПлатежногоПоручения", clean_id(pp_number))

    op_map = {'Покупка': 'оплата', 'Возврат': 'возврат', 'Отмена': 'отмена'}
    df_group['ДатаОперацииБезВремени'] = pd.to_datetime(df_group['Дата операции'], errors='coerce').dt.date
    grouped = df_group.groupby(['Номер мерчанта', 'ДатаОперацииБезВремени'])

    for (merchant, date_obj), group in grouped:
        if pd.isna(merchant) or pd.isna(date_obj):
            continue
        merchant_el = ET.SubElement(root, "СтрокаПоМерчанту")
        merchant_el.set("НомерМерчанта", clean_id(merchant))
        merchant_el.set("Дата", date_obj.strftime("%Y-%m-%d"))

        control = {'оплата': {'cnt': 0, 'sum': 0.0},
                   'возврат': {'cnt': 0, 'sum': 0.0},
                   'отмена': {'cnt': 0, 'sum': 0.0}}

        for _, row in group.iterrows():
            term_el = ET.SubElement(merchant_el, "СтрокаПоТерминалу")
            original_term = clean_id(row['Номер терминала'])
            mapped_term = apply_term_mapping(original_term, term_mapping)
            term_el.set("НомерТерминала", mapped_term)

            csv_op = str(row['Тип операции']).strip()
            xml_op = op_map.get(csv_op, 'оплата')
            amt = parse_decimal(row['Сумма операции'])
            comm = parse_decimal(row['Сумма комиссии'])
            settle = parse_decimal(row['Сумма расчета'])

            if xml_op in control:
                control[xml_op]['cnt'] += 1
                control[xml_op]['sum'] += abs(amt)

            ET.SubElement(term_el, "УникальныйНомерТранзакции").text = clean_id(row['RRN'])
            ET.SubElement(term_el, "ВремяОперации").text = parse_datetime(row['Локальная дата операции'])
            ET.SubElement(term_el, "СуммаОперации").text = f"{amt:.2f}"
            ET.SubElement(term_el, "Комиссия").text = f"{comm:.2f}"
            ET.SubElement(term_el, "Возмещение").text = f"{settle:.2f}"
            ET.SubElement(term_el, "НомерКарты").text = str(row['Номер карты'])
            ET.SubElement(term_el, "СистемаРасчетов").text = str(row['Платежная система'])
            ET.SubElement(term_el, "КодАвторизации").text = clean_id(row['Код авторизации'])
            ET.SubElement(term_el, "ТипОперации").text = xml_op
            curr = str(row['Валюта']).strip()
            if curr == "RUR":
                curr = "RUB"
            ET.SubElement(term_el, "Валюта").text = curr

        ctrl_el = ET.SubElement(merchant_el, "КонтрольнаяСуммаПоДате")
        ET.SubElement(ctrl_el, "Дата").text = date_obj.strftime("%Y-%m-%d")
        for op, data in control.items():
            if data['cnt'] > 0:
                op_el = ET.SubElement(ctrl_el, "Операция")
                op_el.set("ТипОперации", op)
                ET.SubElement(op_el, "КоличествоЗаписей").text = str(data['cnt'])
                ET.SubElement(op_el, "СуммаИтого").text = f"{data['sum']:.2f}"

    xml_str = ET.tostring(root, encoding='utf-8')
    dom = xml.dom.minidom.parseString(xml_str)
    pretty = dom.toprettyxml(indent="  ")
    
    safe_pp_num = re.sub(r'[\\/*?:"<>|]', '_', clean_id(pp_number))
    pp_date_str = parse_date(pp_date)
    file_name = f"ПП_{safe_pp_num}_{pp_date_str}.xml"
    zip_path = f"XML_по_ПП/{pp_date_str}/{file_name}"
    
    return zip_path, pretty

def run(plugin_info):
    with st.container(border=True):
        col1, col2 = st.columns(2)
        with col1:
            excel_file = st.file_uploader("Реестр (Excel .xlsx)", type=["xlsx"])
        with col2:
            mapping_file = st.file_uploader("Маппинг (JSON)", type=["json"])
        
        bik_code = st.text_input("БИК", value="045354601")

    if excel_file is not None:
        if st.button("Конвертировать", type="primary"):
            with st.spinner("Обработка данных..."):
                try:
                    term_mapping = load_term_mapping_memory(mapping_file)
                    
                    df = pd.read_excel(excel_file, dtype={
                        'Номер мерчанта': str,
                        'Номер терминала': str,
                        'RRN': str,
                        'Код авторизации': str,
                        'Номер карты': str,
                        'Номер ПП': str
                    })
                    df.columns = [str(c).strip() for c in df.columns]

                    required = ['Номер ПП', 'Дата ПП', 'Наименование юридического лица', 'ИНН', 'Номер расчётного счёта']
                    for col in required:
                        if col not in df.columns:
                            st.error(f"В файле отсутствует колонка: {col}")
                            return

                    df = df.dropna(subset=['Номер ПП', 'Дата ПП'])
                    if df.empty:
                        st.warning("Нет строк с заполненными номером и датой ПП")
                        return

                    grouped = df.groupby(['Номер ПП', 'Дата ПП'])
                    zip_buffer = io.BytesIO()
                    
                    with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zf:
                        for (pp_num, pp_date), group in grouped:
                            zip_path, xml_content = create_xml_for_pp_memory(group, pp_num, pp_date, bik_code, term_mapping)
                            zf.writestr(zip_path, xml_content.encode('utf-8'))
                    
                    st.success(f"Успешно обработано платежных поручений: {len(grouped)}")
                    st.download_button(
                        label="Скачать ZIP-архив с XML",
                        data=zip_buffer.getvalue(),
                        file_name="XML_результаты.zip",
                        mime="application/zip"
                    )
                except Exception as e:
                    st.error(f"Ошибка при обработке файла: {e}")