<?php

class AccountingController extends Controller
{
    private Accounting $accounting;
    private Document $documents;

    public function __construct()
    {
        $this->accounting = new Accounting();
        $this->documents = new Document();
    }

    private function guard(): void
    {
        Auth::requireLogin();
        if (!can('accounting.view')) {
            http_response_code(403);
            require app_path('Views/errors/403.php');
            exit;
        }
    }

    private function manage(): void
    {
        Auth::requireLogin();
        if (!can('accounting.manage')) {
            http_response_code(403);
            require app_path('Views/errors/403.php');
            exit;
        }
    }

    public function index(): void
    {
        $this->guard();
        $stats = $this->accounting->dashboard();
        $expenses = array_slice($this->accounting->expenses(), 0, 8);
        $bills = array_slice($this->accounting->bills(), 0, 8);
        $profitability = array_slice($this->accounting->loadProfitability(), 0, 8);
        $this->view('accounting/index', compact('stats', 'expenses', 'bills', 'profitability'));
    }

    public function expenses(): void
    {
        $this->guard();
        $search = trim($_GET['search'] ?? '');
        $expenses = $this->accounting->expenses($search ?: null);
        $this->view('accounting/expenses', compact('expenses', 'search'));
    }

    public function createExpense(): void
    {
        $this->guard();
        $categories = $this->accounting->expenseCategories();
        $vendors = $this->accounting->vendors();
        $db = Database::connection();
        $loads = $db->query('SELECT id, load_number FROM loads ORDER BY id DESC')->fetchAll();
        $drivers = $db->query('SELECT id, name FROM drivers ORDER BY name')->fetchAll();
        $trucks = $db->query('SELECT id, unit_number FROM trucks ORDER BY unit_number')->fetchAll();
        $trailers = $db->query('SELECT id, trailer_number FROM trailers ORDER BY trailer_number')->fetchAll();
        $crossings = $db->query('SELECT id, crossing_number FROM border_crossings ORDER BY id DESC')->fetchAll();
        $this->view('accounting/expense_form', compact('categories', 'vendors', 'loads', 'drivers', 'trucks', 'trailers', 'crossings'));
    }

