import streamlit as st
import pandas as pd
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
import configparser
from loguru import logger
import socket
import time
import os
import json
import sys
from io import StringIO
import csv
import re

# Setup logger
logger.add("script.log", rotation="10 MB")

# Load configuration with error handling
def read_config():
    config_file = 'config.ini'
    if not os.path.exists(config_file):
        logger.error(f"Файл конфигурации {config_file} не найден.")
        st.error(f"Файл конфигурации {config_file} не найден.")
        st.stop()
    
    config = configparser.ConfigParser()
    config.read(config_file)
    
    try:
        api_url = config['default']['API_URL']
        token = config['default']['YOUR_TOKEN']
        if not api_url:
            raise ValueError("API_URL не указан в файле конфигурации")
        if not token:
            raise ValueError("YOUR_TOKEN не указан в файле конфигурации")
        return config
    except KeyError as e:
        logger.error(f"Ошибка конфигурации: {e}")
        st.error(f"Ошибка конфигурации: {e}")
        st.stop()

config = read_config()
API_URL = config['default']['API_URL']
YOUR_TOKEN = config['default']['YOUR_TOKEN']

logger.info(f"API_URL: {API_URL}")
logger.info(f"YOUR_TOKEN: {'***' if YOUR_TOKEN else 'Не установлен'}")

def get_headers():
    return {'Authorization': f'Token {YOUR_TOKEN}'}

def check_internet(host="8.8.8.8", port=53, timeout=3):
    try:
        socket.setdefaulttimeout(timeout)
        socket.create_connection((host, port))
        logger.info("Интернет-соединение доступно.")
        return True
    except OSError as e:
        logger.error(f"Нет интернет-соединения: {e}")
        return False

@st.cache_data
def get_all_borrowers(limit=250, delay=5, max_retries=3):
    all_data = []
    offset = 0
    retries = 0
    progress_bar = st.progress(0)
    status_text = st.empty()

    session = requests.Session()
    retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504])
    session.mount('https://', HTTPAdapter(max_retries=retries))

    while True:
        try:
            url = f"{API_URL}/account/deals/borrowers?limit={limit}&offset={offset}"
            logger.info(f"Запрос к URL: {url}")
            response = session.get(url, headers=get_headers(), timeout=30)
            logger.info(f"Ответ API: {response.status_code}")
            response.raise_for_status()

            data = response.json()
            logger.debug(f"Исходный ответ API: {data}")

            if isinstance(data, dict) and 'results' in data:
                data = data['results']
            elif isinstance(data, dict) and 'data' in data:
                data = data['data']
            elif isinstance(data, str):
                data = json.loads(data)

            if not data:
                logger.info("Больше данных не получено.")
                break

            if isinstance(data, list):
                all_data.extend(data)
            elif isinstance(data, dict):
                all_data.append(data)
            else:
                logger.error(f"Неожиданный формат данных: {type(data)}")
                break

            logger.info(f"Получено {len(data)} записей.")
            status_text.text(f"Получено {len(all_data)} записей.")
            progress_bar.progress(min(len(all_data) / 1000, 1.0))  # Предполагаем максимум 1000 записей
            
            offset += limit
            if len(data) < limit:
                logger.info("Данные закончились.")
                break
            time.sleep(delay)

        except requests.exceptions.RequestException as e:
            logger.error(f"Ошибка запроса API: {e}")
            retries += 1
            if retries > max_retries:
                logger.error("Превышено максимальное количество попыток.")
                status_text.text("Превышено максимальное количество попыток.")
                break
            logger.info(f"Повтор после ошибки запроса. Попытка {retries}/{max_retries}")
            status_text.text(f"Повтор... Попытка {retries}/{max_retries}")
            time.sleep(delay * 2)

    logger.info(f"Всего получено записей: {len(all_data)}")
    status_text.text(f"Всего получено записей: {len(all_data)}")
    return all_data

def clean_value(value):
    if isinstance(value, str):
        # Удаляем кавычки в начале и конце строки
        value = value.strip('"')
        # Удаляем символы [ ] { } ' "
        value = re.sub(r'[\[\]{}\'"]', '', value)
        # Заменяем множественные пробелы на один
        value = re.sub(r'\s+', ' ', value)
        # Удаляем управляющие символы и пробелы в начале и конце
        value = value.strip()
    return value

def save_to_csv(data):
    if not data:
        logger.error("Нет данных для сохранения в CSV.")
        st.error("Нет данных для сохранения в CSV.")
        return None
    
    output = StringIO()
    writer = csv.writer(output, quoting=csv.QUOTE_MINIMAL)

    # Определяем поля, которые нужно исключить
    exclude_fields = ['type', 'level', 'title', 'checklist']

    # Фильтруем данные, исключая записи с нежелательными полями
    filtered_data = []
    for item in data:
        if isinstance(item, dict):
            filtered_item = {k: v for k, v in item.items() if k not in exclude_fields}
            if filtered_item:
                filtered_data.append(filtered_item)
        elif isinstance(item, list):
            filtered_item = [v for i, v in enumerate(item) if item[i] not in exclude_fields]
            if filtered_item:
                filtered_data.append(filtered_item)

    if filtered_data:
        if isinstance(filtered_data[0], dict):
            fieldnames = sorted(set().union(*filtered_data))
            writer.writerow(fieldnames)
            for record in filtered_data:
                writer.writerow([clean_value(record.get(field, '')) for field in fieldnames])
        else:
            for row in filtered_data:
                writer.writerow([clean_value(value) for value in row])
    
    output.seek(0)
    return output.getvalue()

