//! Clipboard operations on steps, patterns, and banks.

use crate::model;
use crate::services::clipboard;
use crate::state::FlashKind;

use super::App;

impl App {
    pub fn copy_pattern(&mut self, bank: usize, pattern: usize) {
        self.copied_patterns = Some(vec![clipboard::copy_pattern(
            &self.project_state.project,
            bank,
            pattern,
        )]);
        self.ui.flash("Pattern copied", 150, FlashKind::Success);
    }

    pub fn paste_pattern(&mut self, bank: usize, pattern: usize) {
        if let Some(patterns) = self.copied_patterns.take() {
            if let Some(src) = patterns.first() {
                clipboard::paste_pattern(&mut self.project_state.project, bank, pattern, src);
                self.project_state.mark_dirty(bank, pattern);
                if self.editor_ctx.bank == bank && self.editor_ctx.pattern == pattern {
                    self.load_step_to_editor();
                }
                self.ui.flash("Pattern pasted", 150, FlashKind::Success);
            }
            self.copied_patterns = Some(patterns);
        }
    }

    pub fn copy_patterns(&mut self, bank: usize, patterns: &[usize]) {
        self.copied_patterns = Some(clipboard::copy_patterns(
            &self.project_state.project,
            bank,
            patterns,
        ));
        let n = patterns.len();
        self.ui.flash(
            &format!("{n} pattern{} copied", if n == 1 { "" } else { "s" }),
            150,
            FlashKind::Success,
        );
    }

    pub fn paste_patterns(&mut self, bank: usize, start: usize) {
        if let Some(sources) = self.copied_patterns.take() {
            let count = clipboard::paste_patterns(
                &mut self.project_state.project,
                bank,
                start,
                &sources,
            );
            self.copied_patterns = Some(sources);
            for i in 0..count {
                self.project_state.mark_dirty(bank, start + i);
            }
            if self.editor_ctx.bank == bank {
                self.load_step_to_editor();
            }
            self.ui.flash(
                &format!("{count} pattern{} pasted", if count == 1 { "" } else { "s" }),
                150,
                FlashKind::Success,
            );
        }
    }

    pub fn shift_patterns_up(&mut self) {
        let bank = self.patterns_nav.bank_cursor;
        let patterns = self.patterns_nav.selected_patterns();
        let start = *patterns.first().unwrap();
        let end = *patterns.last().unwrap();
        if let Some(dirty) = clipboard::shift_patterns_up(
            &mut self.project_state.project,
            bank,
            start..=end,
        ) {
            for (b, p) in &dirty {
                self.project_state.mark_dirty(*b, *p);
            }
            self.patterns_nav.pattern_cursor -= 1;
            if let Some(ref mut anchor) = self.patterns_nav.pattern_anchor {
                *anchor -= 1;
            }
        }
    }

    pub fn shift_patterns_down(&mut self) {
        let bank = self.patterns_nav.bank_cursor;
        let patterns = self.patterns_nav.selected_patterns();
        let start = *patterns.first().unwrap();
        let end = *patterns.last().unwrap();
        if let Some(dirty) = clipboard::shift_patterns_down(
            &mut self.project_state.project,
            bank,
            start..=end,
        ) {
            for (b, p) in &dirty {
                self.project_state.mark_dirty(*b, *p);
            }
            self.patterns_nav.pattern_cursor += 1;
            if let Some(ref mut anchor) = self.patterns_nav.pattern_anchor {
                *anchor += 1;
            }
        }
    }

    pub fn copy_bank(&mut self, bank: usize) {
        self.copied_banks = Some(vec![clipboard::copy_bank(
            &self.project_state.project,
            bank,
        )]);
        self.ui.flash("Bank copied", 150, FlashKind::Success);
    }

    pub fn paste_bank(&mut self, bank: usize) {
        if let Some(banks) = self.copied_banks.take() {
            if let Some(src) = banks.first() {
                let pat_count =
                    clipboard::paste_bank(&mut self.project_state.project, bank, src);
                for pattern in 0..pat_count {
                    self.project_state.mark_dirty(bank, pattern);
                }
                if self.editor_ctx.bank == bank {
                    self.load_step_to_editor();
                }
                self.ui.flash("Bank pasted", 150, FlashKind::Success);
            }
            self.copied_banks = Some(banks);
        }
    }

