<?php

namespace App\Http\Controllers\Admin\Odoo;

use App\Http\Controllers\Controller;
use Auth;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\PartnerData;
use App\OdooSystem;
use App\OdooJvSection;
use App\OdooJvStep;
use App\OdooJvEntry;
use Illuminate\Support\Facades\Log;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class OdooController extends Controller
{
    private $request;

    private $creditAmountStepKey = [];

    /** Odoo system slug used for JV config lookups (default: 'da') */
    private $systemSlug = 'da';

    public function __construct(Request $request)
    {
        $this->request  = $request;
        // Allow callers to specify a system via ?system=slug
        $this->systemSlug = $request->get('system', 'da') ?: 'da';
    }

    public static function registry(){
        return [
            [
                'name'    => 'Odoo Partners',
                'sessionKey' => 'Odoo_View_Partners_View|16-7-0',
                'summary' => 'Manage Odoo partners configurations and details.',
                'url'     => '/admin/odoo',
            ],
            [
                'name'    => 'Odoo JVs',
                'sessionKey' => 'Odoo_View_JVs_View|16-8-0',
                'summary' => 'Manage Odoo JVs configurations and details.',
                'url'     => '/admin/odoo/jvs',
            ],
            [
                'name'    => 'Odoo Configuration',
                'sessionKey' => 'Odoo_View_JVs_View|16-8-0',
                'summary' => 'Configure JV steps, sections and GL code mappings per system (DA, Credit Fair, Co-lending).',
                'url'     => '/admin/odoo/dashboard',
            ],
            [
                'name'    => 'Odoo Execution',
                'sessionKey' => 'Odoo_Execution_View|16-10-0',
                'summary' => 'Maker & Checker execution dashboard for JVs and Disbursement PVs, view audit logs of changes',
                'url'     => '/admin/odoo/execution',
            ]
        ];
    }

    public function index()
    {
        $fromDate = $this->request->get('from_date');
        $toDate = $this->request->get('to_date');

        $query = PartnerData::with(['odooDetail', 'parent'])->whereNotNull('sub_partner_id');

        if ($fromDate) {
            $query->whereDate('created_at', '>=', $fromDate);
        }
        if ($toDate) {
            $query->whereDate('created_at', '<=', $toDate);
        }

        $partners = $query->orderBy('id', 'desc')->paginate(20);

        return view('admin.odoo.generate-odoo', compact('partners', 'fromDate', 'toDate'));
    }

    public function jvs()
    {
        $fromDate = $this->request->get('from_date', \Carbon\Carbon::now()->subWeek()->format('Y-m-d'));
        $toDate = $this->request->get('to_date', \Carbon\Carbon::now()->format('Y-m-d'));
        $systemSlug = $this->request->get('system', 'da') ?: 'da';

        // NEW: Fetch partners for the selected system
        $system = OdooSystem::where('slug', $systemSlug)->where('is_active', true)->first();
        $partners = $system ? $system->getPartners() : collect();

        $query = \App\DaSystemData::query();

        if ($fromDate) {
            $query->whereDate('created_at', '>=', $fromDate);
        }
        if ($toDate) {
            $query->whereDate('created_at', '<=', $toDate);
        }

        $applications = $query->orderBy('id', 'desc')->paginate(20);
        $systems = OdooSystem::where('is_active', true)->orderBy('id')->get();
        $user = \Auth::user();
        $userOdooRole = $user ? $user->odoo_role : null;
        $isMaker = $user && $user->isOdooMaker();

        return view('admin.odoo.jvs', compact(
            'applications', 
            'fromDate', 
            'toDate', 
            'systems', 
            'systemSlug', 
            'isMaker', 
            'userOdooRole',
            'partners' // NEW: Pass partners to view
        ));
    }

    public function getJvStepsAjax()
    {
        $applicationId = $this->request->get('application_id');
        $partnerId = $this->request->get('partner_id'); // NEW: Accept partner ID
        $this->systemSlug = $this->request->get('system', 'da') ?: 'da';
        
        if (!$applicationId) {
            return response()->json([
                'status' => 'error',
                'message' => 'application_id is required'
            ], 400);
        }

        $month = $this->request->get('month') ?? Carbon::now()->format('m');
        $year = $this->request->get('year') ?? Carbon::now()->format('Y');

        if($this->systemSlug == 'da'){
            $data = $this->getSystemData($applicationId, ['year' => $year, 'month' => $month]);
        }else if($this->systemSlug == 'creditfair'){
            $data = $this->getCreditfairSystemData($applicationId, ['year' => $year, 'month' => $month]);
        }else if($this->systemSlug == 'co-lending'){
            $data = $this->getCoLendingSystemData($applicationId, ['year' => $year, 'month' => $month]);
        }

        $jvSections = $this->getJvSteps('', $partnerId); // Pass partner ID
        $calculatedSections = [];

        foreach ($jvSections as $section) {
            $calculatedSteps = [];
            foreach ($section['steps'] as $step) {
                // Call getStepsWithCalculation to calculate the real amount based on gl_code
                $calculatedEntries = $this->getStepsWithCalculation($step['id'], $data, $partnerId); // Pass partner ID

                $originalEntries = $step['entries'];
                $finalEntries = [];
                $totalDebit = 0;
                $totalCredit = 0;

                foreach ($calculatedEntries as $index => $calcEntry) {
                    $orig = $originalEntries[$index] ?? null;

                    $debitVal = isset($calcEntry['debit']) ? (float)$calcEntry['debit'] : 0.0;
                    $creditVal = isset($calcEntry['credit']) ? (float)$calcEntry['credit'] : 0.0;

                    $totalDebit += $debitVal;
                    $totalCredit += $creditVal;

                    $finalEntries[] = [
                        'type' => $orig ? $orig['type'] : ($debitVal > 0 ? 'Dr' : 'Cr'),
                        'account' => $orig ? $orig['account'] : 'Unknown',
                        'gl_code' => $calcEntry['gl_code'] ?? ($orig ? $orig['gl_code'] : ''),
                        'sequence' => $calcEntry['sequence'] ?? ($orig ? $orig['sequence'] : ($index + 1)),
                        'debit' => $debitVal,
                        'credit' => $creditVal,
                        'summary' => $orig && isset($orig['summary']) ? $orig['summary'] : '',
                    ];
                }

                // Check if already completed in odoo_jv_logs
                $queryLog = \DB::table('odoo_jv_logs')
                    ->where('application_id', $applicationId)
                    ->where('step_key', $step['id'])
                    ->where('status', 'success');

                if ($step['id'] !== 'disbursement_pv') {
                    $queryLog->where('month', $month)->where('year', $year);
                }

                $isCompleted = $queryLog->exists();

                $isInitiated = false;
                $execStatusText = 'Pending';
                $execQuery = \App\OdooExecution::where('application_id', $applicationId)
                    ->where('step_key', $step['id']);
                if ($step['id'] !== 'disbursement_pv') {
                    $execQuery->where('month', $month)->where('year', $year);
                }
                $latestExec = $execQuery->orderBy('id', 'desc')->first();
                if ($latestExec) {
                    if ($latestExec->odoo_status === 'pushed') {
                        $isCompleted = true;
                    } elseif ($latestExec->l1_status !== 'rejected' && $latestExec->l2_status !== 'rejected') {
                        $isInitiated = true;
                        if ($latestExec->odoo_status === 'ready') {
                            $execStatusText = 'Ready for Odoo';
                        } elseif ($latestExec->l1_status === 'approved') {
                            $execStatusText = 'L2 Review Pending';
                        } else {
                            $execStatusText = 'L1 Review Pending';
                        }
                    }
                }

                $isMismatch = abs($totalDebit - $totalCredit) > 0.01;
                $isZero = ($totalDebit == 0 && $totalCredit == 0);

                // Hide step if both Debit and Credit total are zero
                if ($isZero) {
                    continue;
                }

                $calculatedSteps[] = [
                    'id'               => $step['id'],
                    'voucher_type'     => $step['voucher_type'],
                    'entries'          => $finalEntries,
                    'is_mismatch'      => $isMismatch,
                    'is_zero'          => false,
                    'is_completed'     => $isCompleted,
                    'is_initiated'     => $isInitiated,
                    'execution_status' => $execStatusText,
                    'total_debit'      => $totalDebit,
                    'total_credit'     => $totalCredit,
                    'diff'             => abs($totalDebit - $totalCredit)
                ];
            }

            if (!empty($calculatedSteps)) {
                $calculatedSections[] = [
                    'section' => $section['section'],
                    'steps' => $calculatedSteps
                ];
            }
        }

        return response()->json([
            'status' => 'success',
            'data' => $calculatedSections
        ]);
    }

    public function createJv()
    {
        try {
            $user = Auth::user();
            $userOdooRole = $user ? $user->odoo_role : null;
            $isMaker = $user && $user->isOdooMaker();

            if (!$isMaker) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Unauthorized: Only an assigned Odoo Maker can initiate JV entries.'
                ], 403);
            }

            $applicationId = $this->request->get('application_id');
            $stepKey = $this->request->get('step_key');
            $month = $this->request->get('month') ?: Carbon::now()->format('m');
            $year = $this->request->get('year') ?: Carbon::now()->format('Y');
            $this->systemSlug = $this->request->get('system', 'da') ?: 'da';
            $partnerId = $this->request->get('partner_id'); // NEW: Accept partner ID

            if (!$applicationId || !$stepKey) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'application_id and JV are required'
                ], 400);
            }

            $application = \App\DaSystemData::with('partner')->find($applicationId);
            if (!$application) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'DA System record not found'
                ], 404);
            }

            // Check if already completed in odoo_jv_logs
            $queryCheck = \DB::table('odoo_jv_logs')
                ->where('application_id', $applicationId)
                ->where('step_key', $stepKey)
                ->where('status', 'success');

            if ($stepKey !== 'disbursement_pv') {
                $queryCheck->where('month', $month)->where('year', $year);
            }

            if ($queryCheck->exists()) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'JV entry has already been successfully created for this period.'
                ], 400);
            }

            // Check if active execution already exists in Maker-Checker queue
            $execCheck = \App\OdooExecution::where('application_id', $applicationId)
                ->where('step_key', $stepKey)
                ->where('l1_status', '!=', 'rejected')
                ->where('l2_status', '!=', 'rejected');

            if ($stepKey !== 'disbursement_pv') {
                $execCheck->where('month', $month)->where('year', $year);
            }

            $existingExec = $execCheck->first();
            if ($existingExec) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'This JV entry has already been initiated for Maker-Checker review. Please view it at /admin/odoo/execution.'
                ], 400);
            }

            $partner = $application->partner;
            $partnerName = $application->member_name ?? '';
            $partnerUniqueCode = strtoupper(substr(trim($application->member_name ?? 'CUST'), 0, 3)) . '-' . $application->id;
            
            if ($partner) {
                $partnerName = $partner->name ?? ($application->member_name ?? '');
                $partnerUniqueCode = strtoupper(substr(trim($partner->name), 0, 3)) . '-' . $partner->id;
            }

            $loan_data = $this->fetchLoanData($application->loan_no, $year, $month);

            $lines = $this->getStepsWithCalculation($stepKey, [
                'applicationId'     => $applicationId,
                'partnerName'       => $partnerName,
                'partnerUniqueCode' => $partnerUniqueCode,
                'application'       => $application,
                'loan_data'         => $loan_data
            ], $partnerId); // Pass partner ID

            // Retrieve section and step details (with partner-specific config if available)
            $stepConfig = null;
            $system = OdooSystem::where('slug', $this->systemSlug)->first();
            
            if ($system && $partnerId) {
                // Try partner-specific config first
                $stepConfig = OdooJvStep::where('step_key', $stepKey)
                    ->where('partner_id', $partnerId)
                    ->where('partner_type', $this->systemSlug)
                    ->with('section', 'entries')
                    ->first();
            }
            
            // Fall back to system default if no partner config
            if (!$stepConfig) {
                $stepConfig = OdooJvStep::where('step_key', $stepKey)
                    ->whereNull('partner_id')
                    ->with('section', 'entries')
                    ->first();
            }
            
            $sectionName = ($stepConfig && $stepConfig->section) ? $stepConfig->section->name : 'General';
            $voucherType = $stepConfig ? $stepConfig->voucher_type : 'Journal Voucher';

            $businessName = $system ? $system->name : strtoupper($this->systemSlug);

            // NEW: Get partner name from partner_data table if provided
            $jvPartnerName = null;
            if ($partnerId && $system && $system->partner_table) {
                $jvPartner = \DB::table($system->partner_table)->where('id', $partnerId)->first();
                if ($jvPartner) {
                    $jvPartnerName = $jvPartner->name;
                    
                    // Format: "Parent → Sub" if it's a sub-partner
                    if (!empty($jvPartner->sub_partner_id)) {
                        $parentPartner = \DB::table($system->partner_table)->where('id', $jvPartner->sub_partner_id)->first();
                        if ($parentPartner) {
                            $jvPartnerName = $parentPartner->name . ' → ' . $jvPartner->name;
                        }
                    }
                }
            }

            $origEntries = ($stepConfig && $stepConfig->entries) ? $stepConfig->entries->keyBy('sequence') : collect();
            $totalDebit = 0.0;
            $totalCredit = 0.0;
            $enrichedLines = [];

            foreach ($lines as $idx => $line) {
                $seq = $line['sequence'] ?? ($idx + 1);
                $orig = isset($origEntries[$seq]) ? $origEntries[$seq] : null;
                $dVal = isset($line['debit']) ? (float)$line['debit'] : 0.0;
                $cVal = isset($line['credit']) ? (float)$line['credit'] : 0.0;
                $totalDebit += $dVal;
                $totalCredit += $cVal;

                $enrichedLines[] = [
                    'sequence'            => $seq,
                    'type'                => $line['type'] ?? ($orig ? $orig->type : ($dVal > 0 ? 'Dr' : 'Cr')),
                    'account'             => $orig ? $orig->account : ($line['account'] ?? 'Unknown Account'),
                    'gl_code'             => $line['gl_code'] ?? ($orig ? $orig->gl_code : ''),
                    'reference'           => $line['reference'] ?? ('REF' . $applicationId),
                    'partner_name'        => $line['partner_name'] ?? $partnerName,
                    'partner_unique_code' => $line['partner_unique_code'] ?? $partnerUniqueCode,
                    'debit'               => $dVal,
                    'credit'              => $cVal,
                    'summary'             => $orig ? $orig->summary : ($line['summary'] ?? ''),
                ];
            }

            if ($totalDebit == 0 && $totalCredit == 0) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Cannot initiate: Total Debit and Credit amounts are zero.'
                ], 400);
            }

            if (abs($totalDebit - $totalCredit) > 0.01) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Cannot initiate: Total Debit (' . number_format($totalDebit, 2) . ') and Credit (' . number_format($totalCredit, 2) . ') do not balance.'
                ], 400);
            }

            $user = Auth::user();
            $makerId = $user ? $user->id : 107;
            $makerName = $user ? ($user->name ?? $user->email) : 'Maker Admin';

            $custName = $application->member_name;
            if (!$custName && $partner) {
                $custName = $partner->name;
            }
            if (!$custName) {
                $custName = 'Loan #' . ($application->loan_no ?? $applicationId);
            }

            $txNo = "TXN_" . date('YmdHis') . "_" . $stepKey . "_" . $applicationId;

            // Create execution record in maker-checker queue
            $execution = \App\OdooExecution::create([
                'application_id'     => $applicationId,
                'loan_no'            => $application->loan_no ?? (string)$applicationId,
                'customer_name'      => $custName,
                'business_name'      => $businessName,
                'system_slug'        => $this->systemSlug,
                'partner_id'         => $partnerId, // NEW: Store partner ID
                'partner_name'       => $jvPartnerName, // NEW: Store partner name
                'transaction_type'   => $sectionName,
                'step_key'           => $stepKey,
                'voucher_type'       => $voucherType,
                'month'              => $month,
                'year'               => $year,
                'total_debit'        => $totalDebit,
                'total_credit'       => $totalCredit,
                'amount'             => $totalDebit,
                'entries_json'       => json_encode($enrichedLines),
                'maker_id'           => $makerId,
                'maker_name'         => $makerName,
                'initiated_at'       => Carbon::now(),
                'l1_status'          => 'pending',
                'l2_status'          => 'pending',
                'odoo_status'        => 'pending',
                'transaction_number' => $txNo,
            ]);

            return response()->json([
                'status'       => 'success',
                'message'      => 'JV successfully initiated by Maker! The entry has landed on /admin/odoo/execution for Checker review.',
                'execution_id' => $execution->id,
            ]);

        } catch (\Exception $e) {
            Log::error('Odoo createJv Exception: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);

            return response()->json([
                'status'  => 'error',
                'message' => 'An internal error occurred: ' . $e->getMessage(),
            ], 500);
        }
    }


    public function getStepsWithCalculation($stepKey, $data, $partnerId = null){
        $this->creditAmountStepKey[$stepKey] = 0;
        $entries = $this->getJvSteps($stepKey, $partnerId); // Pass partner ID

        $lines = array_map(function ($entry) use ($data, $stepKey) {
            $line = [
                "sequence"            => $entry['sequence'],
                "gl_code"             => $entry['gl_code'],
                "reference"           => "REF" . $data['applicationId'],
                "partner_name"        => $data['partnerName'],
                "partner_unique_code" => $data['partnerUniqueCode']
            ];

            $line = array_merge($line, $this->calculateJvAmount(
                $stepKey,
                $entry['gl_code'],
                $data['application'],
                $data['loan_data'],
                $entry['type'] ?? 'Dr',
                $entry['amount_field'] ?? null,
                $entry['is_balancing_credit'] ?? false
            ));

            return $line;
        }, $entries);

        return $lines;
    }

    /**
     * Calculate JV amount for an entry using the data-driven amount_field.
     *
     * @param  string      $stepKey
     * @param  string      $glCode
     * @param  mixed       $application
     * @param  array       $loanData
     * @param  string      $type          'Dr' | 'Cr'
     * @param  string|null $amountField   free-text field name (e.g. 'disbursement_amount')
     * @param  bool        $isBalancingCredit
     */
    private function calculateJvAmount(
        $stepKey,
        $glCode,
        $application,
        $loanData,
        $type = 'Dr',
        $amountField = null,
        $isBalancingCredit = false
    ) {
        // ── Balancing credit: returns the accumulated Dr total for this step ──
        if ($isBalancingCredit) {
            if ($type === 'Cr') {
                $amount = $this->creditAmountStepKey[$stepKey] ?? 0.0;
                return ['credit' => (float)$amount, 'debit' => 0.0];
            }
            return ['credit' => 0.0, 'debit' => 0.0];
        }

        $amount = 0.0;

        // ── Data-driven: resolve amount_field from application or loanData[0] ─
        if ($amountField) {
            // Try application model first
            if (isset($application->{$amountField})) {
                $amount = (float)$application->{$amountField};
            }
            // Then try first loan data row
            elseif (!empty($loanData) && isset($loanData[0]->{$amountField})) {
                $amount = (float)$loanData[0]->{$amountField};
            }
        }

        // ── Special fallback: disbursement_amount can fall back to principal_total ─
        if ($amountField === 'disbursement_amount' && $amount == 0.0) {
            $amount = (float)($application->principal_total ?? 0);
        }

        if ($amount > 0) {
            $this->updateCreditAmountThroughStepKey($stepKey, $amount);
        }

        if ($type === 'Dr') {
            return ['debit' => $amount, 'credit' => 0.0];
        } else {
            return ['credit' => $amount, 'debit' => 0.0];
        }
    }

    public function updateCreditAmountThroughStepKey($stepKey, $amount){
        if(isset($this->creditAmountStepKey[$stepKey])){
            $this->creditAmountStepKey[$stepKey] += $amount;
        }else{
            $this->creditAmountStepKey[$stepKey] = $amount;
        }
    }

    private function fetchLoanData($loanNo, $month, $year)
    {
        if (!$loanNo) {
            return [];
        }
        
        $query = "with payment_agg as (
    select
        loan_id,
        DATE_FORMAT(payment_date, '%Y-%m') AS payment_month_year,
        SUM(CASE WHEN payment_for = 'principal' THEN amount ELSE 0 END) AS principal_received,
        SUM(CASE WHEN payment_for = 'interest' THEN amount ELSE 0 END) AS interest_received,
        SUM(CASE WHEN payment_for = 'overdue_principal' THEN amount ELSE 0 END) AS overdue_principal_received,
        SUM(CASE WHEN payment_for = 'overdue_interest' THEN amount ELSE 0 END) AS overdue_interest_received,
        SUM(CASE WHEN payment_for = 'penal_charges' THEN amount ELSE 0 END) AS penal_charges_received

    from blacksoil_payment_transactions
    WHERE (is_deleted_at IS NULL AND deleted_at IS NULL AND refunded_at IS NULL) 
    AND payment_for in ('principal','interest','overdue_principal','overdue_interest','penal_charges')
    GROUP BY loan_id, cust_id, DATE_FORMAT(payment_date, '%Y-%m')
)

