use std::io::Read;
use std::time::Instant;
use crate::serial_core::frame::{FrameHandler};
use crate::serial_core::{codec::Codec, error::CodecError, frame::TestFrame};
use anyhow::anyhow;
use async_trait::async_trait;
use csv::StringRecord;
use crate::serial_core::record::{write_csv, CsvExporter, CsvImporter, RecordedFrame, Recording};
use crate::serial_core::utils::{
    elapsed_millis,
    usize_to_u16_be_bytes
};
pub struct TestCodec {
    buffer: Vec<u8>,
}

pub struct TestHandler;

impl TestCodec {
    pub fn new() -> TestCodec {
        Self { buffer: Vec::new() }
    }
}

impl Codec<TestFrame> for TestCodec {
    fn decode(&mut self, input: &[u8], session_started_at: Instant) -> Result<Vec<TestFrame>, CodecError> {
        self.buffer.extend_from_slice(input);
        let mut frames = Vec::new();

        loop {
            if self.buffer.len() < 6 {
                break;
            }

            let header_pos = self.buffer.windows(2).position(|w| w == [0xAA, 0x55]);

            let Some(pos) = header_pos else {
                self.buffer.clear();
                break;
            };
            if pos > 0 {
                self.buffer.drain(0..pos);
            }

            if self.buffer.len() < 6 {
                break;
            }

            let cmd = self.buffer[2];
            let length_bytes = [self.buffer[3], self.buffer[4]];
            let length = u16::from_be_bytes(length_bytes) as usize;
            let frame_length = (length + 6) as usize;
            if self.buffer.len() < frame_length {
                break;
            }
            let payload = self.buffer[5..5 + length].to_vec();
            // let checksum = crc8(payload.as_slice());
            let crc8_alg = crc::Crc::<u8>::new(&crc::CRC_8_SMBUS);
            let checksum = crc8_alg.checksum(payload.as_slice());
            if self.buffer[frame_length - 1] != checksum {
                self.buffer.drain(0..1);
                continue;
            }
            let dts = elapsed_millis(session_started_at);
            println!("dts_ms: {dts}");
            frames.push(TestFrame {
                header: [0xAA, 0x55],
                cmd: cmd,
                length: length,
                payload: payload,
                checksum: checksum,
                dts_ms: dts,
            });

            self.buffer.drain(0..frame_length);
        }

        Ok(frames)
    }
    fn encode(&self, frame: &TestFrame) -> Result<Vec<u8>, CodecError> {
        let _ = u16::try_from(frame.payload.len()).map_err(|_| CodecError::PayloadTooLarge)?;
        let mut out = Vec::with_capacity(6 + frame.length);
        out.extend_from_slice(&frame.header);
        out.push(frame.cmd);
        out.extend_from_slice(&usize_to_u16_be_bytes(frame.length));
        out.extend_from_slice(&frame.payload);
        out.push(frame.checksum);

        Ok(out)
    }
}

#[async_trait]
impl FrameHandler<TestFrame, i32> for TestHandler {
    async fn on_frame(&mut self, frame: &TestFrame) -> anyhow::Result<Option<Vec<i32>>> {
        match frame.cmd {
            0x01 => {
                let vals = parse_data_frame(&frame.payload)?;
                Ok(Some(vals))
            }
            _ => Ok(None),
        }
    }
}

fn parse_data_frame(data: &[u8]) -> Result<Vec<i32>, CodecError> {
    if data.len() % 2 != 0 {
        return Err(CodecError::InvalidLength);
    }

    let vals: Vec<i32> = data
        .chunks_exact(2)
        .map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]) as i32)
        .collect::<Vec<i32>>();

    Ok(vals)
}

pub struct TestCsvExporter;
pub struct TestCsvImporter {
    channels: usize,
    data_row: usize,
    packets: Vec<TestDataPacket>,
}

#[derive(Clone)]
pub struct TestDataPacket {
    pub data: Vec<i32>,
    pub dts_ms: u64
}

