"""filetool: CLI pengelola file sederhana."""

import argparse
import shutil
import sys
from datetime import datetime
from pathlib import Path

VERSI = "2.0.0"


def format_size(ukuran: int) -> str:
    """Ubah ukuran dalam byte menjadi teks yang mudah dibaca manusia."""
    if ukuran < 1024:
        return f"{ukuran} B"
    for satuan in ("KB", "MB", "GB", "TB"):
        ukuran /= 1024
        if ukuran < 1024:
            return f"{ukuran:.1f} {satuan}".replace(".", ",")
    return f"{ukuran:.1f} PB".replace(".", ",")


def format_waktu(epoch: float) -> str:
    """Ubah stempel waktu epoch menjadi teks tanggal dan jam."""
    return datetime.fromtimestamp(epoch).strftime("%Y-%m-%d %H:%M")


def perintah_daftar(args: argparse.Namespace) -> int:
    direktori = Path(args.path)
    if not direktori.is_dir():
        print(f"filetool: bukan direktori: {direktori}", file=sys.stderr)
        return 1
    for entri in sorted(direktori.iterdir()):
        penanda = "d" if entri.is_dir() else "-"
        if args.panjang:
            stat = entri.stat()
            ukuran = "-" if entri.is_dir() else format_size(stat.st_size)
            print(f"{penanda} {ukuran:>8}  {format_waktu(stat.st_mtime)}  {entri.name}")
        else:
            akhiran = "/" if entri.is_dir() else ""
            print(f"{entri.name}{akhiran}")
    return 0


def perintah_info(args: argparse.Namespace) -> int:
    berkas = Path(args.path)
    if not berkas.is_file():
        print(f"filetool: bukan berkas: {berkas}", file=sys.stderr)
        return 1
    stat = berkas.stat()
    for label, nilai in [
        ("jalur", str(berkas)),
        ("jenis", "berkas"),
        ("ekstensi", berkas.suffix or "(tidak ada)"),
        ("ukuran", f"{stat.st_size} byte ({format_size(stat.st_size)})"),
        ("diubah", format_waktu(stat.st_mtime)),
    ]:
        print(f"{label:<9}: {nilai}")
    return 0


def perintah_cari(args: argparse.Namespace) -> int:
    akar = Path(args.path)
    if not akar.is_dir():
        print(f"filetool: bukan direktori: {akar}", file=sys.stderr)
        return 1
    hasil = sorted(p for p in akar.rglob(args.pola) if p.is_file())
    if not hasil:
        print("tidak ada yang cocok.")
        return 0
    for jalur in hasil:
        print(jalur.relative_to(akar))
    return 0


def perintah_salin(args: argparse.Namespace) -> int:
    sumber = Path(args.sumber)
    tujuan = Path(args.tujuan)
    if not sumber.is_file():
        print(f"filetool: sumber bukan berkas: {sumber}", file=sys.stderr)
        return 1
    if tujuan.is_dir():
        tujuan = tujuan / sumber.name
    if tujuan.exists():
        print(f"filetool: tujuan sudah ada: {tujuan}", file=sys.stderr)
        return 1
    shutil.copy2(sumber, tujuan)
    print(f"tersalin: {sumber} -> {tujuan}")
    return 0


def perintah_pindahkan(args: argparse.Namespace) -> int:
    sumber = Path(args.sumber)
    tujuan = Path(args.tujuan)
    if not sumber.exists():
        print(f"filetool: sumber tidak ditemukan: {sumber}", file=sys.stderr)
        return 1
    if tujuan.is_dir():
        tujuan = tujuan / sumber.name
    if tujuan.exists():
        print(f"filetool: tujuan sudah ada: {tujuan}", file=sys.stderr)
        return 1
    shutil.move(sumber, tujuan)
    print(f"dipindahkan: {sumber} -> {tujuan}")
    return 0


def perintah_hapus(args: argparse.Namespace) -> int:
    target = Path(args.path)
    if not target.is_file():
        print(f"filetool: bukan berkas: {target}", file=sys.stderr)
        return 1
    if not args.ya:
        print(f"menolak menghapus tanpa --ya: {target}")
        print("jalankan ulang dengan --ya untuk benar-benar menghapus.")
        return 1
    target.unlink()
    print(f"terhapus: {target}")
    return 0


