from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime
import os
from urllib.parse import quote_plus

# 1. Inisialisasi Aplikasi dan Ekstensi
app = Flask(__name__)
app.config['SECRET_KEY'] = 'ud-kuat-betelok-secret-key-2025'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# =========================================================
# KONFIGURASI DATABASE (AUTO SWITCH LOKAL / CPANEL)
# =========================================================

# GANTI KE 'True' SAAT UPLOAD KE CPANEL / HOSTING (MYSQL). 
# BIARKAN 'False' SAAT DI KOMPUTER (LOKAL - SQLITE).
IS_PRODUCTION = False

if IS_PRODUCTION:
    # --- SETTINGAN CPANEL (MySQL) ---
    # Masukkan detail database MySQL Anda di sini
    db_user = 'rekappas_oshacompUSER'      
    db_password = '@Muzakki123'      
    db_host = 'localhost'                 
    db_name = 'rekappas_oshacompDB'      
    
    # Encode password untuk menangani karakter '@' atau simbol lainnya agar tidak error di URI
    encoded_password = quote_plus(db_password)
    
    # URI Koneksi menggunakan pymysql
    app.config['SQLALCHEMY_DATABASE_URI'] = f'mysql+pymysql://{db_user}:{encoded_password}@{db_host}/{db_name}'
    
    # Optimasi koneksi MySQL di cPanel agar tidak mudah "Gone Away"
    app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
        'pool_recycle': 280, 
        'pool_pre_ping': True
    }
else:
    # --- SETTINGAN LOKAL (SQLite) ---
    # Menggunakan path absolute agar file .db tersimpan di folder project yang benar
    base_dir = os.path.abspath(os.path.dirname(__file__))
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(base_dir, 'oshacomp.db')


db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'

# ==========================================
# HELPER FUNCTION: MULTI SATUAN
# ==========================================
def format_multi_satuan(stok_total, units_list, satuan_terkecil):
    """
    Mengubah stok integer (misal 150) menjadi format string 
    (misal "1 Slop, 2 Bungkus, 6 Batang").
    Digunakan oleh property Product.stok_display.
    """
    if stok_total <= 0:
        return "0 " + (satuan_terkecil or "")

    # 1. Urutkan unit dari yang konversinya paling BESAR ke KECIL
    # Menggunakan atribut 'jumlah_konversi' dari model ProductUnit
    sorted_units = sorted(units_list, key=lambda x: x.jumlah_konversi, reverse=True)
    
    result = []
    sisa_stok = stok_total
    
    # 2. Loop konversi
    for unit in sorted_units:
        if unit.jumlah_konversi > 0 and sisa_stok >= unit.jumlah_konversi:
            jumlah_unit = sisa_stok // unit.jumlah_konversi # Pembagian bulat
            sisa_stok = sisa_stok % unit.jumlah_konversi    # Sisa bagi
            
            result.append(f"{jumlah_unit} {unit.nama_satuan}")
            
    # 3. Masukkan sisa satuan terkecil jika ada
    if sisa_stok > 0:
        result.append(f"{sisa_stok} {satuan_terkecil}")
        
    return ", ".join(result)

# ==========================================
# 2. Definisi Model Database
# ==========================================

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(100), unique=True, nullable=False)
    password_hash = db.Column(db.String(200), nullable=False)
    nama_lengkap = db.Column(db.String(200), nullable=False)
    role = db.Column(db.String(50), nullable=False)  # Admin, Kasir, Owner
    created_at = db.Column(db.DateTime, default=datetime.now)
    
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)
    
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

class Customer(db.Model):
    __tablename__ = 'customers'
    id = db.Column(db.Integer, primary_key=True)
    nama = db.Column(db.String(200), nullable=False)
    alamat = db.Column(db.Text)
    no_hp = db.Column(db.String(20))
    jenis = db.Column(db.String(50), nullable=False)  # Biasa, Sub Agen
    limit_piutang = db.Column(db.Float, default=0)
    created_at = db.Column(db.DateTime, default=datetime.now)    
    sales = db.relationship('Sale', backref='customer', lazy=True)

