<?php

namespace App\Services;

use App\Models\JournalEntry;
use App\Models\JournalItem;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;
use Exception;

class AccountingService
{
    /**
     * Fungsi Global untuk mencatat Jurnal
     */
    public static function log(array $data)
    {
        return DB::transaction(function () use ($data) {
            $userId = $data['user_id'] ?? auth()->id() ?? 1;

            $entry = JournalEntry::create([
                'branch_id'   => $data['branch_id'],
                'entry_date'  => $data['entry_date'],
                'description' => $data['description'],
                'reference'   => $data['reference'] ?? null,
                'source_type' => $data['source_type'] ?? null,
                'user_id'     => $userId,
            ]);

            foreach ($data['items'] as $item) {
                if (empty($item['account_id'])) {
                    throw new Exception("Jurnal Gagal: Akun tidak ditemukan untuk: " . ($data['description'] ?? ''));
                }

                JournalItem::create([
                    'journal_entry_id' => $entry->id,
                    'account_id'       => $item['account_id'],
                    'debit'            => $item['debit'] ?? 0,
                    'credit'           => $item['credit'] ?? 0,
                ]);
            }
            return $entry;
        });
    }

    /**
     * Ambil ID Akun berdasarkan Key Mapping dari tabel accounting_settings
     */
    public static function getAccountId($key)
    {
        $id = DB::table('accounting_settings')
            ->where('setting_key', $key)
            ->value('account_id');

        if (!$id) {
            Log::warning("AccountingService: Key '$key' tidak ditemukan di accounting_settings.");
        }
        
        return $id;
    }

    /**
     * Kalkulasi Moving Average
     */
    public static function updateMovingAverage($productId, $newQty, $newPrice)
    {
        $product = DB::table('products')->where('id', $productId)->lockForUpdate()->first();
        if (!$product) return false;

        $currentQty = (float) ($product->stock_qty ?? 0);
        $oldAvgCost = (float) ($product->average_cost ?? 0);

        if ($currentQty <= 0) {
            $newAvgCost = (float) $newPrice;
        } else {
            $totalValue = ($currentQty * $oldAvgCost) + ($newQty * $newPrice);
            $totalQty = $currentQty + $newQty;
            $newAvgCost = $totalQty > 0 ? ($totalValue / $totalQty) : 0;
        }

        DB::table('products')->where('id', $productId)->update(['average_cost' => $newAvgCost]);
        return true;
    }

