use super::VisibleTrackWindow;
use crate::{
    consts::state_ids::METRONOME_TRACK_ID,
    consts::state_track::{TRACK_FOLDER_HEADER_HEIGHT, TRACK_SUBTRACK_GAP},
    menu,
    message::{Message, MidiLaneChannelSelection, Show, TrackAutomationTarget},
    state::{State, StateData, TrackLaneLayout},
    style,
};
use iced::{
    Alignment, Background, Border, Color, Element, Length, Point, Theme,
    alignment::Horizontal,
    widget::{
        Column, Row, Space, button, column, container, lazy, mouse_area, pick_list, row, text,
    },
};
use iced_drop::droppable;
use iced_fonts::lucide::{audio_waveform, disc};
use maolan_engine::message::{Action, TrackMidiLearnTarget};
use std::hash::{Hash, Hasher};

#[derive(Debug, Default)]
pub struct Tracks {
    state: State,
}

#[derive(Clone)]
struct VisibleAutomationLane {
    target: TrackAutomationTarget,
    points_len: usize,
}

#[derive(Clone)]
struct TrackViewData {
    name: String,
    height: f32,
    layout: TrackLaneLayout,
    selected: bool,
    resize_hovered: bool,
    armed: bool,
    frozen: bool,
    muted: bool,
    phase_inverted: bool,
    soloed: bool,
    solo_upstream: bool,
    is_master: bool,
    input_monitor: bool,
    disk_monitor: bool,
    audio_ins: usize,
    audio_outs: usize,
    primary_audio_ins: usize,
    primary_audio_outs: usize,
    midi_ins: usize,
    midi_outs: usize,
    midi_lane_channels: Vec<Option<u8>>,
    balance: f32,
    automation_mode: String,
    visible_automation_lanes: Vec<VisibleAutomationLane>,
    midi_learn_vol: bool,
    midi_learn_bal: bool,
    midi_learn_mute: bool,
    midi_learn_solo: bool,
    midi_learn_arm: bool,
    midi_learn_input_monitor: bool,
    midi_learn_disk_monitor: bool,
    vca_master: Option<String>,
    color: Option<iced::Color>,
}

#[derive(Clone)]
struct VcaStripViewData {
    label: String,
    prev_same: bool,
    next_same: bool,
}