    public function storeExpense(): void
    {
        $this->guard();
        Csrf::verify();
        if ((float) ($_POST['amount'] ?? 0) <= 0) {
            $this->flash('danger', 'Expense amount is required.');
            $this->redirect('/accounting/expenses/create');
        }
        if (current_role() === 'driver') {
            $user = Auth::user();
            $_POST['driver_id'] = $user['driver_id'] ?? null;
            $_POST['status'] = 'submitted';
        }
        $id = $this->accounting->createExpense($_POST);
        if (!empty($_FILES['receipt']['tmp_name']) && ($_FILES['receipt']['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK) {
            $documentId = $this->saveUpload($_FILES['receipt'], 'expense', $id, 'Expense Receipt', $_POST);
            $this->accounting->attachExpenseDocument($id, $documentId);
        }
        notify_roles(['owner', 'admin', 'accountant'], 'Expense submitted', 'Expense #' . $id . ' is ready for review.', '/accounting/expenses', 'warning');
        log_activity('expense_submitted', 'Expense #' . $id . ' submitted');
        $this->flash('success', 'Expense saved successfully.');
        $this->redirect(current_role() === 'driver' ? '/driver/expenses' : '/accounting/expenses');
    }

    public function expenseStatus(): void
    {
        $this->manage();
        Csrf::verify();
        $this->accounting->setExpenseStatus((int) ($_POST['id'] ?? 0), $_POST['status'] ?? 'submitted');
        $this->flash('success', 'Expense status updated.');
        $this->redirect('/accounting/expenses');
    }

    public function vendors(): void
    {
        $this->guard();
        $vendors = $this->accounting->vendors();
        $this->view('accounting/vendors', compact('vendors'));
    }

    public function storeVendor(): void
    {
        $this->manage();
        Csrf::verify();
        if (empty(trim($_POST['company_name'] ?? ''))) {
            $this->flash('danger', 'Vendor company name is required.');
            $this->redirect('/accounting/vendors');
        }
        $this->accounting->createVendor($_POST);
        $this->flash('success', 'Vendor added.');
        $this->redirect('/accounting/vendors');
    }

    public function bills(): void
    {
        $this->guard();
        $bills = $this->accounting->bills();
        $vendors = $this->accounting->vendors();
        $accounts = $this->accounting->bankAccounts();
        $this->view('accounting/bills', compact('bills', 'vendors', 'accounts'));
    }

    public function storeBill(): void
    {
        $this->manage();
        Csrf::verify();
        try {
            $id = $this->accounting->createBill($_POST);
            $this->accounting->postVendorBill($id);
            $this->flash('success', 'Vendor bill saved and posted.');
        } catch (Throwable $exception) {
            $this->flash('danger', $exception->getMessage());
        }
        $this->redirect('/accounting/bills');
    }

    public function payBill(): void
    {
        $this->manage();
        Csrf::verify();
        try {
            $this->accounting->payBill((int) ($_POST['id'] ?? 0), $_POST);
            $this->flash('success', 'Bill payment recorded.');
        } catch (Throwable $exception) {
            $this->flash('danger', $exception->getMessage());
        }
        $this->redirect('/accounting/bills');
    }

    public function banking(): void
    {
        $this->guard();
        $accounts = $this->accounting->bankAccounts();
        $transactions = $this->accounting->transactions();
        $this->view('accounting/banking', compact('accounts', 'transactions'));
    }

    public function storeBankAccount(): void
    {
        $this->manage();
        Csrf::verify();
        $this->accounting->createBankAccount($_POST);
        $this->flash('success', 'Financial account added.');
        $this->redirect('/accounting/banking');
    }

    public function storeTransaction(): void
    {
        $this->manage();
        Csrf::verify();
        $this->accounting->recordBankTransaction(
            (int) $_POST['bank_account_id'],
            $_POST['transaction_type'],
            (float) $_POST['amount'],
            $_POST['transaction_date'] ?: date('Y-m-d'),
            trim((string) ($_POST['description'] ?? '')),
            trim((string) ($_POST['reference_number'] ?? '')) ?: null
        );
        $this->flash('success', 'Transaction recorded.');
        $this->redirect('/accounting/banking');
    }

    public function reconcileTransaction(): void
    {
        $this->manage();
        Csrf::verify();
        $this->accounting->reconcileTransaction((int) ($_POST['id'] ?? 0));
        $this->flash('success', 'Transaction marked as reconciled.');
        $this->redirect('/accounting/banking');
    }

    public function journals(): void
    {
        $this->guard();
        $journals = $this->accounting->journals();
        $accounts = $this->accounting->chartAccounts();
        $this->view('accounting/journals', compact('journals', 'accounts'));
    }

    public function storeJournal(): void
    {
        $this->manage();
        Csrf::verify();
        try {
            $this->accounting->createJournal($_POST);
            $this->flash('success', 'Journal entry posted.');
        } catch (Throwable $exception) {
            $this->flash('danger', $exception->getMessage());
        }
        $this->redirect('/accounting/journals');
    }

    public function factoring(): void
    {
        $this->guard();
        $records = $this->accounting->factoring();
        $invoices = $this->accounting->openInvoices();
        $vendors = $this->accounting->vendors();
        $this->view('accounting/factoring', compact('records', 'invoices', 'vendors'));
    }

    public function storeFactoring(): void
    {
        $this->manage();
        Csrf::verify();
        $this->accounting->saveFactoring($_POST);
        $this->flash('success', 'Factoring record saved.');
        $this->redirect('/accounting/factoring');
    }

    public function reports(): void
    {
        $this->guard();
        $from = $_GET['from'] ?? date('Y-m-01');
        $to = $_GET['to'] ?? date('Y-m-t');
        $profitLoss = $this->accounting->profitLoss($from, $to);
        $profitability = $this->accounting->loadProfitability();
        $trialBalance = $this->accounting->trialBalance($to);
        $receivableAging = $this->accounting->receivableAging();
        $payableAging = $this->accounting->payableAging();
        $balanceSheet = $this->accounting->balanceSheet($to);
        $cashFlow = $this->accounting->cashFlow($from, $to);
        $expenseSummary = $this->accounting->expenseSummary($from, $to);
        $this->view('accounting/reports', compact('from', 'to', 'profitLoss', 'profitability', 'trialBalance', 'receivableAging', 'payableAging', 'balanceSheet', 'cashFlow', 'expenseSummary'));
    }

    public function customerStatement(): void
    {
        $this->guard();
        $brokerId=(int)($_GET['broker_id']??0);$db=Database::connection();
        $stmt=$db->prepare('SELECT * FROM brokers WHERE id=?');$stmt->execute([$brokerId]);$broker=$stmt->fetch();
        if(!$broker){$this->flash('danger','Customer not found.');$this->redirect('/invoices');}
        $stmt=$db->prepare("SELECT invoices.*,loads.load_number,COALESCE((SELECT SUM(amount) FROM payments WHERE payments.invoice_id=invoices.id),0) paid_amount FROM invoices LEFT JOIN loads ON loads.id=invoices.load_id WHERE invoices.broker_id=? ORDER BY COALESCE(invoices.invoice_date,DATE(invoices.created_at)) DESC,invoices.id DESC LIMIT 18");$stmt->execute([$brokerId]);$invoices=$stmt->fetchAll();
        $pdf=new SimplePdf();$pdf->rect(36,706,540,58,true,0.93);$companyX=50;$logoRelative=setting('company_logo','');if($logoRelative&&$pdf->image(base_path($logoRelative),50,714,50,42))$companyX=112;$pdf->text($companyX,742,setting('company_name',APP_NAME)?:APP_NAME,18,true);$pdf->text($companyX,725,setting('company_address','')?:'',8);$pdf->text(420,742,'CUSTOMER STATEMENT',14,true);$pdf->text(470,724,date('M d, Y'),9);
        $pdf->text(40,675,'CUSTOMER',9,true);$pdf->line(40,670,290,670);$pdf->text(45,648,$broker['company_name'],12,true);$y=$pdf->wrappedText(45,630,$broker['address']?:'',235,8,11,false,3);$pdf->text(45,$y,trim(($broker['email']?:'').'  '.($broker['phone']?:'')),8);
        $total=0;$paid=0;foreach($invoices as$invoice){$total+=(float)$invoice['total'];$paid+=(float)$invoice['paid_amount'];}$balance=max(0,$total-$paid);
        $pdf->text(340,675,'SUMMARY',9,true);$pdf->line(340,670,572,670);$pdf->text(345,648,'Invoiced',9);$pdf->text(495,648,money($total),9);$pdf->text(345,628,'Payments',9);$pdf->text(495,628,money($paid),9);$pdf->line(340,615,572,615);$pdf->text(345,596,'BALANCE DUE',11,true);$pdf->text(485,596,money($balance),11,true);
        $pdf->rect(40,540,532,24,true,0.92);foreach([['INVOICE',48],['DATE',160],['LOAD',250],['TOTAL',350],['PAID',435],['BALANCE',505]] as$h)$pdf->text($h[1],548,$h[0],8,true);$y=520;foreach($invoices as$invoice){if($y<120)break;$invBalance=max(0,(float)$invoice['total']-(float)$invoice['paid_amount']);$pdf->text(48,$y,$invoice['invoice_number'],8);$pdf->text(160,$y,date_display($invoice['invoice_date']?:$invoice['created_at']),8);$pdf->text(250,$y,$invoice['load_number']?:'-',8);$pdf->text(350,$y,money($invoice['total']),8);$pdf->text(435,$y,money($invoice['paid_amount']),8);$pdf->text(505,$y,money($invBalance),8);$pdf->line(40,$y-7,572,$y-7,0.3);$y-=22;}
        $pdf->text(40,75,setting('invoice_payment_instructions','')?:'Please remit payment using the agreed payment method.',8);$pdf->text(500,35,'Page 1 of 1',7);$out=$pdf->output();header('Content-Type: application/pdf');header('Content-Disposition: attachment; filename="statement-'.preg_replace('/[^A-Za-z0-9_-]/','_',$broker['company_name']).'.pdf"');header('Content-Length: '.strlen($out));echo$out;exit;
    }


    private function saveUpload(array $file, string $type, int $id, string $documentType, array $data): int
    {
        $original = basename((string) $file['name']);
        $extension = strtolower(pathinfo($original, PATHINFO_EXTENSION));
        $allowed = ['pdf', 'jpg', 'jpeg', 'png', 'webp', 'heic'];
        if (!in_array($extension, $allowed, true)) {
            throw new RuntimeException('Unsupported receipt file.');
        }
        $folder = storage_path('uploads/accounting/' . date('Y/m'));
        if (!is_dir($folder)) mkdir($folder, 0755, true);
        $stored = bin2hex(random_bytes(16)) . '.' . $extension;
        $target = $folder . DIRECTORY_SEPARATOR . $stored;
        if (!move_uploaded_file($file['tmp_name'], $target)) {
            throw new RuntimeException('Receipt upload failed.');
        }
        return $this->documents->create([
            'related_type' => $type,
            'related_id' => $id,
            'document_type' => $documentType,
            'file_name' => $original,
            'stored_name' => $stored,
            'file_path' => str_replace(base_path() . DIRECTORY_SEPARATOR, '', $target),
            'mime_type' => $file['type'] ?? null,
            'file_size' => $file['size'] ?? null,
            'expires_at' => null,
            'notes' => $data['description'] ?? null,
            'latitude' => $data['latitude'] ?? null,
            'longitude' => $data['longitude'] ?? null,
            'location_address' => $data['location_address'] ?? null,
        ]);
    }
}