class Product(db.Model):
    __tablename__ = 'products'
    id = db.Column(db.Integer, primary_key=True)
    kode_barang = db.Column(db.String(50), unique=True, nullable=False)
    barcode = db.Column(db.String(100), unique=True)
    nama_barang = db.Column(db.String(200), nullable=False)
    kategori = db.Column(db.String(100))
    
    # Satuan Terkecil (Base Unit) - Stok fisik dihitung berdasarkan ini
    satuan = db.Column(db.String(50)) 
    
    # Harga Dasar (Per Satuan Terkecil)
    harga_pokok = db.Column(db.Float, nullable=False)
    harga_ecer = db.Column(db.Float, nullable=False)
    harga_grosir = db.Column(db.Float, nullable=False)
    
    # Stok selalu dalam satuan terkecil
    stok = db.Column(db.Integer, default=0)
    stok_minimum = db.Column(db.Integer, default=10)
    
    is_service = db.Column(db.Boolean, default=False) 
    created_at = db.Column(db.DateTime, default=datetime.now)
    
    # Relasi
    sale_items = db.relationship('SaleItem', backref='product', lazy=True, cascade='all, delete-orphan')
    purchase_items = db.relationship('PurchaseItem', backref='product', lazy=True, cascade='all, delete-orphan')
    movements = db.relationship('StockMovement', backref='product', lazy=True, cascade='all, delete-orphan')
    losses = db.relationship('InventoryLoss', backref='product', lazy=True, cascade='all, delete-orphan')
    
    # Relasi ke Satuan Konversi (Multi Unit)
    units = db.relationship('ProductUnit', backref='product', lazy=True, cascade='all, delete-orphan')

    @property
    def stok_display(self):
        """Menampilkan stok dalam format multi-satuan otomatis."""
        return format_multi_satuan(self.stok, self.units, self.satuan)

class ProductUnit(db.Model):
    """Tabel untuk menyimpan satuan konversi (Multi Satuan)."""
    __tablename__ = 'product_units'
    id = db.Column(db.Integer, primary_key=True)
    product_id = db.Column(db.Integer, db.ForeignKey('products.id'), nullable=False)
    
    nama_satuan = db.Column(db.String(50), nullable=False)
    
    # Berapa jumlah satuan terkecil dalam satuan ini? (Faktor Konversi)
    # Contoh: 1 Slop = 120 Batang -> jumlah_konversi = 120
    jumlah_konversi = db.Column(db.Integer, nullable=False, default=1)
    
    # Harga khusus untuk satuan ini
    harga_pokok = db.Column(db.Float, default=0)
    harga_ecer = db.Column(db.Float, default=0)
    harga_grosir = db.Column(db.Float, default=0)
    
    # Barcode khusus satuan ini
    barcode = db.Column(db.String(100), unique=True, nullable=True)

class InventoryLoss(db.Model):
    __tablename__ = 'inventory_losses'
    id = db.Column(db.Integer, primary_key=True)
    product_id = db.Column(db.Integer, db.ForeignKey('products.id'), nullable=False)
    tanggal = db.Column(db.DateTime, default=datetime.now)
    
    qty = db.Column(db.Integer, nullable=False) 
    satuan = db.Column(db.String(50))  
    qty_konversi = db.Column(db.Integer, default=1) 
    
    alasan = db.Column(db.String(100), nullable=False) 
    total_rugi = db.Column(db.Float, nullable=False) 
    keterangan = db.Column(db.Text)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))

