mod jnovel;

use iced::{Application, Command, Element, executor, Length, Padding, Pixels, Settings, Subscription, subscription, Theme, time};
use iced::widget::{button, column, text, Column, container, toggler::{Toggler}, pick_list, row, Row, text_input};
use std::fmt::{Display, Formatter};
use chrono::{Local};
use serde::{Deserialize};

#[derive(Debug, Clone)]
struct CalibreWebImporter {
    jnc: jnovel::State,
    settings: AppSettings,
    current_menu: Menu,
    previous_menu: Menu,
}

impl Default for CalibreWebImporter {
    fn default() -> Self {
        Self {
            jnc: jnovel::State::default(),
            settings: AppSettings::default(),
            current_menu: Menu::JNovel,
            previous_menu: Menu::JNovel,
        }
    }
}



#[derive(Debug, Clone)]
struct AppSettings {
    theme: AppTheme,
}

impl Default for AppSettings {
    fn default() -> Self {
        Self {
            theme: AppTheme::System
        }
    }
}

#[derive(Debug, Clone, Copy)]
enum Menu {
    Settings,
    JNovel,
    Calibre
}

#[derive(Debug, Clone, Eq, PartialEq)]
enum AppTheme {
    Light,
    Dark,
    System
}

impl Display for AppTheme {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let text = match self {
            AppTheme::Light => "Light",
            AppTheme::Dark => "Dark",
            AppTheme::System => "System",
        };

        write!(f, "{text}")
    }
}

impl AppTheme {
    fn get_iced_theme(&self) -> Theme {
        match self {
            AppTheme::Light => Theme::Light,
            AppTheme::Dark => Theme::Dark,
            AppTheme::System => system_theme_mode().get_iced_theme(),
        }
    }
}

#[derive(Debug, Clone)]
enum Message {
    JncAction(jnovel::Message),
    SetTheme(AppTheme),
    Navigate(Menu),
}

impl Application for CalibreWebImporter {
    type Executor = executor::Default;
    type Message = Message;
    type Theme = Theme;
    type Flags = ();

    fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
        (
            Self {
                settings: AppSettings {
                    theme: AppTheme::System,
                    ..Default::default()
                },
                ..Default::default()
            },
            Command::none()
        )
    }
    fn title(&self) -> String {
        String::from("Calibre Web Importer")
    }

    fn update(&mut self, message: Message) -> Command<Message> {
        match message {
            Message::JncAction(jnc_message) => {
                match jnc_message {
                    jnovel::Message::Login => {
                        self.jnc.login_state = jnovel::LoginState::LoggingIn;
                        let _ = open::that("https://j-novel.club/user/otp");
                        Command::perform(jnovel::State::login(), |status| Message::JncAction(jnovel::Message::LoginState(status)))
                    }
                    jnovel::Message::Logout => {
                        println!("Logging out from J-Novel Club");
                        let jnc = self.jnc.clone();
                        self.jnc.token = None;
                        self.jnc.login_state = jnovel::LoginState::LoggedOut;
                        self.jnc.library_state = jnovel::LibraryState::Unloaded;
                        Command::perform(jnovel::State::logout(jnc), |()| Message::JncAction(jnovel::Message::LoginState(jnovel::LoginState::LoggedOut)))
                    }
                    jnovel::Message::LoginState(status) => {
                        if let jnovel::LoginState::Success(token) = status {
                            self.jnc.token = Some(token);
                            self.jnc.login_state = status;

                            Command::none()
                        }
                        else {
                            self.jnc.login_state = status;
                            Command::none()
                        }
                    }
                    jnovel::Message::LibraryState(status) => {
                        match status {
                            jnovel::LibraryState::Unloaded => {
                                Command::none()
                            }
                            jnovel::LibraryState::Loading(progress) => {
                                println!("Loading: {}", progress);
                                Command::none()
                            }
                            jnovel::LibraryState::Loaded(data) => {
                                self.jnc.library = data;
                                println!("Loaded");
                                Command::none()
                            }
                            jnovel::LibraryState::Idle => {
                                Command::none()
                            }
                            jnovel::LibraryState::Error(err) => {
                                println!("{err}");
                                Command::none()
                            }
                        }
                    }
                }
            },
            Message::Navigate(menu) => {
                self.previous_menu = self.current_menu;
                self.current_menu = menu;
                Command::none()
            }
            Message::SetTheme(theme) => {
                self.settings.theme = theme;
                Command::none()
            },
        }
    }
    fn view(&self) -> Element<Message> {
        // TODO: add collapsible sidebar for navigation

        let menu_content = match self.current_menu {
            Menu::Settings => {
                let theme_options = vec![AppTheme::Light, AppTheme::Dark, AppTheme::System];
                let theme_selector = pick_list(theme_options, Some(self.settings.theme.clone()), Message::SetTheme);

                column(vec![
                    theme_selector.into(),
                    button("Close").on_press(Message::Navigate(self.previous_menu)).into()
                ])
            },
            Menu::JNovel => {
                let jnc_login_button: Element<_, _> = match &self.jnc.login_state {
                    jnovel::LoginState::Success(_) => {
                        let mut col = Column::new();
                        col = col.push(button("Logout").on_press(Message::JncAction(jnovel::Message::Logout)));

                        for (id, entry) in &self.jnc.library {
                            col = col.push(button(entry.info.title.as_str()).on_press(Message::JncAction(jnovel::Message::LoginState(jnovel::LoginState::LoggedOut))));
                        }

                        Element::from(col)},
                    jnovel::LoginState::LoggedOut => Element::from(button("Login").on_press(Message::JncAction(jnovel::Message::Login))),
                    jnovel::LoginState::LoggingIn => Element::from(text("Logging in")),
                    jnovel::LoginState::AwaitingConfirmation(code, start) => Element::from(text(format!("Login Code: {}. Expiring: {}s", code.otp, 600 - (Local::now() - start).num_seconds()))),
                    jnovel::LoginState::Error(err) => Element::from(row![
                            button("Error").on_press(Message::JncAction(jnovel::Message::LoginState(jnovel::LoginState::LoggedOut))),
                            text(err),
                        ]),
                };

                column(vec![Element::from(
                        row(vec![
                            jnc_login_button.into()
                        ])
                )])
            },
            Menu::Calibre => {
                column(vec![
                    text("Not Implemented").into()
                ])
            }
        }
            .width(Length::Fill)
            .padding(Padding::new(10.0));

        let mut menu = Column::new();

        menu = menu
            .push(button("Settings").on_press(Message::Navigate(Menu::Settings)))
            .push(button("JNC").on_press(Message::Navigate(Menu::JNovel)))
            .push(button("Calibre").on_press(Message::Navigate(Menu::Calibre)))
            .width(Length::Shrink)
            .height(Length::Fill)
            .padding(Padding::new(10.0))
            .spacing(Pixels(10.0));

        let app_content: Row<Message> = row(vec![menu.into(), menu_content.into()])
            .width(Length::Fill)
            .spacing(Pixels(10.0));

        container(app_content)
            .width(Length::Fill)
            .height(Length::Fill)
            .center_x()
            .center_y()
            .into()
    }

    fn theme(&self) -> Self::Theme {
        self.settings.theme.get_iced_theme()
    }

    fn subscription(&self) -> Subscription<Self::Message> {
        let system_theme = match self.settings.theme == AppTheme::System {
            true => time::every(time::Duration::from_secs(1))
                .map(|_| Message::SetTheme(AppTheme::System)),
            false => Subscription::none()
        };

        let jnc_otp_check = match self.jnc.login_state.clone() {
            jnovel::LoginState::AwaitingConfirmation(otp, start) => {
                subscription::unfold(
                    "otp_login",
                    jnovel::OtpState::Starting(otp, start),
                    |state| async move {
                        jnovel::Otp::check(state).await
                    }
                )
                    .map(|state| Message::JncAction(jnovel::Message::LoginState(state)))
            },
            _ => Subscription::none(),
        };

        let library_loading = match self.jnc.login_state {
            jnovel::LoginState::Success(token) => {
                match self.jnc.library_state {
                    jnovel::LibraryState::Unloaded => {
                        jnovel::State::load_library(token.clone())
                            .map(|status| Message::JncAction(jnovel::Message::LibraryState(status)))
                    },
                    jnovel::LibraryState::Loading(progress) => {
                        jnovel::State::load_library(token.clone())
                            .map(|status| Message::JncAction(jnovel::Message::LibraryState(status)))
                    },
                    _ => Subscription::none()
                }
            },
            _ => Subscription::none()
        };

        Subscription::batch([
            system_theme,
            jnc_otp_check,
            library_loading
        ])
    }
}

fn system_theme_mode() -> AppTheme {
    match dark_light::detect() {
        dark_light::Mode::Light | dark_light::Mode::Default => AppTheme::Light,
        dark_light::Mode::Dark => AppTheme::Dark,
    }
}


#[tokio::main]
async fn main() -> iced::Result {
    CalibreWebImporter::run(Settings::default())
}