use std::borrow::Cow;

use anyhow::Result;
use crossterm::event::Event;
use gnostr_asyncgit::sync::{self, commit_files::OldNew, CommitDetails, CommitId, RepoPathRef};
use ratatui::{
    layout::{Constraint, Direction, Layout, Rect},
    text::{Line, Span, Text},
    Frame,
};

use crate::{
    app::Environment,
    components::{
        commit_details::style::{style_detail, Detail},
        dialog_paragraph,
        utils::time_to_string,
        CommandBlocking, CommandInfo, Component, DrawableComponent, EventState,
    },
    strings::{self},
    ui::style::SharedTheme,
};

pub struct CompareDetailsComponent {
    repo: RepoPathRef,
    data: Option<OldNew<CommitDetails>>,
    theme: SharedTheme,
    focused: bool,
}

impl CompareDetailsComponent {

    pub fn new(env: &Environment, focused: bool) -> Self {
        Self {
            data: None,
            theme: env.theme.clone(),
            focused,
            repo: env.repo.clone(),
        }
    }

    pub fn set_commits(&mut self, ids: Option<OldNew<CommitId>>) {
        self.data = ids.and_then(|ids| {
            let old = sync::get_commit_details(&self.repo.borrow(), ids.old).ok()?;
            let new = sync::get_commit_details(&self.repo.borrow(), ids.new).ok()?;

            Some(OldNew { old, new })
        });
    }

    #[allow(unstable_name_collisions)]
    fn get_commit_text(&self, data: &CommitDetails) -> Vec<Line<'_>> {
        let mut res = vec![
            Line::from(vec![
                style_detail(&self.theme, &Detail::Author),
                Span::styled(
                    Cow::from(format!("{} <{}>", data.author.name, data.author.email)),
                    self.theme.text(true, false),
                ),
            ]),
            Line::from(vec![
                style_detail(&self.theme, &Detail::Date),
                Span::styled(
                    Cow::from(time_to_string(data.author.time, false)),
                    self.theme.text(true, false),
                ),
            ]),
        ];

        res.push(Line::from(vec![
            style_detail(&self.theme, &Detail::Message),
            Span::styled(
                Cow::from(
                    data.message
                        .as_ref()
                        .map(|msg| msg.subject.clone())
                        .unwrap_or_default(),
                ),
                self.theme.text(true, false),
            ),
        ]));

        res
    }
}

impl DrawableComponent for CompareDetailsComponent {
    fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Length(5), Constraint::Length(5)].as_ref())
            .split(rect);

        if let Some(data) = &self.data {
            f.render_widget(
                dialog_paragraph(
                    &strings::commit::compare_details_info_title(true, data.old.short_hash()),
                    Text::from(self.get_commit_text(&data.old)),
                    &self.theme,
                    false,
                ),
                chunks[0],
            );

            f.render_widget(
                dialog_paragraph(
                    &strings::commit::compare_details_info_title(false, data.new.short_hash()),
                    Text::from(self.get_commit_text(&data.new)),
                    &self.theme,
                    false,
                ),
                chunks[1],
            );
        }

        Ok(())
    }
}

impl Component for CompareDetailsComponent {
    fn commands(&self, _out: &mut Vec<CommandInfo>, _force_all: bool) -> CommandBlocking {
        CommandBlocking::PassingOn
    }

    fn event(&mut self, _event: &Event) -> Result<EventState> {
        Ok(EventState::NotConsumed)
    }

    fn focused(&self) -> bool {
        self.focused
    }

    fn focus(&mut self, focus: bool) {
        self.focused = focus;
    }
}