pub(super) fn track_context_menu_overlay(
    state: &StateData,
    max_y: f32,
) -> Option<(Point, Element<'static, Message>)> {
    let menu_state = state.track_context_menu.as_ref()?;
    let track = state
        .tracks
        .iter()
        .find(|track| track.name == menu_state.track_name)?;

    let mut top = 0.0_f32;
    for t in state
        .tracks
        .iter()
        .filter(|track| track.name != METRONOME_TRACK_ID)
    {
        if t.name == track.name {
            break;
        }
        top += t.height;
    }

    let track_name = track.name.clone();
    let has_visible_automation = track.automation_lanes.iter().any(|lane| lane.visible);
    let freeze_supported = track.midi.ins == 0;
    let mut items = vec![
        menu::menu_item(
            "Automation: Volume",
            Message::TrackAutomationAddLane {
                track_name: track_name.clone(),
                target: TrackAutomationTarget::Volume,
            },
        ),
        menu::menu_item(
            "Automation: Balance",
            Message::TrackAutomationAddLane {
                track_name: track_name.clone(),
                target: TrackAutomationTarget::Balance,
            },
        ),
        menu::menu_item(
            "Automation: Mute",
            Message::TrackAutomationAddLane {
                track_name: track_name.clone(),
                target: TrackAutomationTarget::Mute,
            },
        ),
        menu::menu_item("Rename", Message::TrackRenameShow(track_name.clone())),
        menu::menu_item(
            if has_visible_automation {
                "Hide Automation (A-)"
            } else {
                "Show Automation (A+)"
            },
            Message::TrackAutomationToggle {
                track_name: track_name.clone(),
            },
        ),
        menu::menu_item(
            format!("Automation Mode: {}", track.automation_mode),
            Message::TrackAutomationCycleMode {
                track_name: track_name.clone(),
            },
        ),
        menu::menu_item(
            "Save as template",
            Message::TrackTemplateSaveShow(track_name.clone()),
        ),
        menu::menu_item("Add Return", Message::TrackAddReturn(track_name.clone())),
        menu::menu_item("Add Send", Message::TrackAddSend(track_name.clone())),
        menu::menu_item(
            "MIDI Learn Volume",
            Message::TrackMidiLearnArm {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::Volume,
            },
        ),
        menu::menu_item(
            "MIDI Learn Balance",
            Message::TrackMidiLearnArm {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::Balance,
            },
        ),
        menu::menu_item(
            "MIDI Learn Mute",
            Message::TrackMidiLearnArm {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::Mute,
            },
        ),
        menu::menu_item(
            "MIDI Learn Solo",
            Message::TrackMidiLearnArm {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::Solo,
            },
        ),
        menu::menu_item(
            "MIDI Learn Arm",
            Message::TrackMidiLearnArm {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::Arm,
            },
        ),
        menu::menu_item(
            "MIDI Learn Input Monitor",
            Message::TrackMidiLearnArm {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::InputMonitor,
            },
        ),
        menu::menu_item(
            "MIDI Learn Disk Monitor",
            Message::TrackMidiLearnArm {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::DiskMonitor,
            },
        ),
    ];

    let is_in_group = track.vca_master.is_some()
        || state
            .tracks
            .iter()
            .any(|t| t.vca_master.as_deref() == Some(&track_name));
    if track.primary_audio_outs() == 2 && !is_in_group {
        items.push(menu::menu_item(
            if track.is_master {
                "Unmaster"
            } else {
                "Master"
            },
            Message::Request(Action::TrackToggleMaster(track_name.clone())),
        ));
    }

    items.push(menu::menu_item(
        "Color",
        Message::Show(Show::TrackColor {
            track_name: track_name.clone(),
        }),
    ));

    if freeze_supported {
        items.push(menu::menu_item(
            if track.frozen { "Unfreeze" } else { "Freeze" },
            Message::TrackFreezeToggle {
                track_name: track_name.clone(),
            },
        ));
        if track.frozen {
            items.push(menu::menu_item(
                "Flatten",
                Message::TrackFreezeFlatten {
                    track_name: track_name.clone(),
                },
            ));
        }
    }

    let selected_tracks: Vec<_> = state
        .tracks
        .iter()
        .filter(|candidate| state.selected.contains(candidate.name.as_str()))
        .map(|candidate| candidate.name.clone())
        .collect();
    let can_group_selected =
        selected_tracks.len() > 1 && selected_tracks.iter().any(|name| name == &track.name);

    if can_group_selected && !track.is_master {
        items.push(menu::menu_item(
            "Group",
            Message::TrackGroupShow {
                track_name: track_name.clone(),
            },
        ));
    }

    if let Some(master) = track.vca_master.as_ref() {
        if !track.is_master {
            items.push(menu::menu_item(
                format!("Group: Ungroup ({master})"),
                Message::TrackSetVcaMaster {
                    track_name: track_name.clone(),
                    master_track: None,
                },
            ));
        }
        items.push(menu::menu_item(
            "Save group as template",
            Message::TrackGroupTemplateSaveShow(track_name.clone()),
        ));
    }

    if track.midi_learn_volume.is_some() {
        items.push(menu::menu_item(
            "Clear MIDI Learn Volume",
            Message::TrackMidiLearnClear {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::Volume,
            },
        ));
    }
    if track.midi_learn_balance.is_some() {
        items.push(menu::menu_item(
            "Clear MIDI Learn Balance",
            Message::TrackMidiLearnClear {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::Balance,
            },
        ));
    }
    if track.midi_learn_mute.is_some() {
        items.push(menu::menu_item(
            "Clear MIDI Learn Mute",
            Message::TrackMidiLearnClear {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::Mute,
            },
        ));
    }
    if track.midi_learn_solo.is_some() {
        items.push(menu::menu_item(
            "Clear MIDI Learn Solo",
            Message::TrackMidiLearnClear {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::Solo,
            },
        ));
    }
    if track.midi_learn_arm.is_some() {
        items.push(menu::menu_item(
            "Clear MIDI Learn Arm",
            Message::TrackMidiLearnClear {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::Arm,
            },
        ));
    }
    if track.midi_learn_input_monitor.is_some() {
        items.push(menu::menu_item(
            "Clear MIDI Learn Input Monitor",
            Message::TrackMidiLearnClear {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::InputMonitor,
            },
        ));
    }
    if track.midi_learn_disk_monitor.is_some() {
        items.push(menu::menu_item(
            "Clear MIDI Learn Disk Monitor",
            Message::TrackMidiLearnClear {
                track_name: track_name.clone(),
                target: TrackMidiLearnTarget::DiskMonitor,
            },
        ));
    }

    let menu_height = items.len() as f32 * 32.0 + 10.0;

    let panel = container(Column::with_children(items).spacing(2))
        .width(Length::Fill)
        .padding(6)
        .style(|theme| {
            let palette = theme.extended_palette();
            container::Style {
                background: Some(Background::Color(palette.background.weak.color)),
                border: Border {
                    color: palette.background.strong.color,
                    width: 1.0,
                    radius: 6.0.into(),
                },
                ..container::Style::default()
            }
        })
        .into();
    let mut y = (top + menu_state.anchor.y).max(0.0);
    if y + menu_height > max_y {
        y = (max_y - menu_height).max(0.0);
    }

    Some((Point::new(menu_state.anchor.x.max(0.0), y), panel))
}

impl Tracks {
    pub fn new(state: State) -> Self {
        Self { state }
    }

    fn trim_with_ellipsis(value: &str, max_chars: usize) -> String {
        if value.chars().count() <= max_chars {
            return value.to_string();
        }
        value.chars().take(max_chars).collect()
    }

