/* tests.c - Unit test untuk modul keuangan */
#include <stdio.h>
#include <string.h>

#include "finance.h"
#include "storage.h"
#include "report.h"

static int tests_run = 0;
static int tests_pass = 0;

#define ASSERT(cond, msg) \
    do { \
        tests_run++; \
        if (cond) { \
            tests_pass++; \
        } else { \
            printf("FAIL: %s (line %d)\n", msg, __LINE__); \
        } \
    } while (0)

static void test_transaction_create(void) {
    Transaction t;
    int rc = transaction_create(&t, 1, TXN_INCOME, 1000000.0,
                                "Gaji bulanan", "Gaji", "2024-03-01");
    ASSERT(rc == 0, "transaction_create valid harus sukses");
    ASSERT(t.id == 1, "id harus 1");
    ASSERT(t.type == TXN_INCOME, "type harus INCOME");
    ASSERT(transaction_signed_value(&t) == 1000000.0, "signed = +1M");

    Transaction t2;
    rc = transaction_create(&t2, 2, TXN_EXPENSE, 25000.0,
                            "Makan siang", "Makan", "2024-03-02");
    ASSERT(rc == 0, "transaction_create expense sukses");
    ASSERT(transaction_signed_value(&t2) == -25000.0, "signed = -25K");
}

static void test_transaction_invalid(void) {
    Transaction t;
    ASSERT(transaction_create(&t, 0, TXN_INCOME, 100, "X", "C", "2024-01-01") != 0,
           "id=0 ditolak");
    ASSERT(transaction_create(&t, 1, TXN_INCOME, -1, "X", "C", "2024-01-01") != 0,
           "amount negatif ditolak");
    ASSERT(transaction_create(&t, 1, TXN_INCOME, 100, "X", "C", "2024/01/01") != 0,
           "format tanggal salah ditolak");
    ASSERT(transaction_create(&t, 1, TXN_INCOME, 100, "X", "C", "2024-13-01") != 0,
           "bulan 13 ditolak");
    ASSERT(transaction_create(&t, 1, TXN_INCOME, 100, "X", "C", "2024-01-32") != 0,
           "hari 32 ditolak");
}

static void test_finance_add_summary(void) {
    Finance f;
    finance_init(&f);
    int id1 = finance_add(&f, TXN_INCOME, 5000000.0, "Gaji", "Gaji", "2024-03-01");
    int id2 = finance_add(&f, TXN_EXPENSE, 50000.0, "Bensin", "Transport", "2024-03-02");
    int id3 = finance_add(&f, TXN_EXPENSE, 30000.0, "Makan", "Makan", "2024-03-02");
    ASSERT(id1 == 1 && id2 == 2 && id3 == 3, "id berurutan");
    ASSERT(f.count == 3, "count harus 3");

    double inc, exp, bal;
    finance_summary(&f, &inc, &exp, &bal);
    ASSERT(inc == 5000000.0, "total income 5M");
    ASSERT(exp == 80000.0, "total expense 80K");
    ASSERT(bal == 4920000.0, "balance 4.92M");
}

static void test_finance_filter_category(void) {
    Finance f;
    finance_init(&f);
    finance_add(&f, TXN_EXPENSE, 10000.0, "Kopi", "Makan", "2024-03-01");
    finance_add(&f, TXN_EXPENSE, 20000.0, "Bakso", "Makan", "2024-03-01");
    finance_add(&f, TXN_EXPENSE, 50000.0, "Bensin", "Transport", "2024-03-02");

    Transaction out[16];
    int n = finance_filter_by_category(&f, "makan", out, 16);
    ASSERT(n == 2, "filter 'makan' case-insensitive = 2");

    n = finance_filter_by_category(&f, "TidakAda", out, 16);
    ASSERT(n == 0, "kategori tidak ada = 0");
}

static void test_finance_filter_date(void) {
    Finance f;
    finance_init(&f);
    finance_add(&f, TXN_EXPENSE, 10000.0, "A", "X", "2024-03-01");
    finance_add(&f, TXN_EXPENSE, 20000.0, "B", "X", "2024-03-02");
    finance_add(&f, TXN_EXPENSE, 30000.0, "C", "X", "2024-03-05");
    finance_add(&f, TXN_EXPENSE, 40000.0, "D", "X", "2024-03-10");

    Transaction out[16];
    int n = finance_filter_by_date(&f, "2024-03-02", "2024-03-05", out, 16);
    ASSERT(n == 2, "rentang tanggal 02-05 = 2 transaksi");

    n = finance_filter_by_date(&f, NULL, "2024-03-02", out, 16);
    ASSERT(n == 2, "hanya sampai 02 = 2");

    n = finance_filter_by_date(&f, "2024-03-06", NULL, out, 16);
    ASSERT(n == 1, "hanya dari 06 = 1");
}

static void test_storage_roundtrip(void) {
    Finance f;
    finance_init(&f);
    finance_add(&f, TXN_INCOME, 1000000.0, "Gaji", "Gaji", "2024-03-01");
    finance_add(&f, TXN_EXPENSE, 50000.0, "Bensin", "Transport", "2024-03-02");

    const char *path = "/tmp/test_finance.csv";
    int written = storage_save(&f, path);
    ASSERT(written == 2, "harus menulis 2 baris");

    Finance loaded;
    int read = storage_load(&loaded, path);
    ASSERT(read == 2, "harus membaca 2 baris");
    ASSERT(loaded.count == 2, "count=2 setelah load");
    ASSERT(loaded.next_id == 3, "next_id=3 setelah load");
    ASSERT(strcmp(loaded.items[0].description, "Gaji") == 0,
           "deskripsi[0] = Gaji");
}

static void test_report_category(void) {
    Finance f;
    finance_init(&f);
    finance_add(&f, TXN_INCOME, 1000000.0, "Gaji", "Gaji", "2024-03-01");
    finance_add(&f, TXN_EXPENSE, 50000.0, "Bensin", "Transport", "2024-03-02");
    finance_add(&f, TXN_EXPENSE, 30000.0, "Makan", "Makan", "2024-03-02");
    finance_add(&f, TXN_EXPENSE, 20000.0, "Cafe", "Makan", "2024-03-03");

    CategoryReport r[16];
    int n = report_by_category(&f, r, 16);
    ASSERT(n == 3, "harus ada 3 kategori unik");

    int found_gaji = 0, found_makan = 0, found_transport = 0;
    for (int i = 0; i < n; i++) {
        if (strcmp(r[i].category, "Gaji") == 0) {
            found_gaji = 1;
            ASSERT(r[i].income == 1000000.0, "Gaji income 1M");
            ASSERT(r[i].count == 1, "Gaji count 1");
        }
        if (strcmp(r[i].category, "Makan") == 0) {
            found_makan = 1;
            ASSERT(r[i].expense == 50000.0, "Makan expense 50K");
            ASSERT(r[i].count == 2, "Makan count 2");
        }
        if (strcmp(r[i].category, "Transport") == 0) {
            found_transport = 1;
        }
    }
    ASSERT(found_gaji && found_makan && found_transport,
           "ketiga kategori harus ada");
}

int main(void) {
    test_transaction_create();
    test_transaction_invalid();
    test_finance_add_summary();
    test_finance_filter_category();
    test_finance_filter_date();
    test_storage_roundtrip();
    test_report_category();

    printf("\n=== Hasil Test ===\n");
    printf("Total : %d\n", tests_run);
    printf("Lulus : %d\n", tests_pass);
    printf("Gagal : %d\n", tests_run - tests_pass);
    return (tests_run == tests_pass) ? 0 : 1;
}
