use std::env;
use std::fs;

use tuphium::error::TuphiumError;
use tuphium::frame::csv::{read_csv, read_csv_without_header, CsvColumnType, CsvValue};

/// Writes a CSV into the temp dir and returns its path (needs a .csv extension).
fn write_temp_csv(name: &str, content: &str) -> String {
    let mut path = env::temp_dir();
    path.push(name);
    fs::write(&path, content).unwrap();
    path.to_str().unwrap().to_string()
}

#[test]
fn test_file_not_found() {
    let result = read_csv("src/data/nonexistent.csv");
    assert!(result.is_err());
    println!("{}", result.unwrap_err());
}

#[test]
fn test_not_a_csv() {
    let result = read_csv("src/data/data_cleaned.txt");
    assert!(result.is_err());
    println!("{}", result.unwrap_err());
}

#[test]
fn test_read_csv_with_header() {
    let result = read_csv("data/breast_cancer.csv").unwrap();
    println!("Header: {:?}", result.header);
    println!("Total rows: {}", result.rows.len());
    for row in &result.rows {
        println!("{:?}", row);
    }
}

#[test]
fn test_read_csv_without_header() {
    let result = read_csv_without_header("data/breast_cancer.csv").unwrap();
    println!("Total rows: {}", result.len());
    for row in &result {
        println!("{:?}", row);
    }
}

#[test]
fn test_column_types() {
    let data = read_csv("data/breast_cancer.csv").unwrap();
    let types = data.column_types();

    assert_eq!(types.len(), data.header.len(), "one type per column");

    for (col, t) in data.header.iter().zip(&types) {
        println!("{}: {:?}", col, t);
        assert!(
            *t == CsvColumnType::Integer || *t == CsvColumnType::Float,
            "column '{}' expected numeric, got {:?}",
            col,
            t
        );
    }
}

#[test]
fn test_parse_value_types() {
    let path = write_temp_csv("tuphium_parse_types.csv", "a,b,c,d\n10,3.5,true,hello\n");
    let data = read_csv(&path).unwrap();

    assert_eq!(data.rows.len(), 1);
    let row = &data.rows[0];
    assert!(matches!(row[0], CsvValue::Integer(10)));
    assert!(matches!(row[1], CsvValue::Float(f) if (f - 3.5).abs() < 1e-9));
    assert!(matches!(row[2], CsvValue::Boolean(true)));
    assert!(matches!(&row[3], CsvValue::Text(s) if s == "hello"));
}

#[test]
fn test_header_parsed_correctly() {
    let path = write_temp_csv(
        "tuphium_header.csv",
        "name,age,active\nAlice,30,yes\nBob,25,no\n",
    );
    let data = read_csv(&path).unwrap();

    assert_eq!(data.header, vec!["name", "age", "active"]);
    assert_eq!(data.rows.len(), 2, "header row must not be counted as data");
}

#[test]
fn test_column_types_priority() {
    // Column "mix": Integer then Float → widened to Float.
    // Column "txt": always Text.
    let path = write_temp_csv("tuphium_priority.csv", "mix,txt\n1,abc\n2.5,def\n");
    let data = read_csv(&path).unwrap();
    let types = data.column_types();

    assert_eq!(
        types[0],
        CsvColumnType::Float,
        "int + float must widen to Float"
    );
    assert_eq!(types[1], CsvColumnType::Text);
}

#[test]
fn read_csv_returns_invalid_extension() {
    let error = read_csv("src/data/data_cleaned.txt").unwrap_err();
    assert_eq!(
        error,
        TuphiumError::InvalidFileExtension {
            operation: "read_csv",
            path: "src/data/data_cleaned.txt".to_string(),
            expected_extension: ".csv".to_string(),
        }
    );
}

#[test]
fn read_csv_returns_io_error_for_missing_file() {
    let error = read_csv("src/data/nonexistent.csv").unwrap_err();
    assert!(matches!(
        error,
        TuphiumError::IoError {
            operation: "read_csv",
            path: Some(_),
            ..
        }
    ));
}

#[test]
fn read_csv_returns_empty_data() {
    let path = write_temp_csv("tuphium_empty.csv", "");
    let error = read_csv(&path).unwrap_err();
    assert_eq!(
        error,
        TuphiumError::EmptyData {
            operation: "read_csv",
            target: "CSV file".to_string(),
        }
    );
}

#[test]
fn read_csv_without_header_reuses_file_errors() {
    let error = read_csv_without_header("src/data/data_cleaned.txt").unwrap_err();
    assert!(matches!(
        error,
        TuphiumError::InvalidFileExtension {
            operation: "read_csv",
            ..
        }
    ));
}
