<?php

namespace App\Controllers;

use CodeIgniter\Controller;
use App\Models\UndepositedFundsModel;
use App\Libraries\undeposited_funds\UndepositedFundsLibrary;
use App\Libraries\undeposited_funds\NewLibrary;
use App\Libraries\undeposited_funds\PullSalesReceiptsAndJournalEntries;

class UndepositedFunds extends Controller
{
    public function index()
    {
        if (ENVIRONMENT === 'development') {
            cache()->clean();
        }

        if (!service('auth')->loggedIn()) {
            return redirect()->to('/login');
        }
        $user = service('auth')->user();
        if ((int) $user->is_admin !== 1) {
            return redirect()->to('/')->with('error', 'Access denied: Admins only.');
        }

        $session     = session();
        $realmId     = $session->get('realmid');
        $accessToken = $session->get('access_token');
        $isConnected = ($realmId && $accessToken);

        $req      = service('request');
        $fromDate = $req->getGetPost('from_date');
        $toDate   = $req->getGetPost('to_date');
        $action   = (string) $req->getPost('action');

        $firstOfMonth = date('Y-m-01');
        $today        = date('Y-m-d');
        if (empty($fromDate)) {
            $fromDate = $firstOfMonth;
        }
        if (empty($toDate)) {
            $toDate = $today;
        }

        $pullHelper = new NewLibrary();
        $errMsg     = $pullHelper->pullAndCrossReference(
            $action,
            $isConnected,
            $fromDate,
            $toDate,
            $realmId,
            $accessToken
        );
        if ($errMsg !== null) {
            $error = $errMsg;
        }

        if ($action === 'fetch_sales_receipts' && $isConnected && $fromDate && $toDate) {
            $pullLibErr = (new PullSalesReceiptsAndJournalEntries())
                ->fetchSalesReceipts($realmId, $accessToken, $fromDate, $toDate);
            if ($pullLibErr !== null) {
                $error = $pullLibErr;
            }
        }

        if ($action === 'fetch_journal_entries' && $isConnected && $fromDate && $toDate) {
            $pullLibErr = (new PullSalesReceiptsAndJournalEntries())
                ->fetchJournalEntries($realmId, $accessToken, $fromDate, $toDate);
            if ($pullLibErr !== null) {
                $error = $pullLibErr;
            }
        }

        $ufModel  = new UndepositedFundsModel();
        $limitVal = $req->getGetPost('limit') ?: '100';
        $all      = ($limitVal === 'ALL');
        $results  = [];
        $pager    = null;

        if ($all) {
            $results = $ufModel->where('txn_date >=', $fromDate)
                               ->where('txn_date <=', $toDate)
                               ->orderBy('txn_date', 'ASC')
                               ->findAll();
        } else {
            $perPage = (int) $limitVal;
            if ($perPage < 1) {
                $perPage = 100;
            }
            $ufModel->where('txn_date >=', $fromDate)
                    ->where('txn_date <=', $toDate)
                    ->orderBy('txn_date', 'ASC');
            $results = $ufModel->paginate($perPage, 'ufGroup');
            $pager   = $ufModel->pager;
        }

        $data = [
            'isConnected' => $isConnected,
            'error'       => $error ?? null,
            'from_date'   => $fromDate,
            'to_date'     => $toDate,
            'results'     => $results,
            'pager'       => $pager,
            'limitVal'    => $limitVal,
        ];

        return view('undepositedfunds/index', $data);
    }

