use anyhow::{Context, Result};
use rustledger_core::{Directive, InternedStr, NaiveDate};
use rustledger_loader::Loader;
use std::collections::BTreeSet;
use std::io::Write;
use std::path::PathBuf;

pub(super) fn cmd_stats<W: Write>(file: &PathBuf, writer: &mut W) -> Result<()> {
    let mut loader = Loader::new();
    let load_result = loader
        .load(file)
        .with_context(|| format!("failed to load {}", file.display()))?;

    let mut transactions = 0;
    let mut postings = 0;
    let mut accounts = 0;
    let mut commodities_set: BTreeSet<InternedStr> = BTreeSet::new();
    let mut balance_assertions = 0;
    let mut prices = 0;
    let mut first_date: Option<NaiveDate> = None;
    let mut last_date: Option<NaiveDate> = None;

    for spanned in &load_result.directives {
        match &spanned.value {
            Directive::Transaction(txn) => {
                transactions += 1;
                postings += txn.postings.len();
                for posting in &txn.postings {
                    if let Some(amount) = posting.amount() {
                        commodities_set.insert(amount.currency.clone());
                    }
                }
                if first_date.is_none() || Some(txn.date) < first_date {
                    first_date = Some(txn.date);
                }
                if last_date.is_none() || Some(txn.date) > last_date {
                    last_date = Some(txn.date);
                }
            }
            Directive::Open(_) => accounts += 1,
            Directive::Balance(bal) => {
                balance_assertions += 1;
                commodities_set.insert(bal.amount.currency.clone());
            }
            Directive::Commodity(comm) => {
                commodities_set.insert(comm.currency.clone());
            }
            Directive::Price(price) => {
                prices += 1;
                commodities_set.insert(price.currency.clone());
                commodities_set.insert(price.amount.currency.clone());
            }
            _ => {}
        }
    }

    writeln!(writer, "Ledger Statistics for {}", file.display())?;
    writeln!(writer, "{}", "=".repeat(60))?;
    writeln!(writer)?;

    if let (Some(first), Some(last)) = (first_date, last_date) {
        writeln!(writer, "Date range: {first} to {last}")?;
        writeln!(writer)?;
    }

    writeln!(
        writer,
        "Directives:       {:>8}",
        load_result.directives.len()
    )?;
    writeln!(writer, "  Transactions:   {transactions:>8}")?;
    writeln!(writer, "  Postings:       {postings:>8}")?;
    writeln!(writer, "  Accounts:       {accounts:>8}")?;
    writeln!(writer, "  Commodities:    {:>8}", commodities_set.len())?;
    writeln!(writer, "  Balances:       {balance_assertions:>8}")?;
    writeln!(writer, "  Prices:         {prices:>8}")?;
    writeln!(writer)?;
    writeln!(writer, "Parse errors:     {:>8}", load_result.errors.len())?;

    Ok(())
}