def process_excel_data(file):
    try:
        bytes_data = file.getvalue()
        buffer = pd.io.excel.BytesIO(bytes_data)
        
        df = pd.read_excel(buffer)
        
        if df.empty:
            st.warning("Excel файл пуст.")
            return None
        
        # Ищем столбец с ИНН
        inn_column = None
        for col in df.columns:
            if 'inn' in str(col).lower() or 'инн' in str(col).lower():
                inn_column = col
                break
        
        if inn_column is None:
            # Если столбец с ИНН не найден, ищем его в первом столбце
            first_column = df.iloc[:, 0]
            inn_data = first_column.astype(str).apply(lambda x: re.findall(r'\d{10,12}', x))
            inn_data = inn_data[inn_data.apply(bool)]  # Удаляем пустые списки
            inn_data = inn_data.apply(lambda x: x[0] if x else None)  # Берем первое найденное значение
        else:
            inn_data = df[inn_column].astype(str).apply(lambda x: re.findall(r'\d{10,12}', x))
            inn_data = inn_data[inn_data.apply(bool)]
            inn_data = inn_data.apply(lambda x: x[0] if x else None)

        if inn_data.empty:
            st.error("Не удалось найти корректные данные ИНН.")
            return None

        return inn_data.dropna().tolist()
    except Exception as e:
        st.error(f"Ошибка обработки Excel-файла: {str(e)}")
        logger.error(f"Ошибка обработки Excel-файла: {str(e)}")
        return None

def post_data(session, url, headers, inn):
    data = {"inn": inn}
    try:
        response = session.post(url, headers=headers, json=data, timeout=10)
        response.raise_for_status()
        return response
    except requests.exceptions.HTTPError as err:
        if err.response.status_code == 405:
            logger.error(f"Ошибка 405 Method Not Allowed для ИНН {inn}. Проверьте настройки API.")
            st.error(f"Ошибка 405 Method Not Allowed для ИНН {inn}. Проверьте настройки API.")
        else:
            logger.error(f"Ошибка отправки данных для ИНН {inn}: {err}")
            st.error(f"Ошибка отправки данных для ИНН {inn}: {err}")
        return None
    except requests.exceptions.RequestException as err:
        logger.error(f"Ошибка отправки данных для ИНН {inn}: {err}")
        st.error(f"Ошибка отправки данных для ИНН {inn}: {err}")
        return None

def main():
    st.title("Панель администрирования обработки данных")

    page = st.sidebar.selectbox("Выберите задачу", ["Получить данные заемщиков", "Обработать Excel-данные"])

    if page == "Получить данные заемщиков":
        st.header("Получить данные заемщиков")
        if st.button("Проверить интернет-соединение"):
            if check_internet():
                st.success("Интернет-соединение доступно.")
            else:
                st.error("Нет интернет-соединения.")

        if st.button("Получить данные заемщиков"):
            with st.spinner("Получение данных..."):
                data = get_all_borrowers()
            if data:
                st.success(f"Получено {len(data)} записей.")
                st.dataframe(pd.DataFrame(data))
                st.session_state.borrowers_data = data
                st.session_state.enable_save = True
            else:
                st.error("Нет данных, полученных от API.")
                st.session_state.enable_save = False

        if st.button("Сохранить в CSV", disabled=not st.session_state.get('enable_save', False)):
            if hasattr(st.session_state, 'borrowers_data'):
                csv_data = save_to_csv(st.session_state.borrowers_data)
                if csv_data:
                    st.download_button(
                        label="Скачать CSV файл",
                        data=csv_data,
                        file_name="borrowers_data.csv",
                        mime="text/csv"
                    )
            else:
                st.error("Нет данных для сохранения. Пожалуйста, сначала получите данные.")

    elif page == "Обработать Excel-данные":
        st.header("Обработка Excel-данных")
        uploaded_file = st.file_uploader("Выберите Excel-файл", type="xlsx")
        if uploaded_file is not None:
            st.info("Файл загружен. Обработка...")
            data = process_excel_data(uploaded_file)
            if data:
                st.success(f"Excel-файл успешно обработан. Найдено {len(data)} ИНН.")
                st.write(data[:10])  # Показываем первые 10 ИНН

                if st.button("Отправить данные в API"):
                    progress_bar = st.progress(0)
                    status_text = st.empty()
                    session = requests.Session()
                    retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504])
                    session.mount('https://', HTTPAdapter(max_retries=retries))
                    headers = get_headers()
                    successful_count = 0
                    failed_count = 0

                    for i, inn in enumerate(data):
                        if inn and inn.isdigit():
                            response = post_data(session, f"{API_URL}/account/deals/borrowers", headers, inn)
                            if response is not None:
                                successful_count += 1
                            else:
                                failed_count += 1
                        else:
                            logger.warning(f"Некорректное значение ИНН: {inn}")
                            failed_count += 1
                        
                        progress = (i + 1) / len(data)
                        progress_bar.progress(progress)
                        status_text.text(f"Обработано {i+1}/{len(data)} ИНН")

                    st.success(f"Отправка данных завершена: {successful_count} успешных, {failed_count} неудачных")
            else:
                st.error("Не удалось обработать Excel-файл. Пожалуйста, проверьте формат и содержимое файла.")

if __name__ == "__main__":
    if 'enable_save' not in st.session_state:
        st.session_state.enable_save = False
    main()