    pub fn copy_banks(&mut self, banks: &[usize]) {
        self.copied_banks = Some(clipboard::copy_banks(
            &self.project_state.project,
            banks,
        ));
        let n = banks.len();
        self.ui.flash(
            &format!("{n} bank{} copied", if n == 1 { "" } else { "s" }),
            150,
            FlashKind::Success,
        );
    }

    pub fn paste_banks(&mut self, start: usize) {
        if let Some(sources) = self.copied_banks.take() {
            let count = clipboard::paste_banks(
                &mut self.project_state.project,
                start,
                &sources,
            );
            self.copied_banks = Some(sources);
            for i in 0..count {
                let bank = start + i;
                for pattern in 0..model::MAX_PATTERNS {
                    self.project_state.mark_dirty(bank, pattern);
                }
            }
            if (start..start + count).contains(&self.editor_ctx.bank) {
                self.load_step_to_editor();
            }
            self.ui.flash(
                &format!("{count} bank{} pasted", if count == 1 { "" } else { "s" }),
                150,
                FlashKind::Success,
            );
        }
    }

    pub fn share_pattern(&mut self, bank: usize, pattern: usize) {
        let pattern_data = &self.project_state.project.banks[bank].patterns[pattern];
        match model::share::export(pattern_data) {
            Ok(encoded) => {
                let len = encoded.len();
                if let Ok(mut clip) = arboard::Clipboard::new() {
                    let _ = clip.set_text(encoded);
                }
                if len > 2000 {
                    self.ui.flash(
                        &format!("Shared ({len} chars — too long for Discord)"),
                        200,
                        FlashKind::Error,
                    );
                } else {
                    self.ui
                        .flash(&format!("Shared ({len} chars)"), 150, FlashKind::Success);
                }
            }
            Err(e) => {
                self.ui
                    .flash(&format!("Share failed: {e}"), 200, FlashKind::Error);
            }
        }
    }

    pub fn share_bank(&mut self, bank: usize) {
        let bank_data = &self.project_state.project.banks[bank];
        match model::share::export_bank(bank_data) {
            Ok(encoded) => {
                let len = encoded.len();
                if let Ok(mut clip) = arboard::Clipboard::new() {
                    let _ = clip.set_text(encoded);
                }
                if len > 2000 {
                    self.ui.flash(
                        &format!("Bank shared ({len} chars — too long for Discord)"),
                        200,
                        FlashKind::Error,
                    );
                } else {
                    self.ui
                        .flash(&format!("Bank shared ({len} chars)"), 150, FlashKind::Success);
                }
            }
            Err(e) => {
                self.ui
                    .flash(&format!("Share failed: {e}"), 200, FlashKind::Error);
            }
        }
    }

    pub fn import_bank(&mut self, bank: usize) {
        let text = match arboard::Clipboard::new().ok().and_then(|mut c| c.get_text().ok()) {
            Some(t) => t,
            None => {
                self.ui.flash("Clipboard empty", 150, FlashKind::Error);
                return;
            }
        };
        match model::share::import_bank(&text) {
            Ok(imported) => {
                self.project_state.project.banks[bank] = imported;
                for pattern in 0..model::MAX_PATTERNS {
                    self.project_state.mark_dirty(bank, pattern);
                }
                if self.editor_ctx.bank == bank {
                    self.load_step_to_editor();
                }
                self.ui
                    .flash("Bank imported", 150, FlashKind::Success);
            }
            Err(e) => {
                self.ui
                    .flash(&format!("Import failed: {e}"), 200, FlashKind::Error);
            }
        }
    }

    pub fn import_pattern(&mut self, bank: usize, pattern: usize) {
        let text = match arboard::Clipboard::new().ok().and_then(|mut c| c.get_text().ok()) {
            Some(t) => t,
            None => {
                self.ui
                    .flash("Clipboard empty", 150, FlashKind::Error);
                return;
            }
        };
        match model::share::import(&text) {
            Ok(imported) => {
                self.project_state.project.banks[bank].patterns[pattern] = imported;
                self.project_state.mark_dirty(bank, pattern);
                if self.editor_ctx.bank == bank && self.editor_ctx.pattern == pattern {
                    self.load_step_to_editor();
                }
                self.ui
                    .flash("Pattern imported", 150, FlashKind::Success);
            }
            Err(e) => {
                self.ui
                    .flash(&format!("Import failed: {e}"), 200, FlashKind::Error);
            }
        }
    }

