use crate::*;

#[derive(Debug, Clone)]
pub enum Message {
    TrackStateChanged {
        column: usize,
        row: usize,
        state: TrackState,
    },
    TrackVolumeChanged {
        column: usize,
        row: usize,
        volume: f32,
    },
    SelectedColumnChanged {
        column: usize,
    },
    SelectedRowChanged {
        row: usize,
    },
    MetronomePosition {
        position: f32, // 0.0 - 1.0 position within current beat
    },
    Tempo {
        bpm: f32,
    },
    ColumnBeatsChanged {
        column: usize,
        beats: usize,
    },
    ColumnBeatChanged {
        column: usize,
        beat: usize,
    },
}

impl Message {
    pub fn from_osc_packet(packet: rosc::OscPacket) -> Result<Option<Self>> {
        match packet {
            rosc::OscPacket::Message(msg) => Self::from_osc_message(msg),
            rosc::OscPacket::Bundle(_) => {
                // For now, we don't handle bundles
                Ok(None)
            }
        }
    }

    pub fn apply_to_state<const COLS: usize, const ROWS: usize>(
        &self,
        state: &mut State<COLS, ROWS>,
    ) {
        match self {
            Message::TrackStateChanged {
                column,
                row,
                state: track_state,
            } => {
                state.set_track_state(*column, *row, track_state.clone());
            }
            Message::SelectedColumnChanged { column } => {
                state.set_selected_column(*column);
            }
            Message::SelectedRowChanged { row } => {
                state.set_selected_row(*row);
            }
            Message::TrackVolumeChanged {
                column,
                row,
                volume,
            } => {
                state.set_track_volume(*column, *row, *volume);
            }
            Message::MetronomePosition { position } => {
                state.set_metronome_position(*position);
            }
            Message::Tempo { bpm } => {
                state.set_tempo(*bpm);
            }
            Message::ColumnBeatsChanged { column, beats } => {
                state.set_column_beats(*column, *beats);
            }
            Message::ColumnBeatChanged { column, beat } => {
                state.set_column_beat(*column, *beat);
            }
        }
    }

    fn from_osc_message(msg: rosc::OscMessage) -> Result<Option<Self>> {
        let rosc::OscMessage { addr, args } = msg;

        if addr.starts_with("/looper/cell/") && addr.ends_with("/state") {
            // Parse: /looper/cell/{column}/{row}/state
            let parts: Vec<&str> = addr.split('/').collect();
            if parts.len() != 6 {
                return Err(Error::AddressParseError(addr));
            }

            let column: usize = parts[3].parse::<usize>().address_part_result("column")? - 1; // Convert to 0-based
            let row: usize = parts[4].parse::<usize>().address_part_result("row")? - 1; // Convert to 0-based

            if let Some(rosc::OscType::String(state_str)) = args.first() {
                let state = state_str.parse::<TrackState>().unwrap_or(TrackState::Empty);
                log::debug!("TrackStateChanged: column={column}, row={row}, state={state}");
                return Ok(Some(Message::TrackStateChanged { column, row, state }));
            }
        } else if addr.starts_with("/looper/cell/") && addr.ends_with("/volume") {
            // Parse: /looper/cell/{column}/{row}/volume
            let parts: Vec<&str> = addr.split('/').collect();
            if parts.len() != 6 {
                return Err(Error::AddressParseError(addr));
            }

            let column: usize = parts[3].parse::<usize>().address_part_result("column")? - 1; // Convert to 0-based
            let row: usize = parts[4].parse::<usize>().address_part_result("row")? - 1; // Convert to 0-based

            if let Some(rosc::OscType::Float(volume)) = args.first() {
                log::debug!("TrackVolumeChanged: column={column}, row={row}, volume={volume}");
                return Ok(Some(Message::TrackVolumeChanged {
                    column,
                    row,
                    volume: *volume,
                }));
            }
        } else if addr.starts_with("/looper/column/") && addr.ends_with("/beats") {
            // Parse: /looper/column/{column}/beats
            let parts: Vec<&str> = addr.split('/').collect();
            if parts.len() != 5 {
                return Err(Error::AddressParseError(addr));
            }

            let column: usize = parts[3].parse::<usize>().address_part_result("column")? - 1; // Convert to 0-based

            if let Some(rosc::OscType::Int(beats)) = args.first() {
                log::debug!("ColumnBeatsChanged: column={column}, beats={beats}");
                return Ok(Some(Message::ColumnBeatsChanged {
                    column,
                    beats: *beats as usize,
                }));
            }
        } else if addr.starts_with("/looper/column/") && addr.ends_with("/beat") {
            // Parse: /looper/column/{column}/beat
            let parts: Vec<&str> = addr.split('/').collect();
            if parts.len() != 5 {
                return Err(Error::AddressParseError(addr));
            }

            let column: usize = parts[3].parse::<usize>().address_part_result("column")? - 1; // Convert to 0-based

            if let Some(rosc::OscType::Int(beat)) = args.first() {
                let beat = (*beat as usize).saturating_sub(1); // Convert to 0-based
                log::debug!("ColumnBeatChanged: column={column}, beat={beat}");
                return Ok(Some(Message::ColumnBeatChanged { column, beat }));
            }
        } else if addr == "/looper/selected/column" {
            if let Some(rosc::OscType::Int(column_1based)) = args.first() {
                let column = (*column_1based as usize).saturating_sub(1); // Convert to 0-based
                log::debug!("SelectedColumnChanged: column={column}");
                return Ok(Some(Message::SelectedColumnChanged { column }));
            }
        } else if addr == "/looper/selected/row" {
            if let Some(rosc::OscType::Int(row_1based)) = args.first() {
                let row = (*row_1based as usize).saturating_sub(1); // Convert to 0-based
                log::debug!("SelectedRowChanged: row={row}");
                return Ok(Some(Message::SelectedRowChanged { row }));
            }
        } else if addr == "/looper/metronome/position" {
            if let Some(rosc::OscType::Float(position)) = args.first() {
                log::trace!("MetronomePosition: position={position}");
                return Ok(Some(Message::MetronomePosition {
                    position: *position,
                }));
            }
        } else if addr == "/looper/tempo" {
            if let Some(rosc::OscType::Float(bpm)) = args.first() {
                log::debug!("Tempo: bpm={bpm}");
                return Ok(Some(Message::Tempo { bpm: *bpm }));
            }
        }
        // Unknown or unsupported message
        Ok(None)
    }