    public function loadView()
    {
        $req     = service('request');
        $pane    = $req->getGet('pane');
        $view    = $req->getGet('view');
        $fresh   = $req->getGet('fresh') ?? '0';
        $session = session();

        if ($pane === 'left') {
            if ($view === 'make_bank_deposit') {
                return view('undepositedfunds/left/make_bank_deposit');
            }
            return view('undepositedfunds/left/main_view_selector');
        }

        $realmId     = $session->get('realmid');
        $accessToken = $session->get('access_token');
        $isConnected = ($realmId && $accessToken);

        $limitVal = $req->getGet('limit') ?: '100';
        $all      = ($limitVal === 'ALL');
        $pager    = null;

        if ($view === 'make_bank_deposit') {
            $fromDate = $req->getGet('from_date');
            $toDate   = $req->getGet('to_date');
        } else {
            $fromDate = $req->getGet('from_date') ?? date('Y-m-01');
            $toDate   = $req->getGet('to_date')   ?? date('Y-m-d');
        }

        if ($view !== 'make_bank_deposit') {
            $session->remove('transaction_ids_selected');
        }

        $data = [
            'isConnected' => $isConnected,
            'error'       => '',
            'from_date'   => $fromDate,
            'to_date'     => $toDate,
            'limitVal'    => $limitVal,
            'pager'       => null,
            'results'     => [],
        ];

        if ($view === 'pull') {
            return view('undepositedfunds/middle/pull_undeposited_funds', $data);
        }

        if ($view === 'make_bank_deposit') {
            if ($fresh === '1') {
                $session->remove('transaction_ids_selected');
            }

            $ufModel       = new UndepositedFundsModel();
            $customerNames = $ufModel->getDistinctCustomers();
            $typeOptions   = $ufModel->getDistinctTxnTypes();

            $customerFilter = $req->getGet('customer') ?? '';
            if ($customerFilter !== '') {
                $ufModel->groupStart()
                        ->where('customer_name', $customerFilter)
                        ->orWhere('je_line_name', $customerFilter)
                        ->groupEnd();
            }
            $typeFilter = $req->getGet('type') ?? '';
            if ($typeFilter !== '') {
                $ufModel->where('txn_type', $typeFilter);
            }
            $memoFilter = $req->getGet('memo') ?? '';
            if ($memoFilter !== '') {
                $ufModel->groupStart()
                        ->like('memo', $memoFilter)
                        ->orLike('je_description', $memoFilter)
                        ->groupEnd();
            }
            $referenceFilter = $req->getGet('reference') ?? '';
            if ($referenceFilter !== '') {
                $ufModel->like('reference_number', $referenceFilter);
            }
            $amountFilter = $req->getGet('amount') ?? '';
            if ($amountFilter !== '') {
                $amt = (float) $amountFilter;
                $abs = abs($amt);
                $ufModel->groupStart()
                        ->where('amount', $amt)
                        ->orWhere('debit_amount', $abs)
                        ->orWhere('credit_amount', $abs)
                        ->groupEnd();
            }
            $fromFilter = $req->getGet('from_date') ?? '';
            if ($fromFilter !== '') {
                $ufModel->where('txn_date >=', $fromFilter);
            }
            $toFilter = $req->getGet('to_date') ?? '';
            if ($toFilter !== '') {
                $ufModel->where('txn_date <=', $toFilter);
            }

            if ($all) {
                $results = $ufModel->where('is_deposited', 0)
                                   ->orderBy('txn_date', 'ASC')
                                   ->findAll();
            } else {
                $perPage = (int) $limitVal;
                if ($perPage < 1) {
                    $perPage = 100;
                }
                $ufModel->where('is_deposited', 0)->orderBy('txn_date', 'ASC');
                $results = $ufModel->paginate($perPage, 'mbdGroup');
                $pager   = $ufModel->pager;
            }

            $data['results']           = $results;
            $data['pager']             = $pager;
            $data['customerNames']     = $customerNames;
            $data['typeOptions']       = $typeOptions;
            $data['selectedCustomer']  = $customerFilter;
            $data['selectedType']      = $typeFilter;
            $data['selectedMemo']      = $memoFilter;
            $data['selectedReference'] = $referenceFilter;
            $data['selectedAmount']    = $amountFilter;
            $data['fromDate']          = $fromFilter;
            $data['toDate']            = $toFilter;

            return view('undepositedfunds/middle/make_bank_deposit', $data);
        }

        $ufModel = new UndepositedFundsModel();
        if ($all) {
            $results = $ufModel->where('txn_date >=', $fromDate)
                               ->where('txn_date <=', $toDate)
                               ->orderBy('txn_date', 'ASC')
                               ->findAll();
        } else {
            $perPage = (int) $limitVal;
            if ($perPage < 1) {
                $perPage = 100;
            }
            $ufModel->where('txn_date >=', $fromDate)
                    ->where('txn_date <=', $toDate)
                    ->orderBy('txn_date', 'ASC');
            $results = $ufModel->paginate($perPage, 'ufGroup');
            $pager   = $ufModel->pager;
        }

        $data['results'] = $results;
        $data['pager']   = $pager;

        return view('undepositedfunds/middle/manage_undeposited_funds', $data);
    }

    public function updateSelected()
    {
        if (!$this->request->isAJAX()) {
            return $this->response->setJSON(['success' => false, 'error' => 'AJAX only'])->setStatusCode(400);
        }

        $body    = $this->request->getJSON(true);
        $ufId    = $body['ufId']    ?? null;
        $checked = $body['checked'] ?? false;

        if (!$ufId) {
            return $this->response->setJSON(['success' => false, 'error' => 'No ufId'])->setStatusCode(400);
        }

        $session  = session();
        $selected = $session->get('transaction_ids_selected') ?? [];

        if ($checked) {
            if (!in_array($ufId, $selected)) {
                $selected[] = $ufId;
            }
        } else {
            $selected = array_values(array_filter($selected, fn($id) => $id != $ufId));
        }

        $session->set('transaction_ids_selected', $selected);

        return $this->response->setJSON(['success' => true]);
    }

