"""
Module: csv_processor.py
Description: Provides utility functions for processing CSV files in bulk.
"""

import csv
import logging
from typing import List, Dict, Union, Callable, Optional
import os

# Configure logging
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

class CSVProcessorError(Exception):
    """Custom exception for CSV processing errors."""
    pass


def read_csv_file(file_path: str, delimiter: str = ',', quotechar: str = '"', encoding: str = 'utf-8') -> List[Dict[str, str]]:
    """
    Reads a CSV file and returns its content as a list of dictionaries.

    Args:
        file_path (str): The path to the CSV file.
        delimiter (str): The delimiter used in the CSV file (default: ',').
        quotechar (str): The character used to quote fields (default: '"').
        encoding (str): The encoding of the CSV file (default: 'utf-8').

    Returns:
        List[Dict[str, str]]: A list of dictionaries, where each dictionary
                             represents a row in the CSV file, and the keys
                             are the column headers.

    Raises:
        CSVProcessorError: If the file cannot be opened or read.
    """
    try:
        with open(file_path, 'r', encoding=encoding) as csvfile:
            reader = csv.DictReader(csvfile, delimiter=delimiter, quotechar=quotechar)
            data = list(reader)
        logging.info(f"Successfully read CSV file: {file_path}")
        return data
    except FileNotFoundError as e:
        logging.error(f"File not found: {file_path} - {e}")
        raise CSVProcessorError(f"File not found: {file_path}") from e
    except csv.Error as e:
        logging.error(f"CSV error while reading {file_path}: {e}")
        raise CSVProcessorError(f"CSV error while reading {file_path}") from e
    except Exception as e:
        logging.error(f"Error reading file {file_path}: {e}")
        raise CSVProcessorError(f"Error reading file {file_path}") from e


def write_csv_file(file_path: str, data: List[Dict[str, str]], fieldnames: Optional[List[str]] = None, delimiter: str = ',', quotechar: str = '"', encoding: str = 'utf-8') -> None:
    """
    Writes data to a CSV file.

    Args:
        file_path (str): The path to the CSV file.
        data (List[Dict[str, str]]): The data to write, as a list of dictionaries.
        fieldnames (Optional[List[str]]):  A list of column names to use in the CSV file.
                                            If None, the keys of the first dictionary in the data
                                            are used. Defaults to None.
        delimiter (str): The delimiter used in the CSV file (default: ',').
        quotechar (str): The character used to quote fields (default: '"').
        encoding (str): The encoding of the CSV file (default: 'utf-8').

    Raises:
        CSVProcessorError: If the file cannot be opened or written to.
    """
    if not data:
        logging.warning("No data to write to CSV file.")
        return

    if fieldnames is None:
        fieldnames = list(data[0].keys())  # Use keys from the first row as fieldnames

    try:
        with open(file_path, 'w', newline='', encoding=encoding) as csvfile:
            writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=delimiter, quotechar=quotechar)
            writer.writeheader()
            writer.writerows(data)
        logging.info(f"Successfully wrote to CSV file: {file_path}")
    except IOError as e:
        logging.error(f"IOError while writing to {file_path}: {e}")
        raise CSVProcessorError(f"IOError while writing to {file_path}") from e
    except csv.Error as e:
        logging.error(f"CSV error while writing to {file_path}: {e}")
        raise CSVProcessorError(f"CSV error while writing to {file_path}") from e
    except Exception as e:
        logging.error(f"Error writing to file {file_path}: {e}")
        raise CSVProcessorError(f"Error writing to file {file_path}") from e


def process_csv_file(input_file: str, output_file: str, processor_function: Callable[[List[Dict[str, str]]], List[Dict[str, str]]], delimiter: str = ',', quotechar: str = '"', encoding: str = 'utf-8') -> None:
    """
    Reads a CSV file, applies a processing function to the data, and writes the processed data to a new CSV file.

    Args:
        input_file (str): The path to the input CSV file.
        output_file (str): The path to the output CSV file.
        processor_function (Callable[[List[Dict[str, str]]], List[Dict[str, str]]]): A function that takes a list of dictionaries (CSV data)
                                                                                    as input and returns a modified list of dictionaries.
        delimiter (str): The delimiter used in the CSV file (default: ',').
        quotechar (str): The character used to quote fields (default: '"').
        encoding (str): The encoding of the CSV file (default: 'utf-8').

    Raises:
        CSVProcessorError: If any error occurs during the process (reading, processing, writing).
    """
    try:
        data = read_csv_file(input_file, delimiter=delimiter, quotechar=quotechar, encoding=encoding)
        processed_data = processor_function(data)
        if processed_data:
            fieldnames = list(processed_data[0].keys()) # Extract fieldnames
            write_csv_file(output_file, processed_data, fieldnames=fieldnames, delimiter=delimiter, quotechar=quotechar, encoding=encoding)
        else:
             logging.warning("No data to write after processing. Output file will not be created.")

    except CSVProcessorError as e:
        logging.error(f"Error processing CSV file: {e}")
        raise
    except Exception as e:
        logging.error(f"Unexpected error during CSV processing: {e}")
        raise CSVProcessorError(f"Unexpected error during CSV processing: {e}") from e


if __name__ == '__main__':
    # Example usage:

    # 1. Create a sample CSV file
    sample_data = [
        {'name': 'Alice', 'age': '30', 'city': 'New York'},
        {'name': 'Bob', 'age': '25', 'city': 'Los Angeles'},
        {'name': 'Charlie', 'age': '35', 'city': 'Chicago'}
    ]
    sample_file = '/tmp/sample.csv'
    write_csv_file(sample_file, sample_data)

    # 2. Define a processing function
    def age_filter(data: List[Dict[str, str]]) -> List[Dict[str, str]]:
        """Filters out people younger than 30."""
        return [row for row in data if int(row['age']) >= 30]

    # 3. Process the CSV file
    output_file = '/tmp/filtered.csv'
    try:
      process_csv_file(sample_file, output_file, age_filter)
      print(f"Filtered data written to {output_file}")

      #4. Read and print the output file content
      filtered_data = read_csv_file(output_file)
      print("Content of filtered CSV:")
      for row in filtered_data:
          print(row)
    except CSVProcessorError as e:
        print(f"Error: {e}")
    except Exception as e:
        print(f"Unexpected error: {e}")