    fn rotate_text_cw(value: &str, max_chars: usize) -> String {
        Self::trim_with_ellipsis(value, max_chars)
            .chars()
            .map(|c| c.to_string())
            .collect::<Vec<_>>()
            .join("\n")
    }

    fn char_slice(value: &str, start: usize, len: usize) -> String {
        value.chars().skip(start).take(len).collect()
    }

    fn format_balance(balance: f32) -> String {
        let balance = balance.clamp(-1.0, 1.0);
        if balance.abs() < 0.005 {
            "C".to_string()
        } else if balance < 0.0 {
            format!("L{}", (-balance * 100.0).round() as i32)
        } else {
            format!("R{}", (balance * 100.0).round() as i32)
        }
    }

    fn track_row_render_hash(
        track: &TrackViewData,
        track_width_px: f32,
        row_index: usize,
        vca: Option<&VcaStripViewData>,
    ) -> u64 {
        let mut hasher = std::collections::hash_map::DefaultHasher::new();
        row_index.hash(&mut hasher);
        track_width_px.to_bits().hash(&mut hasher);
        track.name.hash(&mut hasher);
        track.height.to_bits().hash(&mut hasher);
        track.layout.header_height.to_bits().hash(&mut hasher);
        track.layout.lane_height.to_bits().hash(&mut hasher);
        track.layout.audio_lanes.hash(&mut hasher);
        track.layout.midi_lanes.hash(&mut hasher);
        track.selected.hash(&mut hasher);
        track.resize_hovered.hash(&mut hasher);
        track.armed.hash(&mut hasher);
        track.frozen.hash(&mut hasher);
        track.muted.hash(&mut hasher);
        track.phase_inverted.hash(&mut hasher);
        track.soloed.hash(&mut hasher);
        track.solo_upstream.hash(&mut hasher);
        track.input_monitor.hash(&mut hasher);
        track.disk_monitor.hash(&mut hasher);
        track.audio_ins.hash(&mut hasher);
        track.audio_outs.hash(&mut hasher);
        track.primary_audio_ins.hash(&mut hasher);
        track.primary_audio_outs.hash(&mut hasher);
        track.midi_ins.hash(&mut hasher);
        track.midi_outs.hash(&mut hasher);
        track.midi_lane_channels.hash(&mut hasher);
        track.balance.to_bits().hash(&mut hasher);
        track.automation_mode.hash(&mut hasher);
        track.midi_learn_vol.hash(&mut hasher);
        track.midi_learn_bal.hash(&mut hasher);
        track.midi_learn_mute.hash(&mut hasher);
        track.midi_learn_solo.hash(&mut hasher);
        track.midi_learn_arm.hash(&mut hasher);
        track.midi_learn_input_monitor.hash(&mut hasher);
        track.midi_learn_disk_monitor.hash(&mut hasher);
        track.visible_automation_lanes.len().hash(&mut hasher);
        for lane in &track.visible_automation_lanes {
            std::mem::discriminant(&lane.target).hash(&mut hasher);
            match lane.target {
                TrackAutomationTarget::Volume
                | TrackAutomationTarget::Balance
                | TrackAutomationTarget::Mute => {}
                TrackAutomationTarget::Lv2Parameter {
                    instance_id,
                    index,
                    min,
                    max,
                } => {
                    instance_id.hash(&mut hasher);
                    index.hash(&mut hasher);
                    min.to_bits().hash(&mut hasher);
                    max.to_bits().hash(&mut hasher);
                }
                TrackAutomationTarget::Vst3Parameter {
                    instance_id,
                    param_id,
                } => {
                    instance_id.hash(&mut hasher);
                    param_id.hash(&mut hasher);
                }
                TrackAutomationTarget::ClapParameter {
                    instance_id,
                    param_id,
                    min,
                    max,
                } => {
                    instance_id.hash(&mut hasher);
                    param_id.hash(&mut hasher);
                    min.to_bits().hash(&mut hasher);
                    max.to_bits().hash(&mut hasher);
                }
            }
            lane.points_len.hash(&mut hasher);
        }
        if let Some(vca) = vca {
            vca.label.hash(&mut hasher);
            vca.prev_same.hash(&mut hasher);
            vca.next_same.hash(&mut hasher);
        }
        if let Some(color) = track.color {
            color.r.to_bits().hash(&mut hasher);
            color.g.to_bits().hash(&mut hasher);
            color.b.to_bits().hash(&mut hasher);
            color.a.to_bits().hash(&mut hasher);
        } else {
            0u8.hash(&mut hasher);
        }
        hasher.finish()
    }

