import os
import json
import uuid
from datetime import datetime
import google.auth
from google.cloud import documentai
from google.api_core.client_options import ClientOptions
from pydantic import BaseModel, Field
from typing import List

# Modul untuk memunculkan kotak dialog Windows (Pop-up)
import tkinter as tk
from tkinter import filedialog

import vertexai
from vertexai.generative_models import GenerativeModel, GenerationConfig

# =====================================================================
# 1. PARAMETER AKUN GOOGLE CLOUD & STRUKTUR FOLDER INPUT
# =====================================================================
PROJECT_ID = "invoice-ocr-500700"        
LOCATION = "asia-southeast1"              
PROCESSOR_ID = "7f36972f78cd7ad7"          

# Folder tempat menaruh potongan foto invoice panjang (page1.jpg, page2.jpg, dst)
FOLDER_INPUT_INVOICE = "invoice_masuk"

# =====================================================================
# 2. SKEMA PYDANTIC: STRUKTUR MENYESUAIKAN TABEL MYSQL ANDA
# =====================================================================
class HeaderPembelian(BaseModel):
    NoNota: str = Field(default="KOSONG", description="Nomor nota utama. Jika ada beberapa header, ambil nomor nota yang konsisten di halaman pertama.")
    TglTx: str = Field(default="KOSONG", description="Tanggal transaksi dalam format YYYY-MM-DD.")
    Overdue: str = Field(default="KOSONG", description="Tanggal jatuh tempo pembayaran dalam format YYYY-MM-DD.")
    Total: float = Field(default=0.0, description="Total kotor/subtotal transaksi.")
    Diskon: float = Field(default=0.0, description="Persentase diskon utama.")
    Net: float = Field(default=0.0, description="Nilai bersih setelah diskon.")
    PPn: float = Field(default=0.0, description="Nilai total PPn dari seluruh halaman.")
    SubTotal: float = Field(default=0.0, description="Nilai subtotal transaksi.")
    DiskonLain: float = Field(default=0.0, description="Diskon tambahan lain jika ada.")
    BiayaLain: float = Field(default=0.0, description="Biaya kirim atau biaya tambahan lain.")
    GrandTotal: float = Field(default=0.0, description="Total akhir yang wajib dibayar (biasanya ada di halaman paling terakhir).")
    Status: str = Field(default="KOSONG", description="Status nota.")
    ID_Suplier: str = Field(default="S-0000", description="Nama supplier teks asli (Misal 'INDOMARCO').")
    Faktur: str = Field(default="KOSONG", description="Nomor faktur pajak jika tertera.")

class DetailPembelianItem(BaseModel):
    NamaBarangAsli: str = Field(default="KOSONG", description="Nama barang/produk/item lengkap dari halaman mana pun.")
    ID_Barang: str = Field(default="KOSONG", description="Isi selalu dengan 'KOSONG'.")
    Jumlah: float = Field(default=0.0, description="Jumlah kuantitas barang (Qty).")
    Satuan: str = Field(default="KOSONG", description="Satuan barang seperti 'ZAK', 'PCS', 'CRT'.")
    Diskon: float = Field(default=0.0, description="Diskon per item barang jika ada.")
    HargaBeli: float = Field(default=0.0, description="Harga satuan barang.")
    PPn: float = Field(default=0.0, description="Nilai PPn per item.")
    SubTotal: float = Field(default=0.0, description="Total harga untuk item ini (Jumlah x HargaBeli).")
    Barcode: str = Field(default="KOSONG", description="Kode barcode barang jika tertera.")

class InvoiceMasterSkrema(BaseModel):
    header: HeaderPembelian
    details: List[DetailPembelianItem]


