import pandas as pd
import time
import os
from datetime import datetime

def load_config():
    """Загрузка конфигурации из файла"""

    config = {'Files': {}, 'Formats': {}, 'Defaults': {}}
    
    if os.path.exists('config.txt'):
        with open('config.txt', 'r', encoding='utf-8') as f:
            current_section = None
            
            for line in f:
                line = line.strip()
                
                # Игнорируем пустые строки
                if not line:
                    continue
                
                # Удаляем комментарии, если они есть
                line = line.split('#', 1)[0].strip()  # Удаляем все, что после '#'
                
                # Проверяем, является ли строка заголовком секции
                if line.startswith('[') and line.endswith(']'):
                    current_section = line[1:-1].strip()  # Получаем имя секции
                    continue
                
                # Обрабатываем строки с параметрами
                if '=' in line and current_section:
                    key, value = line.split('=', 1)
                    key = key.strip()
                    value = value.strip()  # Удаляем пробелы вокруг значения
                    
                    # Добавляем значение в соответствующую секцию
                    if current_section in config:
                        config[current_section][key] = value

    return config

def show_file_preview(fileIN):
    """Показывает первые две строки файла и общее количество строк"""
    print("\nПервые две строки файла:")
    df_preview = pd.read_csv(fileIN, nrows=2)
    print(df_preview.to_string())
    
    # print("\nОценка количества строк в файле...")
    
    # # Вариант 1: Быстрая оценка по размеру файла
    # file_size = os.path.getsize(fileIN)
    # # Читаем первые 10 строк для оценки средней длины строки
    # with open(fileIN, 'rb') as f:
    #     first_lines = [len(next(f)) for _ in range(11)]  # 11 строк (включая заголовок)
    # avg_line_length = sum(first_lines[1:]) / (len(first_lines) - 1)  # Исключаем заголовок
    # estimated_rows = int(file_size / avg_line_length) - 1  # Вычитаем 1 для заголовка
    
    # Вариант 2: Подсчет строк блоками
    def count_lines_fast(filename):
        def blocks(file, size=65536):
            while True:
                b = file.read(size)
                if not b: break
                yield b
                
        with open(filename, "rb") as f:
            return sum(bl.count(b"\n") for bl in blocks(f))
    
    exact_rows = count_lines_fast(fileIN) - 1  # Вычитаем 1 для заголовка
    
    # print(f"Оценочное количество строк (по размеру файла): {estimated_rows:,}")
    print(f"Cтрок в файле: {exact_rows:,}")
    
    return df_preview.columns.tolist(), exact_rows

def process_csv_files():
    print("Начало работы программы")
    # Загружаем конфигурацию
    config = load_config()
    
    # Получаем параметры из конфига
    # fileIN = config.get('Files', 'filein', fallback='large_data_sort.csv')
    # fileOUT = config.get('Files', 'fileout', fallback='select_rows.csv')
    fileIN = config['Files'].get('filein')  # Удалите fallback
    fileOUT = config['Files'].get('fileout')  # Удалите fallback
    
    # Определяем формат данных на основе конфига
    datetime_row = config['Formats'].get('datetime_row') # config.get('Formats', 'datetime_row') or 0 #, fallback='0')
    date_row = config['Formats'].get('date_row') # config.get('Formats', 'date_row') #, fallback='0')
    time_row = config['Formats'].get('time_row') # config.get('Formats', 'time_row') # , fallback='0')
    
    try:
        datetime_row = int(datetime_row)
        date_row = int(date_row)
        time_row = int(time_row)
    except ValueError:
        datetime_row = date_row = time_row = 0

    # Проверка на корректность значений date_row и time_row
    if datetime_row <=0 and date_row <= 0 and time_row <= 0:
        raise ValueError(f"Ошибка параметров: datetime_row={datetime_row} или (date_row={date_row} и time_row={time_row}) должны быть больше 0.")
    
    # Показываем превью файла
    header, total_rows = show_file_preview(fileIN)
    
    # Определяем форматы и колонки
    if datetime_row > 0:
        print("\nИспользуется формат с одним полем даты-времени")
        datetime_col = datetime_row - 1
        date_col = time_col = None
        datetime_format = config['Formats'].get('datetime_format') # config.get('Formats', 'datetime_format', fallback='%Y-%m-%d %H:%M:%S.%f')
    else:
        print("\nИспользуется формат с раздельными полями даты и времени")
        datetime_col = None
        date_col = date_row - 1 if date_row > 0 else 0
        time_col = time_row - 1 if time_row > 0 else 1
        date_format = config['Formats'].get('date_format') # config.get('Formats', 'date_format', fallback='%Y-%m-%d')
        time_format = config['Formats'].get('time_format') # config.get('Formats', 'time_format', fallback='%H:%M:%S.%f')
    
    # Получаем параметры поиска
    rows_count = config['Defaults'].get('rows_count') # config.get('Defaults', 'rows_count', fallback='10')
    try:
        rows_count = int(rows_count)
    except ValueError:
        rows_count = 0
        
    date_start = config['Defaults'].get('date_start') # config.get('Defaults', 'date_start', fallback='2015-01-01')
    time_start = config['Defaults'].get('time_start') # config.get('Defaults', 'time_start', fallback='00:00:00')
    date_end = config['Defaults'].get('date_end') # config.get('Defaults', 'date_end', fallback='2026-01-01')
    time_end = config['Defaults'].get('time_end') # config.get('Defaults', 'time_end', fallback='00:00:00')

    print(f"\nПараметры обработки:")
    print(f"Входной файл: {fileIN}")
    print(f"Выходной файл: {fileOUT}")
    print(f"Дата начала: {date_start} {time_start}")
    if rows_count > 0:
        print(f"Количество строк для отбора: {rows_count}")
    else:
        print(f"Дата окончания: {date_end} {time_end}")

    datetime_start = pd.to_datetime(f"{date_start} {time_start}")
    datetime_end = pd.to_datetime(f"{date_end} {time_end}") if date_end else None
    
    # Засекаем время начала обработки
    start_time = time.time()
    
    # Обработка файла
    results = process_file(fileIN, datetime_start, datetime_end, rows_count, 
                         datetime_col, date_col, time_col,
                         datetime_format if datetime_col is not None else (date_format, time_format))
    
    # Сохранение результатов
    results.to_csv(fileOUT, index=False)
    print(f"\nРезультаты сохранены в файл: {fileOUT}")
    print(f"Количество отобранных строк: {len(results):,}")

    # После завершения обработки
    end_time = time.time()
    elapsed_time = end_time - start_time  # Время в секундах
    current_time = datetime.now().strftime("%H:%M:%S")  # Текущее время в формате ЧЧ:ММ:СС

    print(f"\nОбработка завершена. Текущее время: {current_time}, длительность: {elapsed_time:.2f} сек.")