    fn info_badge(label: String, accent: bool) -> Element<'static, Message> {
        container(text(label).size(9))
            .padding([2, 5])
            .style(move |_theme| container::Style {
                background: Some(Background::Color(if accent {
                    Color::from_rgba(0.34, 0.48, 0.69, 0.88)
                } else {
                    Color::from_rgba(0.16, 0.19, 0.25, 0.95)
                })),
                border: Border {
                    color: Color::from_rgba(0.62, 0.74, 0.9, if accent { 0.55 } else { 0.18 }),
                    width: 1.0,
                    radius: 5.0.into(),
                },
                text_color: Some(if accent {
                    Color::WHITE
                } else {
                    Color::from_rgb(0.82, 0.86, 0.93)
                }),
                ..container::Style::default()
            })
            .into()
    }

    fn render_track_row(
        track: TrackViewData,
        track_width_px: f32,
        index: usize,
        vca: Option<VcaStripViewData>,
    ) -> Element<'static, Message> {
        let selected = track.selected;
        let height = track.height;
        let (outer_body_bg, header_bg, inner_body_bg) = if let Some(color) = track.color {
            let with_alpha = |c: Color, a: f32| Color::from_rgba(c.r, c.g, c.b, a);
            if selected {
                (
                    with_alpha(style::mixer::brighten(color, 0.02), 0.98),
                    with_alpha(style::mixer::brighten(color, 0.12), 0.98),
                    with_alpha(style::mixer::brighten(color, 0.05), 0.98),
                )
            } else {
                (
                    with_alpha(style::mixer::darken(color, 0.06), 0.96),
                    with_alpha(style::mixer::brighten(color, 0.04), 0.96),
                    with_alpha(color, 0.96),
                )
            }
        } else {
            if selected {
                (
                    Color::from_rgba(0.10, 0.14, 0.22, 0.98),
                    Color::from_rgba(0.28, 0.39, 0.56, 0.98),
                    Color::from_rgba(0.13, 0.18, 0.27, 0.98),
                )
            } else {
                (
                    Color::from_rgba(0.08, 0.10, 0.16, 0.96),
                    Color::from_rgba(0.18, 0.22, 0.30, 0.96),
                    Color::from_rgba(0.11, 0.14, 0.20, 0.96),
                )
            }
        };
        let is_resize_hovered = track.resize_hovered;
        let midi_learn_vol = track.midi_learn_vol;
        let midi_learn_bal = track.midi_learn_bal;
        let midi_learn_mute = track.midi_learn_mute;
        let midi_learn_solo = track.midi_learn_solo;
        let midi_learn_arm = track.midi_learn_arm;
        let midi_learn_input_monitor = track.midi_learn_input_monitor;
        let midi_learn_disk_monitor = track.midi_learn_disk_monitor;
        let layout = track.layout;
        let lane_h = layout.lane_height.max(12.0);
        let has_visible_automation = !track.visible_automation_lanes.is_empty();
        let inline_midi_lane_selector =
            !track.is_master && track.audio_ins == 0 && track.midi_ins > 0;
        let max_name_chars = (((track_width_px - 98.0) / 7.0).floor() as i32).clamp(10, 64);
        let learn_count = [
            midi_learn_vol,
            midi_learn_bal,
            midi_learn_mute,
            midi_learn_solo,
            midi_learn_arm,
            midi_learn_input_monitor,
            midi_learn_disk_monitor,
        ]
        .iter()
        .filter(|bound| **bound)
        .count();
        let resize_handle_height = 6.0;
        let outer_spacing = 6.0;
        let inner_vertical_padding = 8.0;
        let lane_row_count = if track.is_master {
            track.visible_automation_lanes.len()
        } else {
            track
                .midi_ins
                .saturating_sub(usize::from(inline_midi_lane_selector))
                + track.visible_automation_lanes.len()
        };
        let lane_rows_height = if lane_row_count > 0 {
            lane_row_count as f32 * lane_h
                + lane_row_count.saturating_sub(1) as f32 * TRACK_SUBTRACK_GAP
        } else {
            0.0
        };
        let header_vertical_padding = 4.0;
        let body_vertical_padding = 6.0;
        let inner_available_height = (height - inner_vertical_padding).max(16.0);
        let body_height = (inner_available_height
            - TRACK_FOLDER_HEADER_HEIGHT
            - header_vertical_padding
            - resize_handle_height
            - outer_spacing
            - lane_rows_height
            - body_vertical_padding)
            .max(0.0);
        let mut title_badges = row![].spacing(4).align_y(Alignment::Center);
        if track.frozen {
            title_badges = title_badges.push(Self::info_badge("FRZ".to_string(), false));
        }
        if learn_count > 0 {
            title_badges =
                title_badges.push(Self::info_badge(format!("CC {}", learn_count), false));
        }

        let header = mouse_area(
            container(
                row![
                    text(Self::trim_with_ellipsis(
                        &track.name,
                        max_name_chars as usize
                    ))
                    .size(13),
                    Space::new().width(Length::Fill),
                    title_badges,
                ]
                .align_y(Alignment::Center)
                .spacing(6),
            )
            .height(Length::Fixed(TRACK_FOLDER_HEADER_HEIGHT))
            .padding([2, 6])
            .style(move |_theme| container::Style {
                background: Some(Background::Color(header_bg)),
                border: Border {
                    color: Color::from_rgba(0.78, 0.87, 0.99, if selected { 0.5 } else { 0.16 }),
                    width: 1.0,
                    radius: 7.0.into(),
                },
                text_color: Some(Color::from_rgb(0.92, 0.95, 1.0)),
                ..container::Style::default()
            }),
        )
        .on_press(Message::SelectTrack(track.name.clone()))
        .on_double_click(Message::OpenTrackPlugins(track.name.clone()))
        .on_enter(Message::ShortcutsHint(Some("Select track".to_string())))
        .on_exit(Message::ShortcutsHint(None));

        let track_name = track.name.clone();
        let mut controls: Vec<Element<'static, Message>> = vec![];
        if !track.is_master {
            controls.push(
                button(
                    container(text("R").size(13))
                        .width(Length::Fill)
                        .height(Length::Fill)
                        .center_x(Length::Fill)
                        .center_y(Length::Fill),
                )
                .width(Length::Fixed(22.0))
                .height(Length::Fixed(22.0))
                .padding(0)
                .style(move |theme, _state| style::arm::style(theme, track.armed))
                .on_press(Message::Request(Action::TrackToggleArm(track_name.clone())))
                .into(),
            );
        }
        controls.push(
            button(
                container(text("M").size(13))
                    .width(Length::Fill)
                    .height(Length::Fill)
                    .center_x(Length::Fill)
                    .center_y(Length::Fill),
            )
            .width(Length::Fixed(22.0))
            .height(Length::Fixed(22.0))
            .padding(0)
            .style(move |theme, _state| style::mute::style(theme, track.muted))
            .on_press(Message::Request(Action::TrackToggleMute(
                track.name.clone(),
            )))
            .into(),
        );
        controls.push(
            button(
                container(text("S").size(13))
                    .width(Length::Fill)
                    .height(Length::Fill)
                    .center_x(Length::Fill)
                    .center_y(Length::Fill),
            )
            .width(Length::Fixed(22.0))
            .height(Length::Fixed(22.0))
            .padding(0)
            .style(move |theme, _state| {
                style::solo::style(theme, track.soloed, track.solo_upstream)
            })
            .on_press(Message::Request(Action::TrackToggleSolo(
                track.name.clone(),
            )))
            .into(),
        );
        if !track.is_master {
            controls.push(
                button(
                    container(text("Ø").size(13))
                        .width(Length::Fill)
                        .height(Length::Fill)
                        .center_x(Length::Fill)
                        .center_y(Length::Fill),
                )
                .width(Length::Fixed(22.0))
                .height(Length::Fixed(22.0))
                .padding(0)
                .style(move |theme, _state| style::phase_invert::style(theme, track.phase_inverted))
                .on_press(Message::Request(Action::TrackTogglePhase(
                    track.name.clone(),
                )))
                .into(),
            );
        }
        if !track.is_master {
            controls.push(
                button(
                    container(audio_waveform().size(13))
                        .width(Length::Fill)
                        .height(Length::Fill)
                        .center_x(Length::Fill)
                        .center_y(Length::Fill),
                )
                .width(Length::Fixed(22.0))
                .height(Length::Fixed(22.0))
                .padding(0)
                .style(move |theme, _state| style::input::style(theme, track.input_monitor))
                .on_press(Message::Request(Action::TrackToggleInputMonitor(
                    track.name.clone(),
                )))
                .into(),
            );
            controls.push(
                button(
                    container(disc().size(13))
                        .width(Length::Fill)
                        .height(Length::Fill)
                        .center_x(Length::Fill)
                        .center_y(Length::Fill),
                )
                .width(Length::Fixed(22.0))
                .height(Length::Fixed(22.0))
                .padding(0)
                .style(move |theme, _state| style::disk::style(theme, track.disk_monitor))
                .on_press(Message::Request(Action::TrackToggleDiskMonitor(
                    track.name.clone(),
                )))
                .into(),
            );
        }
        let controls = Row::with_children(controls)
            .spacing(4)
            .align_y(Alignment::Center);

        let inline_midi_channel: Element<'static, Message> = if inline_midi_lane_selector {
            let track_name = track.name.clone();
            let selected_channel = MidiLaneChannelSelection::from_engine(
                track.midi_lane_channels.first().copied().flatten(),
            );
            container(
                pick_list(
                    MidiLaneChannelSelection::ALL,
                    Some(selected_channel),
                    move |channel| Message::TrackMidiLaneChannelSelected {
                        track_name: track_name.clone(),
                        lane: 0,
                        channel,
                    },
                )
                .placeholder("Channel"),
            )
            .padding([0, 2])
            .into()
        } else {
            Space::new().into()
        };

        let mut lane_rows: Column<'static, Message> = column![];
        if !track.is_master {
            for lane_index in 0..track.midi_ins {
                if inline_midi_lane_selector && lane_index == 0 {
                    continue;
                }
                let track_name = track.name.clone();
                let selected_channel = MidiLaneChannelSelection::from_engine(
                    track.midi_lane_channels.get(lane_index).copied().flatten(),
                );
                lane_rows = lane_rows.push(
                    container(
                        row![
                            Self::info_badge(format!("MIDI {}", lane_index + 1), true),
                            Space::new().width(Length::Fill),
                            pick_list(
                                MidiLaneChannelSelection::ALL,
                                Some(selected_channel),
                                move |channel| Message::TrackMidiLaneChannelSelected {
                                    track_name: track_name.clone(),
                                    lane: lane_index,
                                    channel,
                                },
                            )
                            .placeholder("Channel"),
                        ]
                        .align_y(Alignment::Center)
                        .spacing(6),
                    )
                    .width(Length::Fill)
                    .height(Length::Fixed(lane_h))
                    .padding([4, 6])
                    .style(move |_theme| container::Style {
                        background: Some(Background::Color(Color::from_rgba(
                            0.08, 0.18, 0.14, 0.9,
                        ))),
                        border: Border {
                            color: Color::from_rgba(0.34, 0.63, 0.48, 0.26),
                            width: 1.0,
                            radius: 6.0.into(),
                        },
                        text_color: Some(Color::from_rgb(0.83, 0.93, 0.88)),
                        ..container::Style::default()
                    }),
                );
            }
        }
        for lane in &track.visible_automation_lanes {
            lane_rows = lane_rows.push(
                container(
                    row![
                        Self::info_badge("AUTO".to_string(), false),
                        text(format!("{}", lane.target)).size(11),
                        Space::new().width(Length::Fill),
                        text(format!("{} pts", lane.points_len)).size(10),
                    ]
                    .align_y(Alignment::Center)
                    .spacing(6),
                )
                .width(Length::Fill)
                .height(Length::Fixed(lane_h))
                .padding([4, 6])
                .style(move |_theme| container::Style {
                    background: Some(Background::Color(Color::from_rgba(0.19, 0.16, 0.11, 0.88))),
                    border: Border {
                        color: Color::from_rgba(0.62, 0.49, 0.28, 0.22),
                        width: 1.0,
                        radius: 6.0.into(),
                    },
                    text_color: Some(Color::from_rgb(0.84, 0.79, 0.69)),
                    ..container::Style::default()
                }),
            );
        }

        let audio_io = format!("A {}/{}", track.audio_ins, track.audio_outs);
        let return_io = format!(
            "RET {}",
            track.audio_ins.saturating_sub(track.primary_audio_ins)
        );
        let midi_io = format!("M {}/{}", track.midi_ins, track.midi_outs);
        let send_io = format!(
            "SND {}",
            track.audio_outs.saturating_sub(track.primary_audio_outs)
        );
        let mode_label = track.automation_mode.clone();
        let balance_label = Self::format_balance(track.balance);
        let automation_hint = if has_visible_automation { "AUTO" } else { "" };

        let body = row![
            controls,
            if !track.is_master {
                column![
                    row![
                        inline_midi_channel,
                        Self::info_badge(audio_io, false),
                        Self::info_badge(return_io, false),
                        Self::info_badge(midi_io, false),
                        Self::info_badge(send_io, false),
                        Self::info_badge(mode_label, false),
                        Self::info_badge(balance_label, false),
                        container(text(automation_hint).size(9))
                            .padding([1, 0])
                            .style(|_theme: &Theme| container::Style {
                                text_color: Some(Color::from_rgba(0.77, 0.67, 0.52, 0.96)),
                                ..container::Style::default()
                            }),
                    ]
                    .spacing(4)
                    .align_y(Alignment::Center),
                ]
                .spacing(3)
                .width(Length::Fill)
                .align_x(Horizontal::Left)
                .into()
            } else {
                Element::from(Space::new().width(Length::Fill))
            },
        ]
        .spacing(8)
        .align_y(Alignment::Start);

        let track_ui: Column<'static, Message> = column![
            header,
            container(body)
                .height(Length::Fixed(body_height))
                .padding([3, 6])
                .style(move |_theme| container::Style {
                    background: Some(Background::Color(inner_body_bg)),
                    border: Border {
                        color: Color::from_rgba(
                            0.71,
                            0.82,
                            0.97,
                            if selected { 0.22 } else { 0.1 }
                        ),
                        width: 1.0,
                        radius: 8.0.into(),
                    },
                    text_color: Some(Color::from_rgb(0.90, 0.93, 0.98)),
                    ..container::Style::default()
                }),
            lane_rows.spacing(TRACK_SUBTRACK_GAP),
            mouse_area(
                mouse_area(
                    container("")
                        .width(Length::Fill)
                        .height(Length::Fixed(resize_handle_height))
                        .style(move |_theme| container::Style {
                            background: Some(Background::Color(if is_resize_hovered {
                                Color::from_rgba(0.51, 0.68, 0.92, 0.95)
                            } else {
                                Color::from_rgba(0.33, 0.40, 0.52, 0.65)
                            })),
                            border: Border {
                                color: Color::TRANSPARENT,
                                width: 0.0,
                                radius: 2.0.into(),
                            },
                            ..container::Style::default()
                        }),
                )
                .on_enter(Message::TrackResizeHover(track.name.clone(), true))
                .on_exit(Message::TrackResizeHover(track.name.clone(), false))
                .on_press(Message::TrackResizeStart(track.name.clone())),
            )
            .on_enter(Message::ShortcutsHint(Some("Resize height".to_string())))
            .on_exit(Message::ShortcutsHint(None)),
        ]
        .spacing(TRACK_SUBTRACK_GAP);

        let vca_strip_width = 12.0;
        let vca_strip = if let Some(vca) = vca {
            container(
                container(text(vca.label).size(9).align_x(Horizontal::Center))
                    .width(Length::Fill)
                    .align_x(Alignment::Center),
            )
            .width(Length::Fixed(vca_strip_width))
            .height(Length::Fill)
            .padding([6, 1])
            .style(move |_theme| container::Style {
                background: Some(Background::Color(Color::from_rgba(0.25, 0.34, 0.49, 0.92))),
                border: Border {
                    color: Color::from_rgba(0.82, 0.9, 1.0, 0.18),
                    width: 1.0,
                    radius: iced::border::Radius {
                        top_left: if vca.prev_same { 0.0 } else { 4.0 },
                        top_right: 0.0,
                        bottom_right: 0.0,
                        bottom_left: if vca.next_same { 0.0 } else { 4.0 },
                    },
                },
                text_color: Some(Color::from_rgb(0.89, 0.93, 0.99)),
                ..container::Style::default()
            })
        } else {
            container("")
                .width(Length::Fixed(vca_strip_width))
                .height(Length::Fill)
        };
        let vca_strip = container(row![Space::new().width(Length::Fixed(4.0)), vca_strip])
            .width(Length::Fixed(vca_strip_width + 4.0))
            .height(Length::Fill);
        let track_body = container(track_ui)
            .id(track.name.clone())
            .width(Length::Fill)
            .height(Length::Fixed(height))
            .padding([4, 6])
            .style(move |_theme| container::Style {
                background: Some(Background::Color(outer_body_bg)),
                border: Border {
                    color: Color::from_rgba(0.74, 0.84, 0.98, if selected { 0.32 } else { 0.08 }),
                    width: 1.0,
                    radius: 8.0.into(),
                },
                ..container::Style::default()
            });
        let track_body = container(row![vca_strip, track_body])
            .width(Length::Fill)
            .height(Length::Fixed(height));

        let track_with_drop = droppable(track_body)
            .on_drag(move |position, _| Message::TrackDrag { index, position })
            .on_drop(Message::TrackDropped);

        let track_name_for_menu = track.name.clone();
        let track_name_for_hover = track_name_for_menu.clone();
        let track_name_for_toggle = track_name_for_menu.clone();
        mouse_area(track_with_drop)
            .on_move(move |position| Message::TrackContextMenuHover {
                track_name: track_name_for_hover.clone(),
                position,
            })
            .on_right_press(Message::TrackContextMenuToggle(track_name_for_toggle))
            .into()
    }

    pub fn view(&self, visible_window: VisibleTrackWindow) -> Element<'_, Message> {
        let (tracks, width) = {
            let state = self.state.blocking_read();
            let hovered_resize_track = state.hovered_track_resize_handle.as_deref();
            let soloed_track_names: std::collections::HashSet<String> = state
                .tracks
                .iter()
                .filter(|t| t.soloed)
                .map(|t| t.name.clone())
                .collect();
            let mut upstream_track_names = std::collections::HashSet::new();
            if !soloed_track_names.is_empty() {
                let mut to_process: Vec<String> = soloed_track_names.iter().cloned().collect();
                let mut processed = std::collections::HashSet::new();
                while let Some(target_name) = to_process.pop() {
                    if !processed.insert(target_name.clone()) {
                        continue;
                    }
                    for conn in &state.connections {
                        if conn.kind == maolan_engine::kind::Kind::Audio
                            && conn.to_track == target_name
                            && !soloed_track_names.contains(&conn.from_track)
                        {
                            upstream_track_names.insert(conn.from_track.clone());
                            to_process.push(conn.from_track.clone());
                        }
                    }
                }
            }
            let tracks = state
                .tracks
                .iter()
                .filter(|track| track.name != METRONOME_TRACK_ID)
                .enumerate()
                .filter(|(index, _)| {
                    *index >= visible_window.start_index && *index < visible_window.end_index
                })
                .map(|(index, track)| {
                    (
                        index,
                        TrackViewData {
                            name: track.name.clone(),
                            height: track.height,
                            layout: track.lane_layout(),
                            selected: state.selected.contains(track.name.as_str()),
                            resize_hovered: hovered_resize_track == Some(track.name.as_str()),
                            armed: track.armed,
                            frozen: track.frozen,
                            muted: track.muted,
                            phase_inverted: track.phase_inverted,
                            soloed: track.soloed,
                            solo_upstream: upstream_track_names.contains(&track.name),
                            is_master: track.is_master,
                            input_monitor: track.input_monitor,
                            disk_monitor: track.disk_monitor,
                            audio_ins: track.audio.ins,
                            audio_outs: track.audio.outs,
                            primary_audio_ins: track.primary_audio_ins(),
                            primary_audio_outs: track.primary_audio_outs(),
                            midi_ins: track.midi.ins,
                            midi_outs: track.midi.outs,
                            midi_lane_channels: track.midi_lane_channels.clone(),
                            balance: track.balance,
                            automation_mode: track.automation_mode.to_string(),
                            visible_automation_lanes: track
                                .automation_lanes
                                .iter()
                                .filter(|lane| lane.visible)
                                .map(|lane| VisibleAutomationLane {
                                    target: lane.target,
                                    points_len: lane.points.len(),
                                })
                                .collect(),
                            midi_learn_vol: track.midi_learn_volume.is_some(),
                            midi_learn_bal: track.midi_learn_balance.is_some(),
                            midi_learn_mute: track.midi_learn_mute.is_some(),
                            midi_learn_solo: track.midi_learn_solo.is_some(),
                            midi_learn_arm: track.midi_learn_arm.is_some(),
                            midi_learn_input_monitor: track.midi_learn_input_monitor.is_some(),
                            midi_learn_disk_monitor: track.midi_learn_disk_monitor.is_some(),
                            vca_master: track.vca_master.clone(),
                            color: track.color,
                        },
                    )
                })
                .collect::<Vec<_>>();
            (tracks, state.tracks_width)
        };
        let track_width_px = match width {
            Length::Fixed(v) => v,
            _ => 200.0,
        };
        let track_heights: Vec<f32> = tracks.iter().map(|(_, t)| t.height).collect();
        let total_height = (visible_window.top_padding
            + track_heights.iter().sum::<f32>()
            + visible_window.bottom_padding)
            .max(1.0);
        let mut vca_has_followers = std::collections::HashSet::new();
        for (_, track) in &tracks {
            if let Some(master) = track.vca_master.as_ref() {
                vca_has_followers.insert(master.clone());
            }
        }
        let vca_display_labels: Vec<Option<String>> = tracks
            .iter()
            .map(|(_, track)| {
                track.vca_master.clone().or_else(|| {
                    vca_has_followers
                        .contains(&track.name)
                        .then(|| track.name.clone())
                })
            })
            .collect();
        let vca_view_data: Vec<Option<VcaStripViewData>> = tracks
            .iter()
            .enumerate()
            .map(|(index, _track)| {
                let master = vca_display_labels[index].as_ref()?;
                let prev_same =
                    index > 0 && vca_display_labels[index - 1].as_deref() == Some(master.as_str());
                let next_same = index + 1 < vca_display_labels.len()
                    && vca_display_labels[index + 1].as_deref() == Some(master.as_str());
                let mut group_start = index;
                while group_start > 0
                    && vca_display_labels[group_start - 1].as_deref() == Some(master.as_str())
                {
                    group_start -= 1;
                }
                let mut group_end = index;
                while group_end + 1 < vca_display_labels.len()
                    && vca_display_labels[group_end + 1].as_deref() == Some(master.as_str())
                {
                    group_end += 1;
                }
                let line_h = 10.0;
                let cap_for = |h: f32| -> usize { ((h - 8.0).max(0.0) / line_h).floor() as usize };
                let mut capacity_before = 0usize;
                for h in &track_heights[group_start..index] {
                    capacity_before += cap_for(*h);
                }
                let segment_capacity = cap_for(track_heights[index]);
                let label = if segment_capacity == 0 {
                    String::new()
                } else {
                    let mut group_capacity = 0usize;
                    for h in &track_heights[group_start..=group_end] {
                        group_capacity += cap_for(*h);
                    }
                    let full = Self::trim_with_ellipsis(master, group_capacity.max(1));
                    let segment = Self::char_slice(&full, capacity_before, segment_capacity);
                    Self::rotate_text_cw(&segment, segment_capacity)
                };
                Some(VcaStripViewData {
                    label,
                    prev_same,
                    next_same,
                })
            })
            .collect();
        let children: Vec<Element<'_, Message>> = tracks
            .into_iter()
            .enumerate()
            .map(|(visible_index, (index, track))| {
                let vca = vca_view_data[visible_index].clone();
                let hash = Self::track_row_render_hash(&track, track_width_px, index, vca.as_ref());
                lazy(hash, move |_| {
                    Self::render_track_row(track.clone(), track_width_px, index, vca.clone())
                })
                .into()
            })
            .collect();
        let mut result = column![];
        if visible_window.top_padding > 0.0 {
            result = result.push(Space::new().height(Length::Fixed(visible_window.top_padding)));
        }
        for child in children {
            result = result.push(child);
        }
        if visible_window.bottom_padding > 0.0 {
            result = result.push(Space::new().height(Length::Fixed(visible_window.bottom_padding)));
        }
        container(result.width(width))
            .style(|_theme| crate::style::app_background())
            .width(width)
            .height(Length::Fixed(total_height))
            .into()
    }
}