class Sale(db.Model):
    __tablename__ = 'sales'
    id = db.Column(db.Integer, primary_key=True)
    nomor_nota = db.Column(db.String(100), unique=True, nullable=False)
    customer_id = db.Column(db.Integer, db.ForeignKey('customers.id'), nullable=False)
    tanggal = db.Column(db.DateTime, default=datetime.now)
    total = db.Column(db.Float, nullable=False)
    diskon = db.Column(db.Float, default=0)
    total_bayar = db.Column(db.Float, nullable=False)
    metode_pembayaran = db.Column(db.String(50), nullable=False)  # Tunai, Tempo, Cicilan
    status = db.Column(db.String(50), default='Lunas')  # Lunas, Belum Lunas
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    jumlah_terima = db.Column(db.Float, default=0) # Uang yang dibayarkan pelanggan
    kembalian = db.Column(db.Float, default=0)     # Uang kembalian
    items = db.relationship('SaleItem', backref='sale', lazy=True, cascade='all, delete-orphan')
    payments = db.relationship('Payment', backref='sale', lazy=True, cascade='all, delete-orphan')
    user = db.relationship('User', backref='sales')

class SaleItem(db.Model):
    __tablename__ = 'sale_items'
    id = db.Column(db.Integer, primary_key=True)
    sale_id = db.Column(db.Integer, db.ForeignKey('sales.id'), nullable=False)
    product_id = db.Column(db.Integer, db.ForeignKey('products.id'), nullable=False)
    
    # Qty fisik yang dipilih user (misal: 2 Slop)
    qty = db.Column(db.Integer, nullable=False)
    
    # Nama satuan yang dipilih saat transaksi
    satuan = db.Column(db.String(50), nullable=False, default='Pcs')
    
    # Faktor konversi saat transaksi (misal: 120 untuk Slop)
    # Total pengurangan stok = qty * qty_konversi
    qty_konversi = db.Column(db.Integer, nullable=False, default=1)
    
    harga = db.Column(db.Float, nullable=False)
    subtotal = db.Column(db.Float, nullable=False)

class Payment(db.Model):
    __tablename__ = 'payments'
    id = db.Column(db.Integer, primary_key=True)
    sale_id = db.Column(db.Integer, db.ForeignKey('sales.id'), nullable=False)
    tanggal = db.Column(db.DateTime, default=datetime.now)
    jumlah = db.Column(db.Float, nullable=False)
    keterangan = db.Column(db.Text)
    
class CashFlow(db.Model):
    __tablename__ = 'cash_flow'
    id = db.Column(db.Integer, primary_key=True)
    tanggal = db.Column(db.DateTime, default=datetime.now)
    jenis = db.Column(db.String(50), nullable=False)  # Masuk, Keluar
    kategori = db.Column(db.String(100))  # Penjualan, Pembelian, Operasional, dll
    jumlah = db.Column(db.Float, nullable=False)
    keterangan = db.Column(db.Text)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    
    user = db.relationship('User', backref='cash_flows')

class StockMovement(db.Model):
    __tablename__ = 'stock_movements'
    id = db.Column(db.Integer, primary_key=True)
    product_id = db.Column(db.Integer, db.ForeignKey('products.id'), nullable=False)
    tanggal = db.Column(db.DateTime, default=datetime.now)
    jenis = db.Column(db.String(50), nullable=False)  # Masuk, Keluar
    qty = db.Column(db.Integer, nullable=False)
    keterangan = db.Column(db.Text)

class Settings(db.Model):
    __tablename__ = 'settings'
    id = db.Column(db.Integer, primary_key=True)
    company_name = db.Column(db.String(200), default='UD. Kuat Betelok')
    company_address = db.Column(db.Text, default='Jl. Raya Perdagangan No. 123')
    company_phone = db.Column(db.String(50), default='(021) 12345678')
    company_wa = db.Column(db.String(50), default='0812-3456-7890')
    company_email = db.Column(db.String(100), default='info@udkuatbetelok.com')
    company_website = db.Column(db.String(100), default='www.udkuatbetelok.com')
    logo_path = db.Column(db.String(200), default='logo.png')
    print_note = db.Column(db.Text, default='Barang yang sudah dibeli tidak dapat dikembalikan')
    created_at = db.Column(db.DateTime, default=datetime.now)
    updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)

