use chrono::{Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone};
use sqlite::Connection;

use crate::{data_layer, entities::*, enums::*};

pub struct App {
    pub current_screen: CurrentScreen,
    pub current_widget: CurrentWidget,
    pub acc_list: AccountList,
    pub trx_table: TrxTable,
    pub new_account: Account,
    pub new_transaction: Transaction,
    pub selected_transaction_input: TransactionInput,
    pub selected_account_input: AccountInput,
    pub current_input: String,
    pub tr_types: Vec<TransactionType>,
    pub selected_tab: usize,
    pub tabs: Vec<String>,
    pub error: String,
    pub error_showing: bool,
    pub selected_tr_type_chart: i64,
    pub selected_tr_type_chart_desc: String,
    pub connection: Connection,
}

impl App {
    pub fn new(path: &str) -> App {
        let con = match Connection::open(path) {
            Ok(con) => con,
            Err(e) => {
                eprintln!("Error opening database: {}", e);
                panic!("stopping");
            }
        };

        let mut error = String::new();
        data_layer::setup(&con);
        let tr_types = match data_layer::get_transaction_types(&con) {
            Ok(tt) => tt,
            Err(e) => {
                error = e.to_string();
                Vec::new()
            }
        };

        return App {
            current_screen: CurrentScreen::Main,
            current_widget: CurrentWidget::AccountList,
            acc_list: AccountList::new(),
            trx_table: TrxTable::new(),
            new_account: Account::new_empty(),
            new_transaction: Transaction::new_empty(),
            selected_transaction_input: TransactionInput::Type,
            selected_account_input: AccountInput::Asset,
            current_input: String::new(),
            tr_types: tr_types.clone(),
            selected_tab: 0,
            tabs: [
                "Transactions".to_string(),
                "Amnt by Type".to_string(),
                "Total".to_string(),
                "Chart 3".to_string(),
            ]
            .to_vec(),
            error: if error.is_empty() {
                String::new()
            } else {
                error
            },
            error_showing: false,
            selected_tr_type_chart: 1,
            selected_tr_type_chart_desc: tr_types
                .iter()
                .filter(|x| x.get_id() == 1)
                .next()
                .unwrap_or(&TransactionType::new(0, String::new()))
                .get_description(),
            connection: con,
        };
    }

    pub fn get_list_accounts(&mut self) -> Vec<Account> {
        return match self.acc_list.get_accounts(&self.connection) {
            Ok(a) => a,
            Err(e) => {
                self.error = e.to_string();
                return Vec::new();
            }
        };
    }

    pub fn next_ac(&mut self) {
        self.acc_list.state.select_next();
    }
    pub fn previous_ac(&mut self) {
        self.acc_list.state.select_previous();
    }

    pub fn get_list_trx(&mut self) -> Vec<Transaction> {
        return match self
            .trx_table
            .get_trx(self.acc_list.get_selected_id(), &self.connection)
        {
            Ok(t) => t,
            Err(e) => {
                self.error = e.to_string();
                Vec::new()
            }
        };
    }

    pub fn next_tr(&mut self) {
        self.trx_table.state.select_next();
    }
    pub fn previous_tr(&mut self) {
        self.trx_table.state.select_previous();
    }

    pub fn save_new_account(&mut self) {
        let new = self.new_account.get_id() == 0;
        self.next_ac_input();
        let ac = match data_layer::upsert_account(&self.connection, self.new_account.clone()) {
            Ok(a) => a,
            Err(e) => {
                self.error = e.to_string();
                return;
            }
        };
        if new {
            self.acc_list.add_account(ac);
        }
        self.new_account = Account::new_empty();
        self.current_input = String::new();
        self.selected_account_input = AccountInput::Asset;
    }

    pub fn save_new_tr(&mut self) {
        let new = self.new_transaction.get_id() == 0;
        self.next_tr_input();
        let tr =
            match data_layer::upsert_transaction(&self.connection, self.new_transaction.clone()) {
                Ok(tr) => tr,
                Err(e) => {
                    self.error = e.to_string();
                    return;
                }
            };
        if new {
            self.trx_table.add_tr(tr);
        }
        self.new_transaction = Transaction::new_empty();
        self.current_input = String::new();
        self.selected_transaction_input = TransactionInput::Type;
    }

