<?php

namespace App\Controllers;

use CodeIgniter\Controller;
use App\Models\UndepositedFundsModel;
use App\Models\DepositsMadeModel;

class UndepositedFunds extends Controller
{
    /** quick helper so we do the “contains Undeposited” test in one place */
    private function strContainsUndeposited(?string $s): bool
    {
        return $s && stripos($s, 'undeposited') !== false;
    }

    public function index()
    {
        //------------------------------------------------------------------
        // 0) ensure we’re always reading the *latest* .env (dev only)
        //------------------------------------------------------------------
        if (ENVIRONMENT === 'development') {
            // clears cached config & .env values in CI4
            cache()->clean();
        }

        //------------------------------------------------------------------
        // 1) auth gate
        //------------------------------------------------------------------
        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.');
        }

        //------------------------------------------------------------------
        // 2) QBO connection in session?
        //------------------------------------------------------------------
        $session     = session();
        $realmId     = $session->get('realmid');
        $accessToken = $session->get('access_token');
        $isConnected = ($realmId && $accessToken);

        //------------------------------------------------------------------
        // 3) read form
        //------------------------------------------------------------------
        $req      = service('request');
        $fromDate = $req->getGetPost('from_date');
        $toDate   = $req->getGetPost('to_date');
        $action   = $req->getPost('action');

        //------------------------------------------------------------------
        // 4) view vars
        //------------------------------------------------------------------
        $data = [
            'isConnected' => $isConnected,
            'error'       => null,
            'from_date'   => $fromDate ?? '',
            'to_date'     => $toDate   ?? '',
            'results'     => [],
        ];

        //------------------------------------------------------------------
        // 5) pull undeposited-funds set
        //------------------------------------------------------------------
        if ($action === 'pull_undeposited' && $isConnected && $fromDate && $toDate) {
            try {
                $m = new UndepositedFundsModel();

                // NEW approach for pulling Payments
                $this->pullPayments($realmId, $accessToken, $fromDate, $toDate, $m);

                // The rest remain unchanged:
                $this->pullSalesReceipts ($realmId, $accessToken, $fromDate, $toDate, $m);
                $this->pullRefundReceipts($realmId, $accessToken, $fromDate, $toDate, $m);
                $this->pullCreditMemos   ($realmId, $accessToken, $fromDate, $toDate, $m);
                $this->pullJournalEntries($realmId, $accessToken, $fromDate, $toDate, $m);

            } catch (\Throwable $e) {
                $data['error'] = 'Error while pulling data: ' . $e->getMessage();
            }
        }

        //------------------------------------------------------------------
        // 6) pull deposits (2-table system)
        //------------------------------------------------------------------
        if ($action === 'pull_deposits' && $isConnected && $fromDate && $toDate) {
            try {
                $this->pullDeposits($realmId, $accessToken, $fromDate, $toDate);
            } catch (\Throwable $e) {
                $data['error'] = 'Error while pulling deposits: ' . $e->getMessage();
            }
        }

        //------------------------------------------------------------------
        // 7) load grid rows
        //------------------------------------------------------------------
        if ($fromDate && $toDate) {
            $data['results'] = (new UndepositedFundsModel())
                                ->fetchByDateRange($fromDate, $toDate);
        }

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

    /* ──────────────────────────────────────────────────────────────
       Deposit header/line puller
       ──────────────────────────────────────────────────────────── */
    private function pullDeposits(string $realm, string $tok, string $from, string $to): void
    {
        $depModel = new DepositsMadeModel();
        $start = 1;  $max = 1000;

        do {
            $q = "
              SELECT *
              FROM Deposit
              WHERE TxnDate >= '{$from}' AND TxnDate <= '{$to}'
              ORDERBY Id
              STARTPOSITION {$start}
              MAXRESULTS {$max}";
            $rows  = $this->runQuery($realm, $tok, $q)['QueryResponse']['Deposit'] ?? [];
            foreach ($rows as $d) {
                $depModel->upsertDeposit($d);
            }
            $start += $max;
        } while (count($rows) === $max);
    }

    /* ──────────────────────────────────────────────────────────────
       Payment - NEW do/while approach
       ──────────────────────────────────────────────────────────── */
    private function pullPayments(string $realm, string $tok, string $from, string $to, UndepositedFundsModel $m)
    {
        $start = 1;  
        $max   = 1000;

        do {
            $q = "
              SELECT *
              FROM Payment
              WHERE TxnDate >= '{$from}' AND TxnDate <= '{$to}'
              ORDERBY Id
              STARTPOSITION {$start}
              MAXRESULTS {$max}";
            $rows = $this->runQuery($realm, $tok, $q)['QueryResponse']['Payment'] ?? [];

            foreach ($rows as $p) {
                // Upsert using existing model logic; 
                // that logic will store only if 'DepositTo' is Undeposited, etc.
                $m->upsertUndepositedRecord($p, 'Payment');
            }

            $start += $max;
        } while (count($rows) === $max);
    }

