"""
Large data processing utilities for handling big Excel files.
"""

import csv
import json
import time
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import Any, Callable, Iterator

from openpyxl import Workbook as OpenpyxlWorkbook

from ..utils.exceptions import ValidationError
from ..utils.memory_manager import MemoryManager, MemoryStrategy


class DataFormat(Enum):
    """Supported data formats."""

    CSV = "csv"
    JSON = "json"
    EXCEL = "excel"
    PARQUET = "parquet"


@dataclass
class ProcessingStats:
    """Processing statistics."""

    rows_processed: int = 0
    rows_skipped: int = 0
    memory_usage_mb: float = 0.0
    processing_time: float = 0.0
    errors: list[str] = field(default_factory=list)


class LargeDataProcessor:
    """
    Efficient processor for large datasets.
    """

    def __init__(self, memory_manager: MemoryManager | None = None) -> None:
        """
        Initialize large data processor.

        Args:
            memory_manager: Memory manager instance
        """
        self.memory_manager = memory_manager or MemoryManager()
        self.stats = ProcessingStats()

    def _reset_stats(self) -> None:
        """Reset processing statistics before a new run."""
        self.stats = ProcessingStats()

    def process_csv_stream(
        self,
        file_path: str | Path,
        processor: Callable[[list[list[str]]], list[Any]],
        chunk_size: int = 10000,
        *,
        batch_size: int | None = None,
    ) -> Iterator[list[Any]]:
        """
        Process CSV file in streaming chunks.

        Args:
            file_path: Path to CSV file
            processor: Row processing function
            chunk_size: Number of rows per chunk

        Yields:
            Processed data chunks
        """
        with open(file_path, "r", encoding="utf-8") as file:
            reader = csv.reader(file)
            headers = next(reader, None)

            if headers is None:
                return

            chunk: list[list[str]] = []
            for row in reader:
                chunk.append(row)
                if len(chunk) >= chunk_size:
                    processed = processor(chunk)
                    for batch in self._iter_batches(
                        iter(processed), batch_size or len(processed)
                    ):
                        yield batch
                    chunk = []

            if chunk:
                processed = processor(chunk)
                for batch in self._iter_batches(
                    iter(processed), batch_size or len(processed)
                ):
                    yield batch

    def process_json_stream(
        self,
        file_path: str | Path,
        processor: Callable[[dict[str, Any]], Any],
        chunk_size: int = 1000,
        *,
        batch_size: int | None = None,
    ) -> Iterator[list[Any]]:
        """
        以流式方式处理JSON文件

        参数:
            file_path: JSON文件路径
            processor: 数据处理函数
            chunk_size: 每个块的条目数量

        返回:
            处理后的数据块
        """
        with open(file_path, "r", encoding="utf-8") as file:
            # 假设JSON文件为数组格式
            data = json.load(file)

            if not isinstance(data, list):
                raise ValidationError("JSON file must contain an array")

            chunk: list[dict[str, Any]] = []
            for item in data:
                chunk.append(item)
                if len(chunk) >= chunk_size:
                    processed = [processor(entry) for entry in chunk]
                    for batch in self._iter_batches(
                        iter(processed), batch_size or len(processed)
                    ):
                        yield batch
                    chunk = []

            if chunk:
                processed = [processor(entry) for entry in chunk]
                for batch in self._iter_batches(
                    iter(processed), batch_size or len(processed)
                ):
                    yield batch

    def export_large_dataset(
        self,
        data_iterator: Iterator[Any],
        output_path: str | Path,
        format_type: DataFormat = DataFormat.CSV,
        headers: list[str] | None = None,
        *,
        batch_size: int = 1000,
        memory_strategy: MemoryStrategy | None = None,
    ) -> ProcessingStats:
        """
        Export large dataset efficiently.

        Args:
            data_iterator: Iterator of data chunks
            output_path: Output file path
            format_type: Output format
            headers: Optional headers

        Returns:
            Processing statistics
        """
        self._reset_stats()
        start_time = time.time()

        iterator = self._iter_batches(data_iterator, batch_size)

        context = (
            self.memory_manager.memory_context(memory_strategy)
            if memory_strategy
            else None
        )

        def _do_export() -> None:
            if format_type == DataFormat.CSV:
                self._export_csv_stream(iterator, output_path, headers)
            elif format_type == DataFormat.JSON:
                self._export_json_stream(iterator, output_path)
            elif format_type == DataFormat.EXCEL:
                self._export_excel_stream(iterator, output_path, headers)
            else:
                raise ValidationError(f"Unsupported format: {format_type}")

        try:
            if context:
                with context:
                    _do_export()
            else:
                _do_export()

            self.stats.processing_time = time.time() - start_time
            self.stats.memory_usage_mb = self.memory_manager.get_memory_usage_mb()
            return self.stats

        except Exception as e:
            self.stats.errors.append(str(e))
            raise ValidationError(f"Export failed: {e}")

    def _export_csv_stream(
        self,
        data_iterator: Iterator[list[Any]],
        output_path: str | Path,
        headers: list[str] | None = None,
    ) -> None:
        """
        Export data to CSV in streaming mode.

        Args:
            data_iterator: Data iterator
            output_path: Output file path
            headers: Optional headers
        """
        with open(output_path, "w", newline="", encoding="utf-8") as file:
            writer = csv.writer(file)

            if headers:
                writer.writerow(headers)

            for chunk in data_iterator:
                writer.writerows(chunk)
                self.stats.rows_processed += len(chunk)
                memory_stats = self.memory_manager.optimize_memory()
                self.stats.memory_usage_mb = memory_stats.get(
                    "after_mb", self.stats.memory_usage_mb
                )

    def _export_json_stream(
        self,
        data_iterator: Iterator[list[Any]],
        output_path: str | Path,
    ) -> None:
        """
        Export data to JSON in streaming mode.

        Args:
            data_iterator: Data iterator
            output_path: Output file path
        """
        with open(output_path, "w", encoding="utf-8") as file:
            file.write("[")
            first_item = True

            for chunk in data_iterator:
                for item in chunk:
                    if not first_item:
                        file.write(",")
                    json.dump(item, file)
                    first_item = False
                    self.stats.rows_processed += 1
                memory_stats = self.memory_manager.optimize_memory()
                self.stats.memory_usage_mb = memory_stats.get(
                    "after_mb", self.stats.memory_usage_mb
                )

            file.write("]")

    def _export_excel_stream(
        self,
        data_iterator: Iterator[list[Any]],
        output_path: str | Path,
        headers: list[str] | None = None,
    ) -> None:
        """
        Export data to Excel in streaming mode using a write-only workbook.

        Args:
            data_iterator: Data iterator
            output_path: Output file path
            headers: Optional headers
        """
        workbook = OpenpyxlWorkbook(write_only=True)
        worksheet = workbook.create_sheet(title="Sheet1")

        if headers:
            worksheet.append(headers)
            self.stats.rows_processed += 1

        for chunk in data_iterator:
            for row_data in chunk:
                if isinstance(row_data, dict):
                    if headers:
                        ordered_row = [row_data.get(header) for header in headers]
                    else:
                        ordered_row = list(row_data.values())
                    worksheet.append(ordered_row)
                elif isinstance(row_data, (list, tuple)):
                    worksheet.append(list(row_data))
                else:
                    worksheet.append([row_data])
                self.stats.rows_processed += 1
            memory_stats = self.memory_manager.optimize_memory()
            self.stats.memory_usage_mb = memory_stats.get(
                "after_mb", self.stats.memory_usage_mb
            )

        workbook.save(str(output_path))

    def convert_large_file(
        self,
        input_path: str | Path,
        output_path: str | Path,
        input_format: DataFormat,
        output_format: DataFormat,
        **kwargs: Any,
    ) -> ProcessingStats:
        """
        Convert large file between formats.

        Args:
            input_path: Input file path
            output_path: Output file path
            input_format: Input format
            output_format: Output format
            **kwargs: Additional processing options

        Returns:
            Processing statistics
        """
        if input_format == DataFormat.CSV and output_format == DataFormat.EXCEL:
            self._reset_stats()
            return self._csv_to_excel(input_path, output_path, **kwargs)
        elif input_format == DataFormat.EXCEL and output_format == DataFormat.CSV:
            self._reset_stats()
            return self._excel_to_csv(input_path, output_path, **kwargs)
        else:
            raise ValidationError(
                f"Conversion not supported: {input_format} -> {output_format}"
            )

    def _csv_to_excel(
        self,
        csv_path: str | Path,
        excel_path: str | Path,
        **kwargs: Any,
    ) -> ProcessingStats:
        """
        Convert large CSV to Excel.

        Args:
            csv_path: CSV file path
            excel_path: Excel file path
            **kwargs: Additional options

        Returns:
            Processing statistics
        """

        def process_chunk(chunk: list[list[str]]) -> list[Any]:
            return [list(row) for row in chunk]

        data_iterator = self.process_csv_stream(csv_path, process_chunk)
        return self.export_large_dataset(data_iterator, excel_path, DataFormat.EXCEL)

    def _excel_to_csv(
        self,
        excel_path: str | Path,
        csv_path: str | Path,
        **kwargs: Any,
    ) -> ProcessingStats:
        """
        Convert large Excel to CSV.

        Args:
            excel_path: Excel file path
            csv_path: CSV file path
            **kwargs: Additional options

        Returns:
            Processing statistics
        """
        from ..core.workbook import Workbook

        workbook = Workbook(excel_path)
        worksheet = workbook.active

        def data_generator() -> Iterator[list[Any]]:
            used_range = worksheet.get_used_range()
            values = worksheet.get_range_values(used_range)
            chunk_size = kwargs.get("batch_size", 10000)
            for i in range(0, len(values), chunk_size):
                chunk = values[i : i + chunk_size]
                yield chunk
                self.stats.rows_processed += len(chunk)
                self.memory_manager.optimize_memory()

        return self.export_large_dataset(
            data_generator(),
            csv_path,
            DataFormat.CSV,
            batch_size=kwargs.get("batch_size", 1000),
            memory_strategy=kwargs.get("memory_strategy"),
        )

    # ------------------------------------------------------------------ #
    # Internal helpers
    # ------------------------------------------------------------------ #
    def _iter_batches(
        self,
        data_iterator: Iterator[Any],
        batch_size: int,
    ) -> Iterator[list[Any]]:
        """
        Normalize arbitrary iterators into batches of rows.
        """
        if batch_size <= 0:
            raise ValidationError("batch_size must be positive")

        current_batch: list[Any] = []

        for chunk in data_iterator:
            if chunk is None:
                continue

            normalized = self._normalize_chunk(chunk)
            for row in normalized:
                current_batch.append(row)
                if len(current_batch) >= batch_size:
                    yield current_batch
                    current_batch = []

        if current_batch:
            yield current_batch

    @staticmethod
    def _normalize_chunk(chunk: Any) -> list[Any]:
        """
        Turn a chunk into a list of row structures.
        """
        if isinstance(chunk, (list, tuple)):
            if not chunk:
                return []
            first = chunk[0]
            if isinstance(first, (list, tuple, dict)):
                return [
                    list(row) if isinstance(row, (list, tuple)) else row
                    for row in chunk
                ]
            return [list(chunk)]
        if isinstance(chunk, dict):
            return [chunk]
        return [[chunk]]


