import argparse
import csv
import sys
import json
from typing import Any, Dict, List, Union
from fastapi import FastAPI, File, UploadFile, HTTPException
from starlette.responses import JSONResponse
import uvicorn
import chardet

app = FastAPI()

def detect_encoding(file_bytes: bytes) -> str:
    """
    Detects the encoding of a byte string using chardet.

    Args:
        file_bytes (bytes): The bytes of the file to detect encoding for.

    Returns:
        str: The name of the detected encoding.
    """
    detection = chardet.detect(file_bytes)
    encoding = detection.get('encoding')
    if encoding is None:
        # Default to utf-8 if detection fails
        encoding = 'utf-8'
    return encoding

def parse_csv(file_bytes: bytes) -> List[Dict[str, Any]]:
    """
    Parses CSV bytes into a list of dictionaries with proper type detection.

    Args:
        file_bytes (bytes): The raw bytes of the CSV file.

    Returns:
        List[Dict[str, Any]]: List of records as dictionaries with properly typed values.
    """
    encoding = detect_encoding(file_bytes)
    try:
        text = file_bytes.decode(encoding)
    except UnicodeDecodeError:
        # Fallback to utf-8 with replacement
        text = file_bytes.decode('utf-8', errors='replace')

    reader = csv.DictReader(text.splitlines())
    records = []

    for row in reader:
        parsed_row = {}
        for key, value in row.items():
            parsed_value = detect_and_convert_type(value)
            parsed_row[key] = parsed_value
        records.append(parsed_row)

    return records

def detect_and_convert_type(value: str) -> Union[str, int, float, bool, None]:
    """
    Detects the type of a string value and converts it accordingly.

    Args:
        value (str): The string value to convert.

    Returns:
        Union[str, int, float, bool, None]: The value converted to its detected type.
    """
    val = value.strip()

    if val == '':
        return None
    lower_val = val.lower()

    # Boolean detection
    if lower_val in ('true', 'false'):
        return lower_val == 'true'

    # Integer detection
    try:
        int_val = int(val)
        return int_val
    except ValueError:
        pass

    # Float detection
    try:
        float_val = float(val)
        return float_val
    except ValueError:
        pass

    # Return original string if no other type matches
    return val

@app.post('/convert/csv')
async def convert_csv(file: UploadFile = File(...)) -> JSONResponse:
    """
    Endpoint to accept a CSV file upload and return a JSON response with parsed data.

    Args:
        file (UploadFile): The uploaded CSV file.

    Returns:
        JSONResponse: The JSONified list of records.
    """
    try:
        file_bytes = await file.read()
        records = parse_csv(file_bytes)
        return JSONResponse(content=records)
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"Error processing file: {str(e)}")

def run_server(host: str, port: int) -> None:
    """
    Runs the FastAPI server using uvicorn.

    Args:
        host (str): The hostname to bind.
        port (int): The port to listen on.
    """
    uvicorn.run("csv_to_json_api:app", host=host, port=port, reload=False)

def main() -> None:
    """
    Parses command-line arguments and starts the server.
    """
    parser = argparse.ArgumentParser(description='FastAPI microservice to convert uploaded CSV files to JSON.')
    parser.add_argument('--host', type=str, default='127.0.0.1', help='Host to bind the server.')
    parser.add_argument('--port', type=int, default=8000, help='Port to listen on.')
    args = parser.parse_args()

    print(f"Starting server at http://{args.host}:{args.port}")
    run_server(args.host, args.port)

if __name__ == '__main__':
    main()