def main():
    print("=== MEMULAI PIPELINE MULTI-HALAMAN UNTUK VB.NET ===")
    
    if not os.path.exists(FOLDER_INPUT_INVOICE):
        os.makedirs(FOLDER_INPUT_INVOICE)
        print(f"[INFO] Folder '{FOLDER_INPUT_INVOICE}' baru saja dibuat. Silakan taruh foto-foto invoice di sana lalu jalankan ulang.")
        return

    # Ambil semua file gambar di dalam folder input
    daftar_foto = sorted([
        f for f in os.listdir(FOLDER_INPUT_INVOICE) 
        if f.lower().endswith(('.png', '.jpg', '.jpeg'))
    ])

    if not daftar_foto:
        print(f"ERROR: Tidak ada file gambar di dalam folder '{FOLDER_INPUT_INVOICE}'!")
        return

    # -----------------------------------------------------------------
    # TAHAP A: ESTAFET OCR (LOOPING SEMUA HALAMAN)
    # -----------------------------------------------------------------
    mega_raw_text = ""
    
    custom_endpoint = f"{LOCATION}://googleapis.com"
    opts = ClientOptions(api_endpoint=custom_endpoint)
    docai_client = documentai.DocumentProcessorServiceClient(client_options=opts, transport="rest")
    processor_name = docai_client.processor_path(PROJECT_ID, LOCATION, PROCESSOR_ID)

    print(f"Mendeteksi {len(daftar_foto)} halaman foto invoice...")
    for indeks, nama_file in enumerate(daftar_foto, start=1):
        jalur_foto = os.path.join(FOLDER_INPUT_INVOICE, nama_file)
        print(f"[{indeks}/{len(daftar_foto)}] Melakukan OCR pada berkas: {nama_file}...")
        
        with open(jalur_foto, "rb") as image:
            image_content = image.read()
            
        raw_document = documentai.RawDocument(content=image_content, mime_type="image/jpeg")
        request = documentai.ProcessRequest(name=processor_name, raw_document=raw_document)
        
        docai_response = docai_client.process_document(request=request)
        mega_raw_text += f"\n--- TEKS HALAMAN POTONGAN KE-{indeks} ---\n"
        mega_raw_text += docai_response.document.text

    print("-> Sukses menggabungkan seluruh teks OCR dari semua halaman.")

    # -----------------------------------------------------------------
    # TAHAP B: REKONSILIASI GABUNGAN DENGAN GEMINI 2.5 FLASH
    # -----------------------------------------------------------------
    print("\nMengirim seluruh gabungan teks OCR ke Gemini 2.5 Flash untuk penyatuan JSON kustom...")
    vertexai.init(project=PROJECT_ID, location=LOCATION)
    
    konfigurasi_output = GenerationConfig(
        response_mime_type="application/json",
        response_schema=InvoiceMasterSkrema.model_json_schema(),
        temperature=0.1
    )
    
    model_gemini = GenerativeModel("gemini-2.5-flash")
    
    prompt_analisis = (
        f"Teks di bawah ini adalah gabungan hasil OCR dari beberapa halaman/potongan foto sebuah invoice panjang.\n"
        f"Tugas utama Anda:\n"
        f"1. Satukan semua data ini menjadi SATU objek JSON tunggal.\n"
        f"2. Gabungkan (*append*) semua daftar item barang dari seluruh halaman ke dalam array 'details'.\n"
        f"3. Untuk bagian objek 'header', ambil informasi nomor nota, tanggal, dan nama supplier utama sekali saja "
        f"dari halaman pertama. Cari nilai nominal GrandTotal akhir dari total akumulasi atau halaman paling akhir.\n"
        f"4. Untuk field string kosong isi dengan 'KOSONG', dan field numerik kosong isi dengan 0:\n\n{mega_raw_text}"
    )
    
    gemini_response = model_gemini.generate_content(prompt_analisis, generation_config=konfigurasi_output)
    data_final = json.loads(gemini_response.text)
    
    # -----------------------------------------------------------------
    # TAHAP C: GENERASI ID DATA PEMBELIAN SEMENTARA
    # -----------------------------------------------------------------
    tanggal_sekarang = datetime.now().strftime("%Y%m%d")
    id_pembelian_sementara = f"BL-{tanggal_sekarang}-{str(uuid.uuid4())[:4].upper()}"
    guid_global = str(uuid.uuid4()).upper()
    
    data_final["header"]["ID_Pembelian"] = id_pembelian_sementara
    data_final["header"]["GUID"] = guid_global
    data_final["header"]["ID_Gudang"] = "1"
    data_final["header"]["Lunas"] = "Hutang"
    
    # Mengambil nomor nota hasil bacaan AI sebagai nama file rekomendasi bawaan
    no_nota_rekomendasi = data_final["header"]["NoNota"].replace("/", "-").replace("\\", "-")
    if no_nota_rekomendasi == "KOSONG":
        no_nota_rekomendasi = id_pembelian_sementara

    # -----------------------------------------------------------------
    # TAHAP D: POP-UP PILIH FOLDER DAN TENTUKAN NAMA FILE (WINDOWS DIALOG)
    # -----------------------------------------------------------------
    print("\nMenunggu tindakan pengguna untuk menentukan lokasi simpan file...")
    
    # Inisialisasi jendela tkinter tersembunyi
    root = tk.Tk()
    root.withdraw()
    root.attributes("-topmost", True) # Memaksa pop-up muncul di baris paling depan layar Windows
    
    # Membuka Windows 'Save As' Dialog Box
    nama_file_pilihan_user = filedialog.asksaveasfilename(
        title="Pilih Lokasi Folder & Tentukan Nama File JSON Anda",
        initialfile=f"{no_nota_rekomendasi}.json",
        defaultextension=".json",
        filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
    )
    
    # Validasi jika pengguna menekan tombol Cancel pada dialog box
    if not nama_file_pilihan_user:
        print("[⚠️ PERINGATAN] Pengguna membatalkan penyimpanan file! Data JSON hanya dicetak di layar.")
        print(json.dumps(data_final, indent=4))
        return

    # Menyimpan file fisik .json ke lokasi folder pilihan user
    with open(nama_file_pilihan_user, "w", encoding="utf-8") as f:
        json.dump(data_final, f, indent=4, ensure_ascii=False)

    print("\n=== PEMROSESAN & PENYIMPANAN SUKSES ===")
    print(f"Total item barang yang berhasil digabungkan: {len(data_final['details'])} item.")
    print(f"Berkas fisik JSON sukses tersimpan di: {nama_file_pilihan_user} ✅")
    print("========================================")

if __name__ == "__main__":
    main()