    /**
     * Menutup selisih (Variance) antara biaya dibebankan dan biaya aktual
     * Dijalankan di akhir periode (biasanya akhir bulan).
     */
    public static function closeMonthlyVariance($branchId, $month, $year, $actualLaborCost, $actualOverheadCost)
    {
        return DB::transaction(function () use ($branchId, $month, $year, $actualLaborCost, $actualOverheadCost) {
            
            // 1. Ambil ID Akun dari database settings
            $akunBTKLDibebankan = self::getAccountId('btkl_dibebankan'); 
            $akunBOPDibebankan  = self::getAccountId('bop_dibebankan');
            $akunHPP            = self::getAccountId('cost_of_goods_sold_account');

            if (!$akunBTKLDibebankan || !$akunBOPDibebankan || !$akunHPP) {
                throw new Exception("Master Akun untuk Variance (BTKL/BOP/HPP) belum dipetakan secara lengkap di accounting_settings.");
            }

            // ====================================================================
            // LOGIKA REVISI TUTUP BUKU (MENCEGAH DOUBLE ENTRY)
            // ====================================================================
            // Jika eksekusi ini adalah hasil dari "Buka Kunci" dan dikerjakan ulang,
            // kita hapus dulu Jurnal Varians bulan terkait yang pernah dibuat sebelumnya.
            JournalEntry::where('branch_id', $branchId)
                ->whereMonth('entry_date', $month)
                ->whereYear('entry_date', $year)
                ->where('source_type', 'MONTHLY_VARIANCE_ADJUSTMENT')
                ->delete(); // Karena ada ON DELETE CASCADE di database, journal_items juga akan otomatis terhapus.

            // 2. Hitung total biaya yang sudah DIBEBANKAN ke persediaan melalui jurnal SPK bulan ini
            // (Catatan: Ini aman dari jurnal revisi karena kita filter strict ke source_type 'FABRICATION_FINISH')
            $totalDibebankanBTKL = DB::table('journal_items')
                ->join('journal_entries', 'journal_items.journal_entry_id', '=', 'journal_entries.id')
                ->where('journal_entries.branch_id', $branchId)
                ->whereMonth('journal_entries.entry_date', $month)
                ->whereYear('journal_entries.entry_date', $year)
                ->where('journal_entries.source_type', 'FABRICATION_FINISH')
                ->where('journal_items.account_id', $akunBTKLDibebankan)
                ->sum('journal_items.credit');

            $totalDibebankanBOP = DB::table('journal_items')
                ->join('journal_entries', 'journal_items.journal_entry_id', '=', 'journal_entries.id')
                ->where('journal_entries.branch_id', $branchId)
                ->whereMonth('journal_entries.entry_date', $month)
                ->whereYear('journal_entries.entry_date', $year)
                ->where('journal_entries.source_type', 'FABRICATION_FINISH')
                ->where('journal_items.account_id', $akunBOPDibebankan)
                ->sum('journal_items.credit');

            // 3. Hitung Selisih (Variance) = Biaya Aktual - Biaya yang Dibebankan
            $varianceBTKL = $actualLaborCost - $totalDibebankanBTKL;
            $varianceBOP  = $actualOverheadCost - $totalDibebankanBOP;

            $items = [];
            
            // 4A. Jurnal Penyesuaian untuk BTKL
            if ($varianceBTKL != 0) {
                $items[] = [
                    'account_id' => $akunBTKLDibebankan, 
                    'debit'      => ($varianceBTKL > 0 ? $varianceBTKL : 0), 
                    'credit'     => ($varianceBTKL < 0 ? abs($varianceBTKL) : 0)
                ];
                $items[] = [
                    'account_id' => $akunHPP, 
                    'debit'      => ($varianceBTKL > 0 ? $varianceBTKL : 0), 
                    'credit'     => ($varianceBTKL < 0 ? abs($varianceBTKL) : 0)
                ];
            }

            // 4B. Jurnal Penyesuaian untuk BOP
            if ($varianceBOP != 0) {
                $items[] = [
                    'account_id' => $akunBOPDibebankan, 
                    'debit'      => ($varianceBOP > 0 ? $varianceBOP : 0), 
                    'credit'     => ($varianceBOP < 0 ? abs($varianceBOP) : 0)
                ];
                $items[] = [
                    'account_id' => $akunHPP, 
                    'debit'      => ($varianceBOP > 0 ? $varianceBOP : 0), 
                    'credit'     => ($varianceBOP < 0 ? abs($varianceBOP) : 0)
                ];
            }

            // 5. Simpan Jurnal jika ada selisih
            if (!empty($items)) {
                $lastDayOfMonth = Carbon::createFromDate($year, $month, 1)->endOfMonth()->toDateString();
                
                self::log([
                    'branch_id'   => $branchId,
                    'entry_date'  => $lastDayOfMonth,
                    'description' => "Penyesuaian Varians Biaya Produksi (Bulan $month/$year)",
                    'reference'   => "VAR-$month-$year",
                    'source_type' => 'MONTHLY_VARIANCE_ADJUSTMENT',
                    'user_id'     => auth()->id() ?? 1,
                    'items'       => $items
                ]);
            }

            return [
                'success' => true,
                'message' => 'Rekonsiliasi varians bulan ini berhasil dicatat.',
                'variance_btkl' => $varianceBTKL,
                'variance_bop' => $varianceBOP
            ];
        });
    }
}