    /// Convert linked steps into independent copies.
    pub fn harden_steps(&mut self) {
        let (bank, pattern) = self.current_bank_pattern();
        let indices = self.selected_steps();
        let count = clipboard::harden_steps(&mut self.project_state.project, bank, pattern, &indices);
        if count == 0 {
            self.ui.set_status("No linked steps to harden".to_string());
            return;
        }
        self.project_state.mark_dirty(bank, pattern);
        self.load_step_to_editor();
        self.editor_ctx.clear_selection();
        if count == 1 {
            self.ui.flash("Step hardened", 150, FlashKind::Success);
        } else {
            self.ui
                .flash(&format!("{count} steps hardened"), 150, FlashKind::Success);
        }
    }

    pub fn copy_steps(&mut self) {
        let (bank, pattern) = self.current_bank_pattern();
        let indices = self.selected_steps();
        let copied = clipboard::copy_steps(
            &self.project_state.project,
            bank,
            pattern,
            &indices,
        );
        let count = copied.steps.len();
        if let Ok(mut clip) = arboard::Clipboard::new() {
            let text: String = copied.steps.iter().map(|s| s.script.as_str()).collect::<Vec<_>>().join("\n");
            let _ = clip.set_text(text);
        }
        self.editor_ctx.copied_steps = Some(copied);
        self.ui
            .flash(&format!("Copied {count} steps"), 150, FlashKind::Info);
    }

    pub fn paste_steps(&mut self, link: &crate::engine::LinkState) {
        let Some(copied) = self.editor_ctx.copied_steps.take() else {
            self.ui.set_status("Nothing copied".to_string());
            return;
        };
        let (bank, pattern) = self.current_bank_pattern();
        let cursor = self.editor_ctx.step;
        let result = clipboard::paste_steps(
            &mut self.project_state.project,
            bank,
            pattern,
            cursor,
            &copied,
        );
        self.editor_ctx.copied_steps = Some(copied);
        self.project_state.mark_dirty(bank, pattern);
        self.load_step_to_editor();
        for &target in &result.compile_targets {
            let saved = self.editor_ctx.step;
            self.editor_ctx.step = target;
            self.compile_current_step(link);
            self.editor_ctx.step = saved;
        }
        self.editor_ctx.clear_selection();
        self.ui.flash(
            &format!("Pasted {} steps", result.count),
            150,
            FlashKind::Success,
        );
    }

    /// Paste steps as linked references to the originals.
    pub fn link_paste_steps(&mut self) {
        let Some(copied) = self.editor_ctx.copied_steps.take() else {
            self.ui.set_status("Nothing copied".to_string());
            return;
        };
        let (bank, pattern) = self.current_bank_pattern();
        let cursor = self.editor_ctx.step;
        let result = clipboard::link_paste_steps(
            &mut self.project_state.project,
            bank,
            pattern,
            cursor,
            &copied,
        );
        self.editor_ctx.copied_steps = Some(copied);
        match result {
            None => {
                self.ui
                    .set_status("Can only link within same pattern".to_string());
            }
            Some(count) => {
                self.project_state.mark_dirty(bank, pattern);
                self.load_step_to_editor();
                self.editor_ctx.clear_selection();
                self.ui.flash(
                    &format!("Linked {count} steps"),
                    150,
                    FlashKind::Success,
                );
            }
        }
    }

    pub fn duplicate_steps(&mut self, link: &crate::engine::LinkState) {
        let (bank, pattern) = self.current_bank_pattern();
        let indices = self.selected_steps();
        let result = clipboard::duplicate_steps(
            &mut self.project_state.project,
            bank,
            pattern,
            &indices,
        );
        self.project_state.mark_dirty(bank, pattern);
        self.load_step_to_editor();
        for &target in &result.compile_targets {
            let saved = self.editor_ctx.step;
            self.editor_ctx.step = target;
            self.compile_current_step(link);
            self.editor_ctx.step = saved;
        }
        self.editor_ctx.clear_selection();
        self.ui.flash(
            &format!("Duplicated {} steps", result.count),
            150,
            FlashKind::Success,
        );
    }
}
