"""Aplikasi Inventaris SQLite - antarmuka pengguna."""

import sqlite3

import database

BATAS_STOK_MENIPIS = 5
JUMLAH_TOP_PRODUK = 5


def escape_like(keyword: str) -> str:
    """Amankan karakter wildcard LIKE (persen dan garis bawah)."""
    return (
        keyword.replace("\\", "\\\\")
        .replace("%", "\\%")
        .replace("_", "\\_")
    )


def jalankan_kueri(sql: str,
                  params: tuple = ()) -> list[sqlite3.Row]:
    """Jalankan statement SELECT dan kembalikan seluruh baris hasilnya."""
    conn = database.koneksi()
    try:
        return conn.execute(sql, params).fetchall()
    finally:
        conn.close()


def cari_produk(keyword: str) -> list[sqlite3.Row]:
    pola = f"%{escape_like(keyword)}%"
    sql = "SELECT * FROM produk WHERE nama LIKE ? ESCAPE '\\' ORDER BY nama"
    return jalankan_kueri(sql, (pola,))


def filter_kategori(kategori: str) -> list[sqlite3.Row]:
    sql = "SELECT * FROM produk WHERE kategori = ? ORDER BY nama"
    return jalankan_kueri(sql, (kategori,))


def laporan_stok_menipis() -> list[sqlite3.Row]:
    sql = "SELECT * FROM produk WHERE stok < ? ORDER BY stok, nama"
    return jalankan_kueri(sql, (BATAS_STOK_MENIPIS,))


def nilai_inventaris_per_kategori() -> list[sqlite3.Row]:
    sql = """
        SELECT kategori,
               COUNT(*) AS jumlah_produk,
               SUM(harga * stok) AS nilai_total
        FROM produk
        GROUP BY kategori
        ORDER BY nilai_total DESC
    """
    return jalankan_kueri(sql)


def top_produk(jumlah: int = JUMLAH_TOP_PRODUK) -> list[sqlite3.Row]:
    sql = """
        SELECT id, nama, kategori, harga, stok,
               harga * stok AS nilai
        FROM produk
        ORDER BY nilai DESC, nama
        LIMIT ?
    """
    return jalankan_kueri(sql, (jumlah,))


def format_rupiah(nilai: int) -> str:
    """Format angka menjadi teks rupiah, contoh 15000 menjadi Rp15.000."""
    return "Rp" + f"{nilai:,}".replace(",", ".")


def cetak_tabel(rows: list) -> None:
    """Cetak daftar produk sebagai tabel rapi."""
    print(f"{'ID':<3} {'Nama':<18} {'Kategori':<10} {'Harga':>10} {'Stok':>4}")
    print(f"{'---':<3} {'-' * 18} {'-' * 10} {'-' * 10} {'-' * 4}")
    for row in rows:
        print(
            f"{row['id']:<3} {row['nama']:<18} {row['kategori']:<10} "
            f"{format_rupiah(row['harga']):>10} {row['stok']:>4}"
        )


def baca_angka(prompt: str) -> int:
    """Minta masukan angka bulat, ulangi sampai masukan sah."""
    while True:
        teks = input(prompt).strip()
        try:
            return int(teks)
        except ValueError:
            print("Masukkan angka bulat, misalnya 15000.")


def menu_tambah() -> None:
    nama = input("Nama produk: ").strip()
    kategori = input("Kategori: ").strip()
    harga = baca_angka("Harga (Rp): ")
    stok = baca_angka("Stok awal: ")

    try:
        database.validasi_produk(nama, kategori, harga, stok)
    except ValueError as galat:
        print(f"Gagal: {galat}")
        return

    try:
        id_baru = database.tambah_produk(nama, kategori, harga, stok)
    except sqlite3.IntegrityError:
        print(f"Produk dengan nama '{nama}' sudah terdaftar.")
        return

    print(f"Produk '{nama}' tersimpan dengan id {id_baru}.")


def menu_lihat() -> None:
    rows = database.lihat_produk()
    if not rows:
        print("Belum ada produk.")
        return
    cetak_tabel(rows)


def menu_cari() -> None:
    keyword = input("Kata kunci nama: ").strip()
    if not keyword:
        print("Kata kunci tidak boleh kosong.")
        return
    hasil = cari_produk(keyword)
    if not hasil:
        print(f"Tidak ada produk yang mengandung '{keyword}'.")
        return
    print(f"Hasil pencarian untuk '{keyword}':")
    cetak_tabel(hasil)


def menu_filter() -> None:
    kategori = input("Kategori: ").strip()
    if not kategori:
        print("Kategori tidak boleh kosong.")
        return
    hasil = filter_kategori(kategori)
    if not hasil:
        print(f"Tidak ada produk pada kategori '{kategori}'.")
        return
    print(f"Produk pada kategori '{kategori}':")
    cetak_tabel(hasil)


def menu_ubah_harga() -> None:
    produk_id = baca_angka("ID produk: ")
    harga_baru = baca_angka("Harga baru (Rp): ")
    if database.perbarui_harga(produk_id, harga_baru):
        print("Harga berhasil diperbarui.")
    else:
        print(f"Tidak ada produk dengan id {produk_id}.")