impl TryFrom<&TestFrame> for TestDataPacket {
    type Error = CodecError;
    fn try_from(frame: &TestFrame) -> Result<TestDataPacket, Self::Error> {
        let data = parse_data_frame(&frame.payload)?;
        let dts = frame.dts_ms;
        Ok(TestDataPacket { data: data, dts_ms: dts })
    }
}
// impl From<TestFrame> for TestDataPacket {
//     fn from(frame: TestFrame) -> Self {
//         let data = parse_data_frame(&frame.payload)?;
//         let dts = frame.dts_ms;
//         TestDataPacket { data: data, dts_ms: dts }
//     }
// }


impl CsvExporter<TestFrame> for TestCsvExporter {
    type Error = CodecError;
    fn csv_header(&self, recording: &Recording<TestFrame>) -> Vec<String> {
        let channel_nb = recording
            .frames
            .iter()
            .find_map(|frame| parse_data_frame(&frame.frame.payload).ok().map(|vals| vals.len()))
            .unwrap_or(0);
        let mut header: Vec<String> = Vec::new();
        for i in 0..channel_nb {
            header.push(format!("channel{}", i + 1));
        }
        header.push("dts".to_string());

        header
    }

    fn csv_row(&self, item: &RecordedFrame<TestFrame>) -> anyhow::Result<Vec<String>> {
        let packet: TestDataPacket = TestDataPacket::try_from(&item.frame)?;
        let mut row: Vec<String> = packet.data.iter().map(|&x| x.to_string()).collect();
        row.push(packet.dts_ms.to_string());
        Ok(row)
    }
}

impl TestCsvImporter {
    pub fn new(_path: &str) -> TestCsvImporter {
        Self {
            channels: 0,
            data_row: 0,
            packets: Vec::new(),
        }
    }

    fn parse_record(&mut self, record: StringRecord) -> anyhow::Result<TestDataPacket>{
        if self.channels == 0 {
            return Err(anyhow!("csv header is missing channel columns"));
        }

        if record.len() < self.channels + 1 {
            return Err(anyhow!("csv row has insufficient columns"));
        }

        let mut data = Vec::with_capacity(self.channels);
        for index in 0..self.channels {
            let cell = record.get(index).ok_or_else(|| anyhow!("missing channel cell"))?;
            data.push(cell.parse::<i32>()?);
        }

        let dts_cell = record
            .get(self.channels)
            .ok_or_else(|| anyhow!("missing dts cell"))?;
        let dts_ms = dts_cell.parse::<u64>()?;

        Ok(TestDataPacket {
            data: data,
            dts_ms: dts_ms,
        })
    }
}

impl CsvImporter<TestDataPacket> for TestCsvImporter {
    fn load<R: Read>(&mut self, reader: R) -> anyhow::Result<Vec<TestDataPacket>> {
        let mut rdr = csv::Reader::from_reader(reader);
        let headers = rdr.headers()?.clone();
        self.channels = headers.len().saturating_sub(1);
        self.data_row = 0;
        self.packets.clear();

        for record in rdr.records() {
            let record = record?;
            let packet = self.parse_record(record)?;
            self.packets.push(packet);
            self.data_row += 1;
        }

        Ok(self.packets.clone())
    }
}


pub fn export_recording_csv<W>(recording: &Recording<TestFrame>, writer: W) -> anyhow::Result<()>
where
    W: std::io::Write,
{
    write_csv(recording, &TestCsvExporter, writer)
}

#[cfg(test)]
mod tests {
    use super::*;
    use csv::Reader;
    use std::io::Cursor;

    #[test]
    fn test_read_csv_basic() -> anyhow::Result<()> {
        let mut rdr = Reader::from_path("recording_20260329_125238.csv")?;
        let headers = rdr.headers()?;
        println!("headers: {:?}", headers);

        for result in rdr.records() {
            let record = result?;
            println!("record: {:?}", record);
        }

        Ok(())
    }
}