    /* ──────────────────────────────────────────────────────────────
       SalesReceipt
       ──────────────────────────────────────────────────────────── */
    private function pullSalesReceipts(string $realm, string $tok, string $from, string $to, UndepositedFundsModel $m)
    {
        $start = 1;  $max = 1000;
        do {
            $q = "
              SELECT *
              FROM SalesReceipt
              WHERE TxnDate >= '{$from}' AND TxnDate <= '{$to}'
              ORDERBY Id
              STARTPOSITION {$start}
              MAXRESULTS {$max}";
            $rows = $this->runQuery($realm, $tok, $q)['QueryResponse']['SalesReceipt'] ?? [];

            foreach ($rows as $sr) {
                $acctName = $sr['DepositToAccountRef']['name'] ?? '';
                if ($this->strContainsUndeposited($acctName)) {
                    $m->upsertUndepositedRecord($sr, 'SalesReceipt');
                }
            }
            $start += $max;
        } while (count($rows) === $max);
    }

    /* ──────────────────────────────────────────────────────────────
       RefundReceipt
       ──────────────────────────────────────────────────────────── */
    private function pullRefundReceipts(string $realm, string $tok, string $from, string $to, UndepositedFundsModel $m)
    {
        $start = 1;  $max = 1000;
        do {
            $q = "
              SELECT *
              FROM RefundReceipt
              WHERE TxnDate >= '{$from}' AND TxnDate <= '{$to}'
              ORDERBY Id
              STARTPOSITION {$start}
              MAXRESULTS {$max}";
            $rows = $this->runQuery($realm, $tok, $q)['QueryResponse']['RefundReceipt'] ?? [];

            foreach ($rows as $rr) {
                $acctName = $rr['RefundFromAccountRef']['name']
                            ?? $rr['DepositToAccountRef']['name']
                            ?? '';
                if ($this->strContainsUndeposited($acctName)) {
                    $m->upsertUndepositedRecord($rr, 'RefundReceipt');
                }
            }
            $start += $max;
        } while (count($rows) === $max);
    }

    /* CreditMemo */
    private function pullCreditMemos(string $realm, string $tok, string $from, string $to, UndepositedFundsModel $m)
    {
        $start = 1;  $max = 1000;
        do {
            $q = "
              SELECT *
              FROM CreditMemo
              WHERE TxnDate >= '{$from}' AND TxnDate <= '{$to}'
              ORDERBY Id
              STARTPOSITION {$start}
              MAXRESULTS {$max}";
            $rows = $this->runQuery($realm, $tok, $q)['QueryResponse']['CreditMemo'] ?? [];

            foreach ($rows as $cm) {
                $acctName = $cm['DepositToAccountRef']['name'] ?? '';
                if ($this->strContainsUndeposited($acctName)) {
                    $m->upsertUndepositedRecord($cm, 'CreditMemo');
                }
            }
            $start += $max;
        } while (count($rows) === $max);
    }

    /* JournalEntry */
    private function pullJournalEntries(string $realm, string $tok, string $from, string $to, UndepositedFundsModel $m)
    {
        $start = 1;  $max = 1000;

        do {
            $q = "
              SELECT *
              FROM JournalEntry
              WHERE TxnDate >= '{$from}' AND TxnDate <= '{$to}'
              ORDERBY Id
              STARTPOSITION {$start}
              MAXRESULTS {$max}";
            $rows = $this->runQuery($realm, $tok, $q)['QueryResponse']['JournalEntry'] ?? [];

            foreach ($rows as $je) {
                $hasUF = false;
                foreach ($je['Line'] ?? [] as $ln) {
                    $ref = $ln['JournalEntryLineDetail']['AccountRef'] ?? [];
                    if ($this->strContainsUndeposited($ref['name'] ?? '')) {
                        $hasUF = true; 
                        break;
                    }
                }
                if ($hasUF) {
                    $m->upsertUndepositedRecord($je, 'JournalEntry');
                }
            }
            $start += $max;
        } while (count($rows) === $max);
    }

    /* ──────────────────────────────────────────────────────────────
       helper to run queries
       ──────────────────────────────────────────────────────────── */
    private function runQuery(string $realm, string $tok, string $q): array
    {
        $url = "https://quickbooks.api.intuit.com/v3/company/{$realm}/query?query=" . urlencode($q);

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_HTTPHEADER     => [
                "Authorization: Bearer {$tok}",
                "Accept: application/json",
                "Content-Type: application/text",
            ],
            CURLOPT_RETURNTRANSFER => true,
        ]);
        $resp = curl_exec($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($code !== 200) {
            throw new \RuntimeException("QBO API error {$code}: {$resp}");
        }
        return json_decode($resp, true);
    }
}