    public function updateSelectedBulk()
    {
        if (!$this->request->isAJAX()) {
            return $this->response->setJSON(['success' => false, 'error' => 'AJAX only'])->setStatusCode(400);
        }

        $body    = $this->request->getJSON(true);
        $ids     = $body['ids']     ?? [];
        $checked = $body['checked'] ?? true;

        $session  = session();
        $selected = $session->get('transaction_ids_selected') ?? [];

        if ($checked) {
            $selected = array_values(array_unique(array_merge($selected, $ids)));
        } else {
            $selected = array_values(array_diff($selected, $ids));
        }

        $session->set('transaction_ids_selected', $selected);
        return $this->response->setJSON(['success' => true]);
    }

    public function createDeposit()
    {
        if (!$this->request->isAJAX()) {
            return $this->response->setStatusCode(400);
        }

        $session     = session();
        $selectedIds = $session->get('transaction_ids_selected') ?? [];
        if (empty($selectedIds)) {
            return $this->response->setJSON(['success' => false, 'error' => 'No transactions selected'])->setStatusCode(400);
        }

        $realmId     = $session->get('realmid');
        $accessToken = $session->get('access_token');
        if (!$realmId || !$accessToken) {
            return $this->response->setJSON(['success' => false, 'error' => 'Not connected to QBO'])->setStatusCode(400);
        }

        $db = db_connect();
        $rawRows = $db->table('undeposited_funds uf')
                      ->select('uf.*, utl.txn_line_id')
                      ->join('undeposited_funds_txn_lines utl', 'utl.uf_id = uf.id', 'left')
                      ->whereIn('uf.id', $selectedIds)
                      ->get()->getResultArray();

        $rows = [];
        foreach ($rawRows as $r) {
            $key = $r['id'];
            if (!isset($rows[$key])) {
                $rows[$key]                = $r;
                $rows[$key]['txn_line_ids'] = [];
            }
            if (!empty($r['txn_line_id'])) {
                $rows[$key]['txn_line_ids'][] = (string) $r['txn_line_id'];
            }
        }
        $rows = array_values($rows);

        $lib = new UndepositedFundsLibrary();
        try {
            $result = $lib->createDeposit($rows, $realmId, $accessToken);
        } catch (\Throwable $e) {
            /* Propagate full debug/error string to the frontend */
            return $this->response->setJSON([
                'success' => false,
                'error'   => $e->getMessage(),
            ])->setStatusCode(500);
        }
        $resp    = $result['response'] ?? [];
        $payload = $result['payload']  ?? [];

        $deposit = $resp['Deposit'] ?? null;
        if (!$deposit) {
            return $this->response->setJSON([
                'success' => false,
                'error'   => 'Invalid QBO response',
            ])->setStatusCode(500);
        }

        $depModel  = new \App\Models\DepositsMadeModel();
        $lineModel = new \App\Models\DepositsMadeLinesModel();
        $consoleModel = new \App\Models\ConsoleDataModel();

        $masterData = [
            'qb_deposit_id'                => $deposit['Id'] ?? '',
            'deposit_date'                 => $deposit['TxnDate'] ?? null,
            'doc_number'                   => $deposit['DocNumber'] ?? '',
            'deposit_to_account_ref_value' => $deposit['DepositToAccountRef']['value'] ?? '',
            'deposit_to_account_ref_name'  => $deposit['DepositToAccountRef']['name'] ?? '',
            'total_amt'                    => $deposit['TotalAmt'] ?? 0,
            'memo'                         => $deposit['PrivateNote'] ?? '',
        ];
        $depModel->insert($masterData);
        $masterId = $depModel->insertID();

        foreach ($deposit['Line'] ?? [] as $line) {
            $linked = $line['LinkedTxn'][0] ?? [];
            $lineModel->insert([
                'deposits_made_id' => $masterId,
                'line_num'         => $line['Id'] ?? '',
                'description'      => $line['Description'] ?? '',
                'amount'           => $line['Amount'] ?? 0,
                'linked_txn_type'  => $linked['TxnType'] ?? '',
                'linked_txn_id'    => $linked['TxnId'] ?? '',
            ]);
        }

        (new UndepositedFundsModel())->update($selectedIds, [
            'is_deposited'   => 1,
            'deposit_txn_id' => $deposit['Id'] ?? '',
            'deposit_date'   => $deposit['TxnDate'] ?? null,
        ]);

        // Save raw JSON of the created deposit for debugging purposes
        $consoleModel->insert([
            'context' => 'deposit',
            'data'    => json_encode($deposit)
        ]);

        $session->remove('transaction_ids_selected');

        return $this->response->setJSON([
            'success'   => true,
            'depositId' => $masterId,
            'payload'   => $payload,
            'deposit'   => $deposit,
        ]);
    }

    public function leftMakeBankDeposit()
    {
        return view('undepositedfunds/left/make_bank_deposit');
    }

    public function clearSelected()
    {
        session()->remove('transaction_ids_selected');
        return view('undepositedfunds/left/main_view_selector');
    }
}