    pub fn to_osc_packet(self) -> rosc::OscPacket {
        match self {
            Message::TrackStateChanged { column, row, state } => {
                let address = format!("/looper/cell/{}/{}/state", column + 1, row + 1); // 1-based indexing

                rosc::OscPacket::Message(rosc::OscMessage {
                    addr: address,
                    args: vec![rosc::OscType::String(state.to_string())],
                })
            }
            Message::TrackVolumeChanged {
                column,
                row,
                volume,
            } => {
                let address = format!("/looper/cell/{}/{}/volume", column + 1, row + 1); // 1-based indexing

                rosc::OscPacket::Message(rosc::OscMessage {
                    addr: address,
                    args: vec![rosc::OscType::Float(volume)],
                })
            }
            Message::SelectedColumnChanged { column } => {
                let address = "/looper/selected/column".to_string();

                rosc::OscPacket::Message(rosc::OscMessage {
                    addr: address,
                    args: vec![rosc::OscType::Int((column + 1) as i32)], // 1-based indexing
                })
            }
            Message::SelectedRowChanged { row } => {
                let address = "/looper/selected/row".to_string();

                rosc::OscPacket::Message(rosc::OscMessage {
                    addr: address,
                    args: vec![rosc::OscType::Int((row + 1) as i32)], // 1-based indexing
                })
            }
            Message::MetronomePosition { position } => {
                let address = "/looper/metronome/position".to_string();

                rosc::OscPacket::Message(rosc::OscMessage {
                    addr: address,
                    args: vec![rosc::OscType::Float(position)],
                })
            }
            Message::Tempo { bpm } => {
                let address = "/looper/tempo".to_string();

                rosc::OscPacket::Message(rosc::OscMessage {
                    addr: address,
                    args: vec![rosc::OscType::Float(bpm)],
                })
            }
            Message::ColumnBeatsChanged { column, beats } => {
                let address = format!("/looper/column/{}/beats", column + 1); // 1-based indexing

                rosc::OscPacket::Message(rosc::OscMessage {
                    addr: address,
                    args: vec![rosc::OscType::Int(beats as i32)],
                })
            }
            Message::ColumnBeatChanged { column, beat } => {
                let address = format!("/looper/column/{}/beat", column + 1); // 1-based indexing

                rosc::OscPacket::Message(rosc::OscMessage {
                    addr: address,
                    args: vec![rosc::OscType::Int((beat + 1) as i32)], // Convert to 1-based
                })
            }
        }
    }
}
