use std::{
    fs::File,
    io::{Read, Seek, SeekFrom},
};

use arrow::array::{ArrayRef, Int16Array, StringArray, TimestampMillisecondArray, UInt16Array};
use arrow::{array::RecordBatch, ipc::reader::FileReader};
use chrono::TimeZone;
use chrono::{DateTime, Utc};
use podders::{root_as_footer, ContentType};

#[derive(Debug, Clone)]
pub struct Pod5Info {
    pub acquisition_id: String,
    pub acquisition_start_time: DateTime<Utc>,
    pub adc_max: i16,
    pub adc_min: i16,
    pub experiment_name: String,
    pub flow_cell_id: String,
    pub flow_cell_product_code: String,
    pub protocol_name: String,
    pub protocol_run_id: String,
    pub protocol_start_time: DateTime<Utc>,
    pub sample_id: String,
    pub sample_rate: u16,
    pub sequencing_kit: String,
    pub sequencer_position: String,
    pub sequencer_position_type: String,
    pub software: String,
    pub system_name: String,
    pub system_type: String,
}

impl Pod5Info {
    pub fn from_pod5(file_path: &str) -> Self {
        let mut file = File::open(file_path).unwrap();
        let _end = file.seek(SeekFrom::End(0)).unwrap();
        file.seek(SeekFrom::Current(-32)).unwrap(); // Signature + Section marker + 8 bytes for footer length
        let mut buffer = [0; 8]; // Buffer for 8 bytes

        file.read_exact(&mut buffer).unwrap(); // Read 8 bytes

        // Convert bytes to little-endian i64
        let value = i64::from_le_bytes(buffer);

        // Seek to the footer position
        file.seek(SeekFrom::Current(-(8 + value))).unwrap();

        // Read the footer data
        let mut buf = vec![0; value as usize];
        file.read_exact(&mut buf).unwrap();

        // Deserialize the FlatBuffer
        let footer = root_as_footer(&buf).unwrap();

        let mut acquisition_id = String::new();
        let mut acquisition_start_time = Utc::now();
        let mut adc_max = 0;
        let mut adc_min = 0;
        let mut experiment_name = String::new();
        let mut flow_cell_id = String::new();
        let mut flow_cell_product_code = String::new();
        let mut protocol_name = String::new();
        let mut protocol_run_id = String::new();
        let mut protocol_start_time = Utc::now();
        let mut sample_id = String::new();
        let mut sample_rate = 0;
        let mut sequencing_kit = String::new();
        let mut sequencer_position = String::new();
        let mut sequencer_position_type = String::new();
        let mut software = String::new();
        let mut system_name = String::new();
        let mut system_type = String::new();

        if let Some(contents) = footer.contents() {
            for content in contents.iter() {
                if let ContentType::RunInfoTable = content.content_type() {
                    // println!("{content:#?}");
                    let batch = read_arrow_table(
                        file_path,
                        content.offset() as u64,
                        content.length() as u64,
                    )
                    .unwrap();
                    let schema = batch[0].schema();
                    for column in 0..batch[0].num_columns() {
                        let array: ArrayRef = batch[0].column(column).clone();

                        // Print column name and values
                        let column_name = schema.field(column).name().to_string();
                        // println!("Column: {}", column_name);

                        // Match the type of the array to extract values
                        match array.data_type() {
                            arrow::datatypes::DataType::Int16 => {
                                let int_array =
                                    array.as_any().downcast_ref::<Int16Array>().unwrap();
                                for i in 0..int_array.len() {
                                    // println!("{}: i16,", column_name);
                                    match column_name.as_str() {
                                        "adc_max" => adc_max = int_array.value(i),
                                        "adc_min" => adc_min = int_array.value(i),
                                        _ => (),
                                    }

                                    // println!("{}", int_array.value(i));
                                }
                            }
                            arrow::datatypes::DataType::UInt16 => {
                                let int_array =
                                    array.as_any().downcast_ref::<UInt16Array>().unwrap();
                                for i in 0..int_array.len() {
                                    // println!("{}: u16,", column_name);
                                    if let "sample_rate" = column_name.as_str() {
                                        sample_rate = int_array.value(i)
                                    }
                                }
                            }

                            // arrow::datatypes::DataType::Int32 => {
                            //     let int_array =
                            //         array.as_any().downcast_ref::<Int32Array>().unwrap();
                            //     for i in 0..int_array.len() {
                            //         println!("{}: i32,", column_name);
                            //
                            //         // println!("{}", int_array.value(i));
                            //     }
                            // }
                            // arrow::datatypes::DataType::UInt32 => {
                            //     let int_array =
                            //         array.as_any().downcast_ref::<UInt32Array>().unwrap();
                            //     for i in 0..int_array.len() {
                            //         println!("{}: u32,", column_name);
                            //
                            //         // println!("{}", int_array.value(i));
                            //     }
                            // }
                            // arrow::datatypes::DataType::Float64 => {
                            //     let float_array =
                            //         array.as_any().downcast_ref::<Float64Array>().unwrap();
                            //     for i in 0..float_array.len() {
                            //         println!("{}: f64,", column_name);
                            //
                            //         // println!("{}", float_array.value(i));
                            //     }
                            // }
                            arrow::datatypes::DataType::Utf8 => {
                                let string_array =
                                    array.as_any().downcast_ref::<StringArray>().unwrap();
                                let string_array: Vec<String> = string_array
                                    .iter()
                                    .flat_map(|v| match v {
                                        Some(v) => vec![v.to_string()],
                                        None => vec![],
                                    })
                                    .collect();

                                let value = string_array.join(" ");

                                match column_name.as_str() {
                                    "acquisition_id" => acquisition_id = value,
                                    "experiment_name" => experiment_name = value,
                                    "flow_cell_id" => flow_cell_id = value,
                                    "flow_cell_product_code" => flow_cell_product_code = value,
                                    "protocol_name" => protocol_name = value,
                                    "protocol_run_id" => protocol_run_id = value,
                                    "sample_id" => sample_id = value,
                                    "sequencing_kit" => sequencing_kit = value,
                                    "sequencer_position" => sequencer_position = value,
                                    "sequencer_position_type" => sequencer_position_type = value,
                                    "software" => software = value,
                                    "system_name" => system_name = value,
                                    "system_type" => system_type = value,
                                    _ => (),
                                }
                                // println!("{}: String,", column_name);
                                // println!("{}", string_array.join(" "));
                            }
                            arrow::datatypes::DataType::Timestamp(
                                arrow::datatypes::TimeUnit::Millisecond,
                                Some(timezone),
                            ) => {
                                if &timezone.to_string() == "UTC" {
                                    let timestamp_array = array
                                        .as_any()
                                        .downcast_ref::<TimestampMillisecondArray>()
                                        .unwrap();
                                    for i in 0..timestamp_array.len() {
                                        let timestamp = timestamp_array.value(i);
                                        let datetime: DateTime<Utc> =
                                            Utc.timestamp_millis_opt(timestamp).unwrap();
                                        // println!("{}: DateTime<Utc>,", column_name);

                                        match column_name.as_str() {
                                            "acquisition_start_time" => {
                                                acquisition_start_time = datetime
                                            }
                                            "protocol_start_time" => protocol_start_time = datetime,
                                            _ => (),
                                        }

                                        // println!("{}", datetime.to_rfc3339());
                                    }
                                }
                            }

                            _ => {
                                // println!("Unsupported data type: {:?}", array.data_type());
                            }
                        }
                    }
                }
            }
        }
        Pod5Info {
            acquisition_id,
            acquisition_start_time,
            adc_max,
            adc_min,
            experiment_name,
            flow_cell_id,
            flow_cell_product_code,
            protocol_name,
            protocol_run_id,
            protocol_start_time,
            sample_id,
            sample_rate,
            sequencing_kit,
            sequencer_position,
            sequencer_position_type,
            software,
            system_name,
            system_type,
        }
    }
}

fn read_arrow_table(
    file_path: &str,
    offset: u64,
    length: u64,
) -> arrow::error::Result<Vec<RecordBatch>> {
    let mut file = File::open(file_path)?;

    // Seek to the start of the embedded table
    file.seek(SeekFrom::Start(offset))?;

    // Read the specified length of bytes
    let mut buffer = vec![0; length as usize];
    file.read_exact(&mut buffer)?;

    // Deserialize bytes into Arrow RecordBatch
    let cursor = std::io::Cursor::new(buffer);
    let reader = FileReader::try_new(cursor, None)?;

    let mut batches = Vec::new();
    for batch in reader {
        batches.push(batch?);
    }

    Ok(batches)
}
