use ratatui::prelude::Stylize;
use ratatui::{
    backend::CrosstermBackend,
    layout::{Constraint, Direction, Layout, Rect},
    style::{Color, Style},
    text::Span,
    widgets::{Block, Borders, Paragraph, Row, Table},
    Frame, Terminal,
};
use std::fmt;
use std::io;

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ProcessorType {
    Asr,
    Cut,
    Asrx,
    Yolo,
    Ocr,
    Face,
    Pose,
    Story,
    Caption,
}

impl ProcessorType {
    pub fn as_str(&self) -> &'static str {
        match self {
            ProcessorType::Asr => "ASR",
            ProcessorType::Cut => "CUT",
            ProcessorType::Asrx => "ASRX",
            ProcessorType::Yolo => "YOLO",
            ProcessorType::Ocr => "OCR",
            ProcessorType::Face => "Face",
            ProcessorType::Pose => "Pose",
            ProcessorType::Story => "Story",
            ProcessorType::Caption => "Caption",
        }
    }
}

impl fmt::Display for ProcessorType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ProcessorStatus {
    Pending,
    Running,
    Completed,
    Failed,
}

#[derive(Clone)]
pub struct ProcessorProgress {
    pub processor_type: ProcessorType,
    pub status: ProcessorStatus,
    pub current: u32,
    pub total: u32,
    pub message: String,
    pub elapsed_secs: u64,
}

impl ProcessorProgress {
    pub fn new(processor_type: ProcessorType) -> Self {
        Self {
            processor_type,
            status: ProcessorStatus::Pending,
            current: 0,
            total: 0,
            message: String::new(),
            elapsed_secs: 0,
        }
    }

    pub fn start(&mut self, total: u32) {
        self.status = ProcessorStatus::Running;
        self.total = total;
        self.current = 0;
        self.elapsed_secs = 0;
    }

    pub fn update(&mut self, current: u32, message: &str) {
        self.current = current;
        self.message = message.to_string();
    }

    pub fn complete(&mut self, message: &str) {
        self.status = ProcessorStatus::Completed;
        self.current = self.total;
        self.message = message.to_string();
    }

    pub fn fail(&mut self, message: &str) {
        self.status = ProcessorStatus::Failed;
        self.message = message.to_string();
    }

    pub fn progress_ratio(&self) -> f64 {
        if self.total == 0 {
            0.0
        } else {
            self.current as f64 / self.total as f64
        }
    }

    pub fn eta(&self) -> Option<std::time::Duration> {
        if self.status == ProcessorStatus::Completed || self.total == 0 || self.current == 0 {
            return None;
        }

        let elapsed = std::time::Duration::from_secs(self.elapsed_secs);
        let ratio = self.current as f64 / self.total as f64;
        if ratio <= 0.0 {
            return None;
        }
        let total_estimated = elapsed.div_f64(ratio);
        Some(total_estimated - elapsed)
    }
}

#[derive(Clone)]
pub struct ProgressState {
    pub processors: Vec<ProcessorProgress>,
    pub video_name: String,
    pub is_active: bool,
}

impl ProgressState {
    pub fn new(video_name: &str) -> Self {
        Self {
            processors: vec![
                ProcessorProgress::new(ProcessorType::Asr),
                ProcessorProgress::new(ProcessorType::Cut),
                ProcessorProgress::new(ProcessorType::Asrx),
                ProcessorProgress::new(ProcessorType::Yolo),
                ProcessorProgress::new(ProcessorType::Ocr),
                ProcessorProgress::new(ProcessorType::Face),
                ProcessorProgress::new(ProcessorType::Pose),
                ProcessorProgress::new(ProcessorType::Story),
                ProcessorProgress::new(ProcessorType::Caption),
            ],
            video_name: video_name.to_string(),
            is_active: false,
        }
    }

    pub fn get_processor(&mut self, processor_type: ProcessorType) -> &mut ProcessorProgress {
        self.processors
            .iter_mut()
            .find(|p| p.processor_type == processor_type)
            .unwrap()
    }

    pub fn completed_count(&self) -> usize {
        self.processors
            .iter()
            .filter(|p| p.status == ProcessorStatus::Completed)
            .count()
    }

    pub fn total_count(&self) -> usize {
        self.processors.len()
    }

    pub fn overall_progress(&self) -> f64 {
        let total: f64 = self.processors.iter().map(|p| p.progress_ratio()).sum();
        total / self.processors.len() as f64
    }

    pub fn start(&mut self) {
        self.is_active = true;
    }

    pub fn stop(&mut self) {
        self.is_active = false;
    }

    pub fn update_from_redis(
        &mut self,
        msg_type: &str,
        processor: &str,
        current: Option<i32>,
        total: Option<i32>,
        message: Option<&str>,
    ) {
        let proc_type = match processor.to_uppercase().as_str() {
            "ASR" => ProcessorType::Asr,
            "CUT" => ProcessorType::Cut,
            "ASRX" => ProcessorType::Asrx,
            "YOLO" => ProcessorType::Yolo,
            "OCR" => ProcessorType::Ocr,
            "FACE" => ProcessorType::Face,
            "POSE" => ProcessorType::Pose,
            "STORY" => ProcessorType::Story,
            "CAPTION" => ProcessorType::Caption,
            _ => return,
        };

        let p = self.get_processor(proc_type);

        match msg_type {
            "START" | "INFO" => {
                p.status = ProcessorStatus::Running;
                if let Some(m) = message {
                    p.message = m.to_string();
                }
            }
            "PROGRESS" => {
                p.status = ProcessorStatus::Running;
                if let Some(c) = current {
                    p.current = c as u32;
                }
                if let Some(t) = total {
                    p.total = t as u32;
                }
                if let Some(m) = message {
                    p.message = m.to_string();
                }
            }
            "COMPLETE" => {
                p.status = ProcessorStatus::Completed;
                p.current = p.total;
                if let Some(m) = message {
                    p.message = m.to_string();
                }
            }
            "ERROR" => {
                p.status = ProcessorStatus::Failed;
                if let Some(m) = message {
                    p.message = m.to_string();
                }
            }
            _ => {}
        }
    }
}