def perintah_ukuran(args: argparse.Namespace) -> int:
    akar = Path(args.path)
    if not akar.is_dir():
        print(f"filetool: bukan direktori: {akar}", file=sys.stderr)
        return 1
    total = 0
    jumlah_berkas = 0
    jumlah_direktori = 0
    for jalur in akar.rglob("*"):
        if jalur.is_dir():
            jumlah_direktori += 1
        else:
            total += jalur.stat().st_size
            jumlah_berkas += 1
    mentah = f"{total:,}".replace(",", ".")
    print(f"{'total':<9}: {mentah} byte ({format_size(total)})")
    print(f"{'berkas':<9}: {jumlah_berkas}")
    print(f"{'direktori':<9}: {jumlah_direktori}")
    return 0


def susun_rencana(akar: Path) -> list[tuple[Path, Path]]:
    """Rancang pemindahan setiap berkas ke folder sesuai ekstensinya."""
    rencana = []
    for berkas in sorted(akar.iterdir()):
        if not berkas.is_file():
            continue
        folder = berkas.suffix.lstrip(".") or "tanpa-ekstensi"
        rencana.append((berkas, akar / folder / berkas.name))
    return rencana


def perintah_organisasi(args: argparse.Namespace) -> int:
    akar = Path(args.path)
    if not akar.is_dir():
        print(f"filetool: bukan direktori: {akar}", file=sys.stderr)
        return 1
    rencana = susun_rencana(akar)
    if not rencana:
        print("tidak ada berkas yang perlu diorganisasi.")
        return 0
    for sumber, tujuan in rencana:
        print(f"{sumber} -> {tujuan}")
    if args.dry_run:
        print(f"(dry-run: {len(rencana)} berkas akan dipindahkan)")
        return 0
    for sumber, tujuan in rencana:
        tujuan.parent.mkdir(parents=True, exist_ok=True)
        shutil.move(sumber, tujuan)
    print(f"{len(rencana)} berkas dipindahkan.")
    return 0


def buat_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="filetool",
        description="Pengelola file dari terminal.",
    )
    parser.add_argument("--versi", action="version", version=f"filetool {VERSI}")
    sub = parser.add_subparsers(dest="perintah", required=True, metavar="PERINTAH")

    p_daftar = sub.add_parser("daftar", help="tampilkan isi sebuah direktori")
    p_daftar.add_argument("path", help="direktori yang ingin ditampilkan")
    p_daftar.add_argument("--panjang", action="store_true",
                          help="tampilkan ukuran, waktu ubah, dan nama")
    p_daftar.set_defaults(func=perintah_daftar)

    p_info = sub.add_parser("info", help="tampilkan metadata sebuah berkas")
    p_info.add_argument("path", help="berkas yang ingin diperiksa")
    p_info.set_defaults(func=perintah_info)

    p_cari = sub.add_parser("cari", help="cari berkas berdasarkan pola glob")
    p_cari.add_argument("path", help="direktori awal pencarian")
    p_cari.add_argument("--pola", default="*", help='pola nama berkas, misalnya "*.txt"')
    p_cari.set_defaults(func=perintah_cari)

    p_salin = sub.add_parser("salin", help="salin sebuah berkas")
    p_salin.add_argument("sumber", help="berkas sumber")
    p_salin.add_argument("tujuan", help="berkas atau direktori tujuan")
    p_salin.set_defaults(func=perintah_salin)

    p_pindahkan = sub.add_parser("pindahkan", help="pindahkan berkas atau direktori")
    p_pindahkan.add_argument("sumber", help="berkas atau direktori sumber")
    p_pindahkan.add_argument("tujuan", help="berkas atau direktori tujuan")
    p_pindahkan.set_defaults(func=perintah_pindahkan)

    p_hapus = sub.add_parser("hapus", help="hapus sebuah berkas")
    p_hapus.add_argument("path", help="berkas yang akan dihapus")
    p_hapus.add_argument("--ya", action="store_true",
                         help="konfirmasi bahwa pengguna yakin ingin menghapus")
    p_hapus.set_defaults(func=perintah_hapus)

    p_ukuran = sub.add_parser("ukuran", help="hitung total ukuran secara rekursif")
    p_ukuran.add_argument("path", help="direktori yang ingin diukur")
    p_ukuran.set_defaults(func=perintah_ukuran)

    p_organisasi = sub.add_parser("organisasi", help="urutkan berkas ke folder per ekstensi")
    p_organisasi.add_argument("path", help="direktori yang akan diorganisasi")
    p_organisasi.add_argument("--dry-run", action="store_true",
                              help="tampilkan rencana tanpa memindahkan apa pun")
    p_organisasi.set_defaults(func=perintah_organisasi)

    return parser


def main(argv: list[str] | None = None) -> int:
    args = buat_parser().parse_args(argv)
    return args.func(args)


if __name__ == "__main__":
    sys.exit(main())
