"""Test untuk Pencatat Keuangan Pribadi."""

import sqlite3

import pytest

import backup
import db
import laporan


@pytest.fixture
def path_db(tmp_path):
    """Siapkan database bersih dengan skema sudah dibuat."""
    path = tmp_path / "keuangan.db"
    db.init_db(path)
    return path


def isi_contoh(path):
    """Isi database dengan transaksi tetap dari dua bulan."""
    data = [
        ("2025-06-01", "masuk", "Gaji", 5500000, "Gaji bulanan"),
        ("2025-06-03", "keluar", "Makan", 45000, "Makan siang"),
        ("2025-06-05", "keluar", "Transportasi", 25000, "Ojek ke kantor"),
        ("2025-06-20", "keluar", "Makan", 38000, "Makan malam"),
        ("2025-07-01", "masuk", "Gaji", 5500000, "Gaji bulanan"),
        ("2025-07-02", "keluar", "Hiburan", 50000, "Tiket bioskop"),
    ]
    for baris in data:
        db.insert_transaksi(*baris, path=path)


def test_init_db_bisa_dipanggil_berulang(tmp_path):
    path = tmp_path / "keuangan.db"
    db.init_db(path)
    db.init_db(path)

    con = db.get_connection(path)
    jumlah_tabel = con.execute(
        "SELECT COUNT(*) FROM sqlite_master WHERE name = 'transaksi'"
    ).fetchone()[0]
    con.close()
    assert jumlah_tabel == 1


def test_insert_dan_baca_bolak_balik(path_db):
    id_baru = db.insert_transaksi(
        "2025-06-01", "masuk", "Gaji", 5500000, "Gaji bulanan", path=path_db
    )

    hasil = db.transaksi_terakhir(path=path_db)
    assert len(hasil) == 1
    baris = hasil[0]
    assert baris["id"] == id_baru
    assert baris["tanggal"] == "2025-06-01"
    assert baris["jenis"] == "masuk"
    assert baris["kategori"] == "Gaji"
    assert baris["jumlah"] == 5500000
    assert baris["catatan"] == "Gaji bulanan"


def test_transaksi_terakhir_urutan_dan_batas(path_db):
    isi_contoh(path_db)

    hasil = db.transaksi_terakhir(batas=4, path=path_db)

    assert [t["id"] for t in hasil] == [6, 5, 4, 3]


def test_jumlah_harus_positif(path_db):
    for jumlah_buruk in (0, -5000):
        with pytest.raises(sqlite3.IntegrityError):
            db.insert_transaksi("2025-06-01", "masuk", "Gaji", jumlah_buruk, path=path_db)


def test_jenis_harus_masuk_atau_keluar(path_db):
    with pytest.raises(sqlite3.IntegrityError):
        db.insert_transaksi("2025-06-01", "transfer", "Lain-lain", 10000, path=path_db)


def test_tanggal_harus_format_iso(path_db):
    with pytest.raises(sqlite3.IntegrityError):
        db.insert_transaksi("15-06-2025", "masuk", "Gaji", 5500000, path=path_db)


def test_ringkasan_bulanan(path_db):
    isi_contoh(path_db)

    ringkasan = laporan.ringkasan_bulanan("2025-06", path=path_db)

    assert ringkasan["total_masuk"] == 5500000
    assert ringkasan["total_keluar"] == 108000
    assert ringkasan["saldo"] == 5392000


def test_ringkasan_bulan_tanpa_transaksi(path_db):
    ringkasan = laporan.ringkasan_bulanan("2025-12", path=path_db)

    assert ringkasan["total_masuk"] == 0
    assert ringkasan["total_keluar"] == 0
    assert ringkasan["saldo"] == 0


def test_kategori_terbesar(path_db):
    isi_contoh(path_db)

    hasil = laporan.kategori_terbesar("2025-06", path=path_db)
    assert [(r["kategori"], r["jumlah_transaksi"], r["total"]) for r in hasil] == [
        ("Makan", 2, 83000),
        ("Transportasi", 1, 25000),
    ]

    satu = laporan.kategori_terbesar("2025-06", batas=1, path=path_db)
    assert [r["kategori"] for r in satu] == ["Makan"]


def test_backup_dan_restore(tmp_path):
    path_db = tmp_path / "keuangan.db"
    db.init_db(path_db)
    isi_contoh(path_db)
    path_backup = tmp_path / "keuangan-salinan.db"

    backup.buat_backup(path_db, path_backup)
    assert backup.verifikasi_integritas(path_backup)

    db.insert_transaksi("2025-07-10", "keluar", "Makan", 30000, path=path_db)
    assert len(db.transaksi_terakhir(path=path_db)) == 7

    backup.restore_backup(path_backup, path_db)
    assert len(db.transaksi_terakhir(path=path_db)) == 6
    assert backup.verifikasi_integritas(path_db)


def test_restore_berkas_rusak(tmp_path):
    path_rusak = tmp_path / "rusak.db"
    path_rusak.write_text("ini bukan berkas database", encoding="utf-8")

    with pytest.raises(sqlite3.DatabaseError):
        backup.restore_backup(path_rusak, tmp_path / "tujuan.db")