pub struct ProgressUi {
    terminal: Terminal<CrosstermBackend<io::Stderr>>,
    state: std::sync::Mutex<ProgressState>,
}

impl ProgressUi {
    pub fn new(video_name: &str) -> io::Result<Self> {
        use crossterm::execute;
        use crossterm::terminal::{enable_raw_mode, EnterAlternateScreen};

        let mut stderr = io::stderr();

        enable_raw_mode()?;
        execute!(stderr, EnterAlternateScreen)?;

        let backend = CrosstermBackend::new(stderr);
        let terminal = Terminal::new(backend)?;

        let state = std::sync::Mutex::new(ProgressState::new(video_name));

        Ok(Self { terminal, state })
    }

    pub fn state(&self) -> &std::sync::Mutex<ProgressState> {
        &self.state
    }

    pub fn render(&mut self) -> io::Result<()> {
        let state = self.state.lock().unwrap().clone();
        let video_name = state.video_name.clone();
        let is_active = state.is_active;
        let processors = state.processors.clone();
        let completed = state.completed_count();
        let total = state.total_count();
        let overall = state.overall_progress();

        self.terminal.draw(|f| {
            let chunks = Layout::default()
                .direction(Direction::Vertical)
                .constraints([
                    Constraint::Length(3),
                    Constraint::Min(10),
                    Constraint::Length(3),
                ])
                .split(f.area());

            Self::render_header_static(f, chunks[0], &video_name);
            Self::render_processors_static(f, chunks[1], &processors);
            Self::render_footer_static(f, chunks[2], completed, total, overall, is_active);
        })?;
        Ok(())
    }

    pub fn cleanup(&mut self) -> io::Result<()> {
        use crossterm::execute;
        use crossterm::terminal::{disable_raw_mode, LeaveAlternateScreen};

        execute!(self.terminal.backend_mut(), LeaveAlternateScreen)?;
        disable_raw_mode()?;
        Ok(())
    }

    fn render_header_static(f: &mut Frame, area: Rect, video_name: &str) {
        let title = format!(" Processing: {} ", video_name);
        let block = Block::default()
            .title(title)
            .borders(Borders::ALL)
            .style(Style::default().fg(Color::Cyan));
        f.render_widget(block, area);
    }

    fn render_processors_static(f: &mut Frame, area: Rect, processors: &[ProcessorProgress]) {
        let rows: Vec<Row> = processors
            .iter()
            .map(|p| Self::processor_to_row_static(p))
            .collect();

        let widths = [
            Constraint::Length(8),
            Constraint::Length(10),
            Constraint::Min(20),
            Constraint::Length(12),
        ];

        let table = Table::new(rows, widths)
            .block(Block::default().borders(Borders::ALL).title(" Processors "))
            .column_spacing(1);

        f.render_widget(table, area);
    }

    fn processor_to_row_static(p: &ProcessorProgress) -> Row<'_> {
        let status_color = match p.status {
            ProcessorStatus::Pending => Color::DarkGray,
            ProcessorStatus::Running => Color::Yellow,
            ProcessorStatus::Completed => Color::Green,
            ProcessorStatus::Failed => Color::Red,
        };

        let progress_bar = if p.total > 0 {
            let filled = (p.progress_ratio() * 20.0) as usize;
            let bar: String = format!(
                "[{}{}]",
                "█".repeat(filled.min(20)),
                "░".repeat((20 - filled).min(20))
            );
            bar
        } else {
            "[--------------------]".to_string()
        };

        let percentage = format!("{:5.1}%", p.progress_ratio() * 100.0);

        let detail = if p.total > 0 {
            format!("{}/{}", p.current, p.total)
        } else {
            "-".to_string()
        };

        let eta = match p.eta() {
            Some(d) => {
                let secs = d.as_secs();
                if secs > 60 {
                    format!("{}m", secs / 60)
                } else {
                    format!("{}s", secs)
                }
            }
            None => "-".to_string(),
        };

        Row::new(vec![
            Span::raw(format!(" {} ", p.processor_type.as_str())),
            Span::raw(progress_bar).fg(status_color),
            Span::raw(format!(" {} {}", detail, eta)),
            Span::raw(format!(" {} ", percentage)),
        ])
    }

    fn render_footer_static(
        f: &mut Frame,
        area: Rect,
        completed: usize,
        total: usize,
        overall: f64,
        is_active: bool,
    ) {
        let status_text = if is_active {
            format!(
                " Progress: {}/{} ({:.1}%) | Press Ctrl+C to cancel ",
                completed,
                total,
                overall * 100.0
            )
        } else if completed == total {
            " ✓ All processors completed! ".to_string()
        } else {
            " Ready ".to_string()
        };

        let block = Block::default()
            .borders(Borders::ALL)
            .style(Style::default().fg(Color::Green));
        let paragraph = Paragraph::new(status_text).block(block);
        f.render_widget(paragraph, area);
    }
}

impl Drop for ProgressUi {
    fn drop(&mut self) {
        let _ = self.cleanup();
    }
}