class Supplier(db.Model):
    __tablename__ = 'suppliers'
    id = db.Column(db.Integer, primary_key=True)
    nama = db.Column(db.String(200), nullable=False)
    kontak_person = db.Column(db.String(200))
    no_hp = db.Column(db.String(20))
    alamat = db.Column(db.Text)
    created_at = db.Column(db.DateTime, default=datetime.now)    
    purchases = db.relationship('Purchase', backref='supplier', lazy=True)

class Purchase(db.Model):
    __tablename__ = 'purchases'
    id = db.Column(db.Integer, primary_key=True)
    nomor_faktur = db.Column(db.String(100), unique=True, nullable=False)
    supplier_id = db.Column(db.Integer, db.ForeignKey('suppliers.id'), nullable=False)
    tanggal = db.Column(db.DateTime, default=datetime.now)
    total = db.Column(db.Float, nullable=False, default=0)
    metode_pembayaran = db.Column(db.String(50), nullable=False) # Tunai, Tempo, Cicilan
    status = db.Column(db.String(50), default='Lunas') # Lunas, Belum Lunas
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    
    items = db.relationship('PurchaseItem', backref='purchase', lazy=True, cascade='all, delete-orphan')
    payments = db.relationship('SupplierPayment', backref='purchase', lazy=True, cascade='all, delete-orphan')
    user = db.relationship('User', backref='purchases')

class PurchaseItem(db.Model):
    __tablename__ = 'purchase_items'
    id = db.Column(db.Integer, primary_key=True)
    purchase_id = db.Column(db.Integer, db.ForeignKey('purchases.id'), nullable=False)
    product_id = db.Column(db.Integer, db.ForeignKey('products.id'), nullable=False)
    qty = db.Column(db.Integer, nullable=False)
    satuan = db.Column(db.String(50))
    qty_konversi = db.Column(db.Integer, default=1)
    harga_beli = db.Column(db.Float, nullable=False)
    subtotal = db.Column(db.Float, nullable=False)

class SupplierPayment(db.Model):
    __tablename__ = 'supplier_payments'
    id = db.Column(db.Integer, primary_key=True)
    purchase_id = db.Column(db.Integer, db.ForeignKey('purchases.id'), nullable=False)
    tanggal = db.Column(db.DateTime, default=datetime.now)
    jumlah = db.Column(db.Float, nullable=False)
    keterangan = db.Column(db.Text)

class StockOpname(db.Model):
    __tablename__ = 'stock_opnames'
    id = db.Column(db.Integer, primary_key=True)
    tanggal = db.Column(db.DateTime, default=datetime.now)
    keterangan = db.Column(db.String(200))
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    
    items = db.relationship('StockOpnameItem', backref='opname', lazy=True, cascade='all, delete-orphan')
    user = db.relationship('User', backref='opnames')

class StockOpnameItem(db.Model):
    __tablename__ = 'stock_opname_items'
    id = db.Column(db.Integer, primary_key=True)
    opname_id = db.Column(db.Integer, db.ForeignKey('stock_opnames.id'), nullable=False)
    product_id = db.Column(db.Integer, db.ForeignKey('products.id'), nullable=False)
    stok_sistem = db.Column(db.Integer, nullable=False)
    stok_aktual = db.Column(db.Integer, nullable=False)
    selisih = db.Column(db.Integer, nullable=False)
    product = db.relationship('Product')