,bep as (
    select loan_id, DATE_FORMAT(emi_date, '%Y-%m') AS emi_month_year, interest_due
    from blacksoil_emi_payments
    where blacksoil_emi_payments.deleted_at is null
)



select bep.loan_id, interest_due, 
interest_received + overdue_interest_received total_interest_received,
principal_received + overdue_principal_received total_principal_received,
principal_received + interest_received + overdue_interest_received + overdue_principal_received total_emi_received, 
penal_charges_received

from bep
left join payment_agg on payment_agg.loan_id = bep.loan_id and payment_agg.payment_month_year = bep.emi_month_year
where bep.loan_id = '".$loanNo."' and bep.emi_month_year = '".$year."-".$month."'";

        return \DB::select($query);
    }


    public function createCustomer()
    {
        try {
            $partnerId = $this->request->get('partner_id');
            if (!$partnerId) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'partner_id is required'
                ], 400);
            }

            $odooDetail = \App\OdooDetail::firstOrNew(['partner_id' => $partnerId]);
            if ($odooDetail->status === 'success' || $odooDetail->status === 'Customer already exists') {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Customer is already created in Odoo.'
                ], 400);
            }

            $partner = PartnerData::with('parent')->find($partnerId);

            if (!$partner) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Partner not found'
                ], 404);
            }

            $payload = $this->mapPartnerToOdooPayload($partner);

            $odooService = new \App\Odoo\OdooService();
            $apiResult = $odooService->createCustomer($payload);

            if ($apiResult['http_code'] === 500 && isset($apiResult['message'])) {
                return response()->json([
                    'status' => 'error',
                    'message' => $apiResult['message'],
                    'error' => null
                ], 500);
            }

            $statusCode = $apiResult['http_code'];
            $responseData = is_array($apiResult['response']) ? $apiResult['response'] : json_decode($apiResult['response'], true);
            
            // Reassign $body for fallback error response later
            $body = is_string($apiResult['response']) ? $apiResult['response'] : json_encode($apiResult['response']);
            $odooDetail->api_response = $body;
            $odooDetail->created_by = Auth::user()->id ?? 107;

            if ($statusCode >= 200 && $statusCode < 300) {
                $odooDetail->status = 'success';
                if (isset($responseData['customer']['customer_id'])) {
                    $odooDetail->odoo_customer_id = $responseData['customer']['customer_id'];
                }
                $odooDetail->save();

                return response()->json([
                    'status' => 'success',
                    'message' => 'Successfully created customer in Odoo',
                    'data' => $responseData,
                ], $statusCode);
            } else {
                if (isset($responseData['message']) && $responseData['message'] === 'Customer already exists') {
                    $odooDetail->status = 'Customer already exists';
                } else {
                    $odooDetail->status = 'failed';
                }
                $odooDetail->save();

                $errorMsg = 'Failed to create customer in Odoo';
                if (is_array($responseData) && isset($responseData['message'])) {
                    $errorMsg = $responseData['message'];
                }

                return response()->json([
                    'status' => 'error',
                    'message' => $errorMsg,
                    'error' => $responseData ?? $body,
                ], $statusCode);
            }

        } catch (\Exception $e) {
            Log::error('Odoo createCustomer Exception: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);

            return response()->json([
                'status' => 'error',
                'message' => 'An internal error occurred',
                'error' => $e->getMessage(),
            ], 500);
        }
    }

    private function mapPartnerToOdooPayload(PartnerData $partner)
    {
        $payload = $this->buildPartnerData($partner);

        // Map parent if exists
        if ($partner->parent) {
            $payload['parent_company'] = $this->buildPartnerData($partner->parent);
        } else {
            $payload['parent_company'] = new \stdClass();
        }

        return $payload;
    }

    private function buildPartnerData(PartnerData $partner)
    {
        $uniqueCode = strtoupper(substr(trim($partner->name), 0, 3)) . '-' . $partner->id;

        return [
            "name"                => $partner->name ?? "",
            "phone"               => $partner->mobile ?? "",
            "email"               => $partner->email ?? "",
            "vat"                 => $partner->gstn ?? "",
            "pan"                 => $partner->pan ?? "",
            "tan"                 => $partner->tan ?? "",
            "gst_treatment"       => "regular",
            "company_name"        => $partner->company ?? "",
            "contact_type"        => "customer",
            "partner_unique_code" => $uniqueCode,
            "product"             => $partner->product ?? "",
            "source"              => $partner->source ?? "CreadFair", // Fallback to CreadFair if null
            "segment"             => $partner->industry ?? "Udhyam", // Mapped from industry, defaulting to Udhyam
            "address" => [
                "street"  => $partner->gst_address ?? "",
                "street2" => "",
                "city"    => $partner->city ?? "",
                "state"   => $partner->state ?? "",
                "country" => "India",
                "zip"     => $partner->zip ?? ""
            ]
        ];
    }

    /**
     * Get partners for a specific system
     * GET /admin/odoo/systems/{systemSlug}/partners
     * 
     * @param string $systemSlug
     * @return \Illuminate\Http\JsonResponse
     */
    public function getSystemPartners($systemSlug)
    {
        $system = OdooSystem::where('slug', $systemSlug)
            ->where('is_active', true)
            ->first();

        if (!$system) {
            return response()->json([
                'status' => 'error',
                'message' => 'System not found'
            ], 404);
        }

        $partners = $system->getPartners()->map(function($partner) {
            $displayName = $partner->name;
            $isSubPartner = !empty($partner->sub_partner_id);
            
            // Format: "Parent Name → Sub Partner Name" for sub-partners
            if ($isSubPartner) {
                $parentName = \DB::table('partner_data')
                    ->where('id', $partner->sub_partner_id)
                    ->value('name');
                if ($parentName) {
                    $displayName = $parentName . ' → ' . $partner->name;
                }
            }
            
            return [
                'id' => $partner->id,
                'name' => $partner->name,
                'is_sub_partner' => $isSubPartner,
                'parent_id' => $partner->sub_partner_id,
                'display_name' => $displayName
            ];
        });

        return response()->json([
            'status' => 'success',
            'data' => $partners
        ]);
    }

    /**
     * Clone system config to partner-specific config
     * POST /admin/odoo/config/clone-to-partner
     * 
     * @return \Illuminate\Http\JsonResponse
     */
    public function cloneConfigToPartner()
    {
        try {
            $this->request->validate([
                'system_slug' => 'required|string',
                'partner_id' => 'required|integer',
            ]);

            $systemSlug = $this->request->input('system_slug');
            $partnerId = $this->request->input('partner_id');

            $system = OdooSystem::where('slug', $systemSlug)->first();
            
            if (!$system) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'System not found'
                ], 404);
            }

            // Check if partner already has config
            $existingConfig = OdooJvSection::where('system_id', $system->id)
                ->where('partner_id', $partnerId)
                ->where('partner_type', $systemSlug)
                ->exists();

            if ($existingConfig) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Partner already has a custom configuration. Please delete it first to clone again.'
                ], 400);
            }

            // Clone system-level sections to partner-level
            $systemSections = OdooJvSection::where('system_id', $system->id)
                ->whereNull('partner_id')
                ->with('steps.entries')
                ->get();

            if ($systemSections->isEmpty()) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'No system default configuration found to clone.'
                ], 404);
            }

            $now = \Carbon\Carbon::now();
            $clonedCount = 0;

            foreach ($systemSections as $section) {
                $newSection = $section->replicate();
                $newSection->partner_id = $partnerId;
                $newSection->partner_type = $systemSlug;
                $newSection->created_at = $now;
                $newSection->updated_at = $now;
                $newSection->save();

                foreach ($section->steps as $step) {
                    $newStep = $step->replicate();
                    $newStep->section_id = $newSection->id;
                    $newStep->partner_id = $partnerId;
                    $newStep->partner_type = $systemSlug;
                    $newStep->created_at = $now;
                    $newStep->updated_at = $now;
                    $newStep->save();

                    foreach ($step->entries as $entry) {
                        $newEntry = $entry->replicate();
                        $newEntry->step_id = $newStep->id;
                        $newEntry->created_at = $now;
                        $newEntry->updated_at = $now;
                        $newEntry->save();
                    }
                }

                $clonedCount++;
            }

            return response()->json([
                'status' => 'success',
                'message' => "Configuration cloned successfully! {$clonedCount} section(s) copied for partner."
            ]);

        } catch (\Exception $e) {
            Log::error('Odoo cloneConfigToPartner Exception: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);

            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while cloning configuration: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Returns JV steps configuration from the database for the current system.
     * Supports partner-specific configurations with fallback to system defaults.
     *
     * When $stepKey is provided:
     *   - Returns the flat array of entries for that step (or [] if disabled/not found).
     * When $stepKey is empty:
     *   - Returns all sections with their enabled steps (mirroring old hardcoded format).
     * 
     * @param string $stepKey
     * @param int|null $partnerId
     * @return array
     */
    public function getJvSteps($stepKey = '', $partnerId = null)
    {
        $system = OdooSystem::where('slug', $this->systemSlug)
                            ->where('is_active', true)
                            ->first();

        if (!$system) {
            // No system configured — return empty to avoid errors
            return $stepKey ? [] : [];
        }

        // Try partner-specific config first if partnerId provided
        $sections = null;
        if ($partnerId) {
            $sections = OdooJvSection::where('system_id', $system->id)
                ->where('partner_id', $partnerId)
                ->where('partner_type', $this->systemSlug)
                ->with(['steps' => function($q) use ($partnerId) {
                    $q->where('partner_id', $partnerId)
                      ->orderBy('sort_order');
                }, 'steps.entries' => function($q) {
                    $q->orderBy('sequence');
                }])
                ->orderBy('sort_order')
                ->get();
        }

        // Fall back to system default if no partner config or no partner provided
        if (!$sections || $sections->isEmpty()) {
            $sections = OdooJvSection::where('system_id', $system->id)
                ->whereNull('partner_id')
                ->with(['steps' => function($q) {
                    $q->whereNull('partner_id')
                      ->orderBy('sort_order');
                }, 'steps.entries' => function($q) {
                    $q->orderBy('sequence');
                }])
                ->orderBy('sort_order')
                ->get();
        }

        if ($stepKey) {
            // Find the step across all sections
            foreach ($sections as $section) {
                foreach ($section->steps as $step) {
                    if ($step->step_key === $stepKey) {
                        if (!$step->is_enabled) {
                            return [];
                        }
                        // Return entries as plain arrays matching old format
                        return $step->entries->map(function($e) {
                            return [
                                'sequence'            => $e->sequence,
                                'type'                => $e->type,
                                'account'             => $e->account,
                                'gl_code'             => $e->gl_code,
                                'summary'             => $e->summary,
                                'amount_field'        => $e->amount_field,
                                'is_balancing_credit' => (bool)$e->is_balancing_credit,
                            ];
                        })->toArray();
                    }
                }
            }
            return [];
        }

        // Return all enabled steps grouped by section (old format for getJvStepsAjax)
        $result = [];
        foreach ($sections as $section) {
            $enabledSteps = $section->steps->filter(function($s) {
                return $s->is_enabled;
            })->values();
            if ($enabledSteps->isEmpty()) {
                continue;
            }

            $stepsArr = $enabledSteps->map(function($step) {
                return [
                    'id'           => $step->step_key,
                    'voucher_type' => $step->voucher_type,
                    'disable_step' => false,
                    'entries'      => $step->entries->map(function($e) {
                        return [
                            'type'                => $e->type,
                            'account'             => $e->account,
                            'gl_code'             => $e->gl_code,
                            'sequence'            => $e->sequence,
                            'summary'             => $e->summary,
                            'amount_field'        => $e->amount_field,
                            'is_balancing_credit' => (bool)$e->is_balancing_credit,
                        ];
                    })->toArray(),
                ];
            })->toArray();

            $result[] = [
                'section' => $section->name,
                'steps'   => $stepsArr,
            ];
        }

        return $result;
    }

    public function getSystemData($applicationId, $data){
        $application = \App\DaSystemData::with('partner')->find($applicationId);
        if (!$application) {
            return response()->json([
                'status' => 'error',
                'message' => 'DA System record not found'
            ], 404);
        }

        $partner = $application->partner;
        $partnerName = $application->member_name ?? '';
        $partnerUniqueCode = strtoupper(substr(trim($application->member_name ?? 'CUST'), 0, 3)) . '-' . $application->id;
        
        if ($partner) {
            $partnerName = $partner->name ?? ($application->member_name ?? '');
            $partnerUniqueCode = strtoupper(substr(trim($partner->name), 0, 3)) . '-' . $partner->id;
        }

        $month = $data['month'];
        $year = $data['year'];

        $loan_data = $this->fetchLoanData($application->loan_no, $data['year'], $data['month']);

        $data = [
            'applicationId' => $applicationId,
            'partnerName' => $partnerName,
            'partnerUniqueCode' => $partnerUniqueCode,
            'application' => $application,
            'loan_data' => $loan_data,
            'month' => $data['month'],
            'year' => $data['year']
        ];

        return $data;
    }

    public function getCreditfairSystemData($applicationId){
        $application = \App\DaSystemData::with('partner')->find($applicationId);
        if (!$application) {
            return response()->json([
                'status' => 'error',
                'message' => 'DA System record not found'
            ], 404);
        }

        $partner = $application->partner;
        $partnerName = $application->member_name ?? '';
        $partnerUniqueCode = strtoupper(substr(trim($application->member_name ?? 'CUST'), 0, 3)) . '-' . $application->id;
        
        if ($partner) {
            $partnerName = $partner->name ?? ($application->member_name ?? '');
            $partnerUniqueCode = strtoupper(substr(trim($partner->name), 0, 3)) . '-' . $partner->id;
        }

        $loan_data = $this->fetchLoanData($application->loan_no, $data['year'], $data['month']);

        $data = [
            'applicationId' => $applicationId,
            'partnerName' => $partnerName,
            'partnerUniqueCode' => $partnerUniqueCode,
            'application' => $application,
            'loan_data' => $loan_data,
            'month' => $data['month'],
            'year' => $data['year']
        ];

        return $data;
    }

    public function getCoLendingSystemData($applicationId){
        $application = \App\DaSystemData::with('partner')->find($applicationId);
        if (!$application) {
            return response()->json([
                'status' => 'error',
                'message' => 'DA System record not found'
            ], 404);
        }

        $partner = $application->partner;
        $partnerName = $application->member_name ?? '';
        $partnerUniqueCode = strtoupper(substr(trim($application->member_name ?? 'CUST'), 0, 3)) . '-' . $application->id;
        
        if ($partner) {
            $partnerName = $partner->name ?? ($application->member_name ?? '');
            $partnerUniqueCode = strtoupper(substr(trim($partner->name), 0, 3)) . '-' . $partner->id;
        }

        $loan_data = $this->fetchLoanData($application->loan_no, $data['year'], $data['month']);

        $data = [
            'applicationId' => $applicationId,
            'partnerName' => $partnerName,
            'partnerUniqueCode' => $partnerUniqueCode,
            'application' => $application,
            'loan_data' => $loan_data,
            'month' => $data['month'],
            'year' => $data['year']
        ];

        return $data;
    }
}