    pub fn next_tr_input(&mut self) {
        self.set_tr_from_input();
        self.selected_transaction_input = match self.selected_transaction_input {
            TransactionInput::Type => TransactionInput::Amount,
            TransactionInput::Amount => TransactionInput::Date,
            TransactionInput::Date => TransactionInput::Description,
            TransactionInput::Description => TransactionInput::Asset,
            TransactionInput::Asset => TransactionInput::Asset,
        };
        self.set_current_input_tr();
    }
    pub fn previous_tr_input(&mut self) {
        self.set_tr_from_input();
        self.selected_transaction_input = match self.selected_transaction_input {
            TransactionInput::Type => TransactionInput::Type,
            TransactionInput::Amount => TransactionInput::Type,
            TransactionInput::Date => TransactionInput::Amount,
            TransactionInput::Description => TransactionInput::Date,
            TransactionInput::Asset => TransactionInput::Description,
        };
        self.set_current_input_tr();
    }
    pub fn set_current_input_tr(&mut self) {
        match self.selected_transaction_input {
            TransactionInput::Type => {
                self.current_input = self.new_transaction.get_type().get_id().to_string()
            }
            TransactionInput::Amount => {
                self.current_input = self.new_transaction.get_amount().to_string()
            }
            TransactionInput::Date => {
                self.current_input = self
                    .new_transaction
                    .get_date()
                    .format("%Y-%m-%d")
                    .to_string()
            }
            TransactionInput::Description => self.current_input = self.new_transaction.get_desc(),
            TransactionInput::Asset => {
                self.current_input = self.new_transaction.get_asset_amnt().to_string()
            }
        }
    }
    pub fn set_tr_from_input(&mut self) {
        match self.selected_transaction_input {
            TransactionInput::Type => {
                let type_id = match self.current_input.parse::<i64>() {
                    Ok(t) => t,
                    Err(e) => {
                        self.error = e.to_string();
                        0
                    }
                };
                self.new_transaction.set_type(type_id, &self.connection);
            }
            TransactionInput::Amount => {
                let amnt = match self.current_input.parse::<f64>() {
                    Ok(a) => a,
                    Err(e) => {
                        self.error = e.to_string();
                        0.0
                    }
                };
                self.new_transaction.set_amount(amnt);
            }
            TransactionInput::Date => {
                let date = match NaiveDate::parse_from_str(&self.current_input, "%Y-%m-%d") {
                    Ok(d) => d,
                    Err(e) => {
                        self.error = e.to_string();
                        NaiveDate::default()
                    }
                };
                let time = NaiveTime::from_hms_opt(5, 0, 0).unwrap_or_default(); // to account for my local datetime
                self.new_transaction
                    .set_date(Local.from_utc_datetime(&NaiveDateTime::new(date, time)));
            }
            TransactionInput::Description => {
                self.new_transaction.set_desc(self.current_input.clone());
            }
            TransactionInput::Asset => {
                let ast = match self.current_input.parse::<f64>() {
                    Ok(a) => a,
                    Err(e) => {
                        self.error = e.to_string();
                        0.0
                    }
                };
                self.new_transaction.set_asset_amnt(ast);
            }
        }
    }

    pub fn next_ac_input(&mut self) {
        self.set_ac_from_input();
        self.selected_account_input = match self.selected_account_input {
            AccountInput::Asset => AccountInput::Name,
            AccountInput::Name => AccountInput::Type,
            AccountInput::Type => AccountInput::Type,
        };
        self.set_current_input_ac();
    }
    pub fn previous_ac_input(&mut self) {
        self.set_ac_from_input();
        self.selected_account_input = match self.selected_account_input {
            AccountInput::Asset => AccountInput::Asset,
            AccountInput::Name => AccountInput::Asset,
            AccountInput::Type => AccountInput::Name,
        };
        self.set_current_input_ac();
    }
    pub fn set_current_input_ac(&mut self) {
        match self.selected_account_input {
            AccountInput::Asset => {
                self.current_input = self.new_account.get_asset_price().to_string();
            }
            AccountInput::Name => {
                self.current_input = self.new_account.get_name();
            }
            AccountInput::Type => {
                let ac_type = self.new_account.get_ac_type() as usize;
                self.current_input = ac_type.to_string();
            }
        }
    }
    pub fn set_ac_from_input(&mut self) {
        match self.selected_account_input {
            AccountInput::Asset => {
                let ast = match self.current_input.parse::<f64>() {
                    Ok(a) => a,
                    Err(e) => {
                        self.error = e.to_string();
                        0.0
                    }
                };
                self.new_account.set_asset_price(ast);
            }
            AccountInput::Name => {
                self.new_account.set_name(self.current_input.clone());
            }
            AccountInput::Type => {
                let ac_type = match self.current_input.parse::<usize>() {
                    Ok(a) => a,
                    Err(e) => {
                        self.error = e.to_string();
                        0
                    }
                };
                self.new_account.set_type(AccountType::from_usize(ac_type));
            }
        }
    }

    pub fn next_tab(&mut self) {
        if self.selected_tab < self.tabs.len() {
            self.selected_tab += 1;
        }
    }
    pub fn previous_tab(&mut self) {
        if self.selected_tab > 0 {
            self.selected_tab -= 1;
        }
    }

    pub fn get_cumulative_total(&mut self) -> Vec<(f64, f64)> {
        return match data_layer::get_cumulative_account_sum(
            &self.connection,
            self.acc_list.get_selected_id(),
        ) {
            Ok(d) => d,
            Err(e) => {
                self.error = e.to_string();
                Vec::new()
            }
        };
    }

    pub fn get_trtype_data(&mut self) -> Vec<(f64, f64)> {
        return match data_layer::get_tr_type_price_over_time(
            &self.connection,
            self.acc_list.get_selected_id(),
            self.selected_tr_type_chart,
        ) {
            Ok(d) => d,
            Err(e) => {
                self.error = e.to_string();
                Vec::new()
            }
        };
    }

    pub fn select_tr_type_chart(&mut self, id: i64) {
        self.selected_tr_type_chart = id;
        self.selected_tr_type_chart_desc = self
            .tr_types
            .iter()
            .filter(|x| x.get_id() == id)
            .next()
            .unwrap_or(&TransactionType::new(0, String::new()))
            .get_description();
    }
}