class RepairOrder(db.Model):
    __tablename__ = 'repair_orders'
    id = db.Column(db.Integer, primary_key=True)
    nomor_service = db.Column(db.String(100), unique=True, nullable=False)
    customer_id = db.Column(db.Integer, db.ForeignKey('customers.id'), nullable=False)
    tanggal_masuk = db.Column(db.DateTime, default=datetime.now)
    tanggal_selesai = db.Column(db.DateTime, nullable=True)

    # Data barang yang direparasi
    nama_barang = db.Column(db.String(200), nullable=False)
    merk = db.Column(db.String(100))
    tipe = db.Column(db.String(100))
    serial_number = db.Column(db.String(150))
    kelengkapan = db.Column(db.Text)
    kondisi_fisik = db.Column(db.Text)

    # Rekam masalah & diagnosa (seperti rekam medis)
    keluhan = db.Column(db.Text, nullable=False)
    diagnosa = db.Column(db.Text)

    teknisi = db.Column(db.String(100))
    status = db.Column(db.String(50), default='Masuk')  # Masuk, Diagnosa, Perbaikan, Selesai, Diambil, Batal

    estimasi_biaya = db.Column(db.Float, default=0)
    total_biaya = db.Column(db.Float, default=0)
    jumlah_dibayar = db.Column(db.Float, default=0)
    status_bayar = db.Column(db.String(50), default='Belum Bayar')  # Belum Bayar, DP, Lunas

    catatan = db.Column(db.Text)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))

    actions = db.relationship('RepairAction', backref='repair', lazy=True, cascade='all, delete-orphan',
                              order_by='RepairAction.tanggal.asc()')
    customer = db.relationship('Customer', backref='repairs')
    user = db.relationship('User', backref='repairs')

    @property
    def sisa_tagihan(self):
        return max(0, (self.total_biaya or 0) - (self.jumlah_dibayar or 0))

class RepairAction(db.Model):
    __tablename__ = 'repair_actions'
    id = db.Column(db.Integer, primary_key=True)
    repair_id = db.Column(db.Integer, db.ForeignKey('repair_orders.id'), nullable=False)
    tanggal = db.Column(db.DateTime, default=datetime.now)
    tindakan = db.Column(db.Text, nullable=False)
    sparepart = db.Column(db.Text)
    biaya_jasa = db.Column(db.Float, default=0)
    biaya_sparepart = db.Column(db.Float, default=0)
    total = db.Column(db.Float, default=0)
    teknisi = db.Column(db.String(100))
    catatan = db.Column(db.Text)

# 3. Fungsi Helper dan Konfigurasi Lanjutan
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

@app.context_processor
def inject_settings():
    """Membuat variabel settings tersedia di semua template Jinja2."""
    settings = Settings.query.first()
    if not settings:
        # Jika belum ada data di tabel settings, buat objek default
        settings = Settings()
    return dict(app_settings=settings)
    
# ======================================================================
# Mengimpor semua rute dari file routes.py agar aplikasi mengenali URL.
# Ini harus dilakukan setelah 'app' dan 'db' dibuat.
# ======================================================================
from routes import *

# 4. Fungsi untuk Inisialisasi Database
def init_db():
    """Membuat tabel database, user admin, dan settings default jika belum ada."""
    with app.app_context():
        db.create_all()
        
        # Membuat user admin default jika belum ada
        if not User.query.filter_by(username='admin').first():
            admin = User(
                username='admin',
                nama_lengkap='Administrator',
                role='Admin'
            )
            admin.set_password('06mei1994')
            db.session.add(admin)
            db.session.commit()
            print("Default admin created")
        
        # Membuat data settings default jika belum ada
        if not Settings.query.first():
            settings = Settings()
            db.session.add(settings)
            db.session.commit()
            print("Default settings created")

        # Membuat Pelanggan Umum Default
        if not Customer.query.filter_by(nama='Pelanggan Umum').first():
            general_customer = Customer(
                nama='Pelanggan Umum',
                alamat='-',
                no_hp='-',
                jenis='Biasa',
                limit_piutang=0
            )
            db.session.add(general_customer)
            db.session.commit()
            print("Default 'Pelanggan Umum' created")

# Pindahkan init_db() ke luar blok __main__ 
# agar dieksekusi otomatis oleh server cPanel (WSGI/Passenger)
try:
    init_db()
except Exception as e:
    print(f"Gagal inisialisasi database: {e}")

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)