def menu_sesuaikan_stok() -> None:
    produk_id = baca_angka("ID produk: ")
    perubahan = baca_angka("Perubahan stok (boleh negatif): ")
    keterangan = input("Keterangan: ").strip() or "penyesuaian manual"

    try:
        database.sesuaikan_stok(produk_id, perubahan, keterangan)
    except ValueError as galat:
        print(f"Gagal: {galat}")
        return
    except sqlite3.IntegrityError:
        print("Gagal: penyesuaian membuat stok negatif. Transaksi dibatalkan.")
        return

    print("Stok berhasil disesuaikan.")


def menu_hapus() -> None:
    produk_id = baca_angka("ID produk: ")
    if database.hapus_produk(produk_id):
        print("Produk berhasil dihapus.")
    else:
        print(f"Tidak ada produk dengan id {produk_id}.")


def menu_riwayat() -> None:
    produk_id = baca_angka("ID produk: ")
    produk = database.ambil_produk(produk_id)
    if produk is None:
        print(f"Tidak ada produk dengan id {produk_id}.")
        return

    rows = database.lihat_riwayat(produk_id)
    if not rows:
        print(f"Belum ada riwayat untuk '{produk['nama']}'.")
        return

    print(f"Riwayat untuk '{produk['nama']}':")
    print(f"{'Waktu':<21} {'Perubahan':>9}  Keterangan")
    for row in rows:
        print(f"{row['waktu']:<21} {row['perubahan']:>+9}  {row['keterangan']}")


def menu_stok_menipis() -> None:
    hasil = laporan_stok_menipis()
    if not hasil:
        print(f"Tidak ada produk dengan stok di bawah {BATAS_STOK_MENIPIS}.")
        return
    print(f"Produk dengan stok di bawah {BATAS_STOK_MENIPIS}:")
    print(f"{'ID':<3} {'Nama':<18} {'Kategori':<10} {'Stok':>4}")
    print(f"{'---':<3} {'-' * 18} {'-' * 10} {'-' * 4}")
    for row in hasil:
        print(f"{row['id']:<3} {row['nama']:<18} {row['kategori']:<10} {row['stok']:>4}")


def menu_nilai_inventaris() -> None:
    hasil = nilai_inventaris_per_kategori()
    if not hasil:
        print("Belum ada produk untuk dihitung.")
        return
    print(f"{'Kategori':<12} {'Jumlah':>7} {'Nilai Total':>13}")
    print(f"{'-' * 12} {'-' * 7} {'-' * 13}")
    total_nilai = 0
    total_produk = 0
    for row in hasil:
        print(
            f"{row['kategori']:<12} {row['jumlah_produk']:>7} "
            f"{format_rupiah(row['nilai_total']):>13}"
        )
        total_nilai += row["nilai_total"]
        total_produk += row["jumlah_produk"]
    print(f"{'-' * 12} {'-' * 7} {'-' * 13}")
    print(f"{'TOTAL':<12} {total_produk:>7} {format_rupiah(total_nilai):>13}")


def menu_top_produk() -> None:
    hasil = top_produk()
    if not hasil:
        print("Belum ada produk untuk dihitung.")
        return
    print(f"Top {JUMLAH_TOP_PRODUK} produk berdasarkan nilai stok:")
    print(f"{'#':<3} {'Nama':<18} {'Harga':>10} {'Stok':>4} {'Nilai':>12}")
    print(f"{'---':<3} {'-' * 18} {'-' * 10} {'-' * 4} {'-' * 12}")
    for peringkat, row in enumerate(hasil, start=1):
        print(
            f"{peringkat:<3} {row['nama']:<18} "
            f"{format_rupiah(row['harga']):>10} {row['stok']:>4} "
            f"{format_rupiah(row['nilai']):>12}"
        )


def tampilkan_menu() -> None:
    print()
    print("=== Aplikasi Inventaris ===")
    print("1. Tambah produk")
    print("2. Lihat semua produk")
    print("3. Ubah harga")
    print("4. Sesuaikan stok (+/-)")
    print("5. Hapus produk")
    print("6. Riwayat produk")
    print("7. Cari produk")
    print("8. Filter per kategori")
    print("9. Laporan stok menipis")
    print("10. Nilai inventaris per kategori")
    print("11. Top produk")
    print("0. Keluar")


def main() -> None:
    database.inisialisasi()
    print("Database siap. Selamat datang!")

    while True:
        tampilkan_menu()
        try:
            pilihan = input("Pilih menu: ").strip()
        except EOFError:
            print()
            break

        if pilihan == "0":
            print("Sampai jumpa!")
            break

        match pilihan:
            case "1":
                menu_tambah()
            case "2":
                menu_lihat()
            case "3":
                menu_ubah_harga()
            case "4":
                menu_sesuaikan_stok()
            case "5":
                menu_hapus()
            case "6":
                menu_riwayat()
            case "7":
                menu_cari()
            case "8":
                menu_filter()
            case "9":
                menu_stok_menipis()
            case "10":
                menu_nilai_inventaris()
            case "11":
                menu_top_produk()
            case _:
                print("Pilihan tidak dikenal.")


if __name__ == "__main__":
    main()