def process_file(fileIN, datetime_start, datetime_end, rows_count, datetime_col, date_col, time_col, 
                datetime_format, chunk_size=1000000):
    """Обработка файла чанками"""
    start_time = time.time()
    last_check_time = start_time
    total_rows = 0
    results = []
    
    print("\nНачало обработки файла...")
    
    if isinstance(datetime_format, tuple):
        date_format, time_format = datetime_format
    else:
        date_format = time_format = None
        datetime_format = datetime_format.replace('%%', '%')  # Убираем экранирование

    for chunk in pd.read_csv(fileIN, chunksize=chunk_size):
        total_rows += len(chunk)
        current_time = time.time()
        
        # Вывод прогресса каждые 300 секунд
        if current_time - last_check_time >= 300:
            found_rows = sum(len(df) for df in results)
            print(f"Обработано строк: {total_rows}, найдено: {found_rows}")
            if not input("Продолжить? (y/n): ").lower().startswith('y'):
                break
            last_check_time = current_time
        
        try:
            # Парсинг даты-времени
            if datetime_col is not None:
                chunk['datetime'] = pd.to_datetime(chunk.iloc[:, datetime_col], 
                                                 format=datetime_format, 
                                                 errors='coerce')
            else:
                # Определяем формат времени для текущего чанка
                current_time_format = time_format
                time_example = chunk.iloc[:, time_col].astype(str).iloc[0]
                if '.' not in time_example and '.%f' in current_time_format:
                    current_time_format = current_time_format.split('.')[0]
                
                current_datetime_format = f'{date_format} {current_time_format}'
                
                # Создаем временную колонку для даты-времени
                chunk['datetime'] = pd.to_datetime(
                    chunk.iloc[:, date_col].astype(str) + ' ' + chunk.iloc[:, time_col].astype(str),
                    format=current_datetime_format,
                    errors='coerce'
                )
            
            # Фильтрация по дате-времени
            mask = chunk['datetime'] >= datetime_start
            if rows_count == 0 and datetime_end:
                mask &= chunk['datetime'] <= datetime_end
            
            filtered_chunk = chunk[mask]
            if len(filtered_chunk) > 0:
                # Сохраняем оригинальные данные без временной колонки
                results.append(filtered_chunk.drop('datetime', axis=1))
            
            # Проверка условий остановки
            total_found = sum(len(df) for df in results)
            if rows_count and total_found >= rows_count:
                excess = total_found - rows_count
                if excess > 0:
                    results[-1] = results[-1].iloc[:-excess]
                break
            
            if datetime_end and len(filtered_chunk) > 0:
                max_datetime = filtered_chunk['datetime'].max()
                if max_datetime > datetime_end:
                    # Если превысили верхнюю границу, обрезаем последний чанк
                    mask = filtered_chunk['datetime'] <= datetime_end
                    results[-1] = filtered_chunk[mask].drop('datetime', axis=1)
                    break
                elif max_datetime == datetime_end:
                    # Если достигли точной верхней границы
                    break
            
            # Если условия не выполнены, продолжаем чтение следующего чанка
            continue
                
        except Exception as e:
            print(f"\nОшибка при обработке чанка: {str(e)}")
            print("Пример данных из чанка:")
            print(chunk.iloc[:5])
            if not input("\nПродолжить обработку? (y/n): ").lower().startswith('y'):
                break
    
    print(f"\nОбработка завершена. Всего обработано строк: {total_rows}")
    if not results:
        # Получаем заголовки из исходного файла
        headers = pd.read_csv(fileIN, nrows=0).columns
        return pd.DataFrame(columns=headers)
    return pd.concat(results)

if __name__ == "__main__":
    process_csv_files()