class DataValidator:
    """
    Data validation utilities for large datasets.
    """

    def __init__(self, validation_rules: dict[str, Any]) -> None:
        """
        Initialize data validator.

        Args:
            validation_rules: Validation rules
        """
        self.validation_rules = validation_rules
        self.errors: list[str] = []

    def validate_row(self, row_data: dict[str, Any], row_number: int) -> bool:
        """
        Validate a single row.

        Args:
            row_data: Row data
            row_number: Row number

        Returns:
            True if valid, False otherwise
        """
        is_valid = True

        for field_name, rules in self.validation_rules.items():
            value = row_data.get(field_name)

            # 必填字段验证
            if rules.get("required") and (value is None or value == ""):
                self.errors.append(
                    f"行{row_number}: 字段'{field_name}'为必填项"
                )
                is_valid = False
                continue

            # 类型验证
            if "type" in rules and value is not None:
                expected_type = rules["type"]
                if not isinstance(value, expected_type):
                    self.errors.append(
                        f"行{row_number}: 字段'{field_name}'必须为{expected_type.__name__}类型"
                    )
                    is_valid = False

            # 范围验证
            if "min" in rules and value is not None:
                if value < rules["min"]:
                    self.errors.append(
                        f"行{row_number}: 字段'{field_name}'必须大于等于{rules['min']}"
                    )
                    is_valid = False

            if "max" in rules and value is not None:
                if value > rules["max"]:
                    self.errors.append(
                        f"Row {row_number}: Field '{field_name}' must be <= {rules['max']}"
                    )
                    is_valid = False

        return is_valid

    def get_errors(self) -> list[str]:
        """
        Get validation errors.

        Returns:
            List of error messages
        """
        return self.errors.copy()

    def clear_errors(self) -> None:
        """
        Clear validation errors.
        """
        self.errors.clear()
