<?php

declare(strict_types=1);

namespace App\Modules\Sales\Application\Services;

use PDO;
use Throwable;

final class SalesReceiptsPageService
{
    public function handleIndex(): void
    {
        require_login();

        $pdo = db();
        \App\Core\AppBootstrap::bootSalesReceipts($pdo);
        require_permission('sales.receipts.view');

        $this->renderIndex($this->getIndexData($pdo, $_GET));
    }

    public function handleForm(): void
    {
        require_login();

        $pdo = db();
        \App\Core\AppBootstrap::bootSalesReceipts($pdo, true, true);
        require_permission('sales.receipts.create');

        $formData = $this->loadFormData($pdo, array_merge($_GET, $_POST));
        $routeContext = $this->pageData();

        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            verify_csrf();
            $result = $this->handleFormSubmission($pdo, $_POST, current_user_id(), $routeContext);

            if ($result['ok'] ?? false) {
                flash_set('success', 'تم إنشاء سند القبض');
                redirect((string) $result['redirect']);
            }

            $formData = array_merge($formData, [
                'receipt' => $result['receipt'] ?? $formData['receipt'],
                'party' => $result['party'] ?? null,
                'errors' => $result['errors'] ?? ['تعذر حفظ السند.'],
            ]);
        }

        $this->renderForm($formData);
    }

    public function handleView(): void
    {
        require_login();

        $pdo = db();
        \App\Core\AppBootstrap::bootSalesReceipts($pdo);
        require_permission('sales.receipts.view');

        $id = (int) ($_GET['id'] ?? 0);
        if ($id <= 0) {
            http_response_code(400);
            die('Bad request');
        }

        $receipt = $this->getViewReceipt($pdo, $id);
        if (!$receipt) {
            http_response_code(404);
            die('Not found');
        }

        $this->renderView($receipt);
    }

    public function pageData(): array
    {
        return [
            'dashboard' => SalesReceiptsRouteContext::dashboard(),
            'sales_home' => SalesReceiptsRouteContext::salesHome(),
            'index' => SalesReceiptsRouteContext::index(),
            'form' => SalesReceiptsRouteContext::form(),
            'statement' => static fn(int $partyId): string => SalesReceiptsRouteContext::statement($partyId),
            'view' => static fn(int $id): string => SalesReceiptsRouteContext::view($id),
        ];
    }

    public function renderIndex(array $data): void
    {
        $routeContext = $this->pageData();
        view('pages/sales/customer_receipts', array_merge($data, [
            'routeContext' => $routeContext,
            'page_title' => 'سندات القبض',
            'breadcrumbs' => [
                ['label' => 'لوحة التحكم', 'url' => $routeContext['dashboard']],
                ['label' => 'المبيعات', 'url' => $routeContext['sales_home']],
                ['label' => 'سندات القبض'],
            ],
        ]));
    }

    public function renderForm(array $data): void
    {
        $routeContext = $this->pageData();
        view('pages/sales/customer_receipt_form', array_merge($data, [
            'routeContext' => $routeContext,
            'page_title' => 'إنشاء سند قبض',
            'breadcrumbs' => [
                ['label' => 'لوحة التحكم', 'url' => $routeContext['dashboard']],
                ['label' => 'سندات القبض', 'url' => $routeContext['index']],
                ['label' => 'إنشاء'],
            ],
        ]));
    }

    public function renderView(array $receipt): void
    {
        $routeContext = $this->pageData();
        $id = (int)($receipt['id'] ?? 0);
        $label = (string)($receipt['receipt_no'] ?? ('#' . $id));

        view('pages/sales/customer_receipt_view', [
            'receipt' => $receipt,
            'routeContext' => $routeContext,
            'page_title' => 'عرض سند قبض',
            'breadcrumbs' => [
                ['label' => 'لوحة التحكم', 'url' => $routeContext['dashboard']],
                ['label' => 'سندات القبض', 'url' => $routeContext['index']],
                ['label' => $label],
            ],
        ]);
    }

    public function loadFormData(PDO $pdo, array $request, int $defaultCashboxId = 1): array
    {
        $cashboxes = $pdo->query("SELECT id,name FROM cashboxes WHERE is_active=1 ORDER BY id ASC")->fetchAll() ?: [];
        $bankAccounts = $pdo->query("SELECT id,name FROM bank_accounts WHERE is_active=1 ORDER BY name ASC")->fetchAll() ?: [];

        $receipt = [
            'receipt_date' => date('Y-m-d'),
            'party_id' => 0,
            'account_source_type' => 'cashbox',
            'cashbox_id' => (int)($cashboxes[0]['id'] ?? $defaultCashboxId),
            'bank_account_id' => (int)($bankAccounts[0]['id'] ?? 0),
            'amount' => 0,
            'method' => 'CASH',
            'pay_method' => 'cash',
            'ref_no' => '',
            'cheque_no' => '',
            'cheque_due_date' => '',
            'beneficiary_name' => '',
            'bank_name' => '',
            'transfer_ref' => '',
            'transfer_date' => '',
            'sender_name' => '',
            'notes' => '',
        ];

        $prefillPartyId = (int)($request['party_id'] ?? $request['customer_id'] ?? 0);
        if ($prefillPartyId > 0) {
            $receipt['party_id'] = $prefillPartyId;
        }

        return [
            'receipt' => $receipt,
            'cashboxes' => $cashboxes,
            'bankAccounts' => $bankAccounts,
            'errors' => [],
            'party' => $this->findParty($pdo, (int)$receipt['party_id']),
        ];
    }

    public function handleFormSubmission(PDO $pdo, array $post, int $userId, array $routeContext): array
    {
        $receipt = $this->normalizeReceiptInput($post);
        $errors = $this->validateReceipt($receipt);

        if (!empty($errors)) {
            return [
                'ok' => false,
                'errors' => $errors,
                'receipt' => $receipt,
                'party' => $this->findParty($pdo, (int)$receipt['party_id']),
            ];
        }

        $pdo->beginTransaction();

        try {
            $receiptNo = next_receipt_no($pdo);
            $pdo->prepare("INSERT INTO customer_receipts (receipt_no, receipt_date, customer_id, party_id, amount, method, cashbox_id, bank_account_id, account_source_type, ref_no, pay_method,
                              cheque_no, cheque_due_date, beneficiary_name,
                              bank_name, transfer_ref, transfer_date, sender_name,
                              notes, status, user_id)
                           VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
                ->execute([
                    $receiptNo,
                    $receipt['receipt_date'],
                    null,
                    $receipt['party_id'],
                    $receipt['amount'],
                    $receipt['method'],
                    $receipt['account_source_type'] === 'cashbox' ? $receipt['cashbox_id'] : null,
                    $receipt['account_source_type'] === 'bank_account' ? $receipt['bank_account_id'] : null,
                    $receipt['account_source_type'],
                    $receipt['ref_no'] !== '' ? $receipt['ref_no'] : null,
                    $receipt['pay_method'],
                    $receipt['cheque_no'] !== '' ? $receipt['cheque_no'] : null,
                    $receipt['cheque_due_date'] !== '' ? $receipt['cheque_due_date'] : null,
                    $receipt['beneficiary_name'] !== '' ? $receipt['beneficiary_name'] : null,
                    $receipt['bank_name'] !== '' ? $receipt['bank_name'] : null,
                    $receipt['transfer_ref'] !== '' ? $receipt['transfer_ref'] : null,
                    $receipt['transfer_date'] !== '' ? $receipt['transfer_date'] : null,
                    $receipt['sender_name'] !== '' ? $receipt['sender_name'] : null,
                    $receipt['notes'] !== '' ? $receipt['notes'] : null,
                    'POSTED',
                    $userId,
                ]);

            $id = (int)$pdo->lastInsertId();
            $dt = $receipt['receipt_date'] . ' 00:00:00';

            if ($receipt['account_source_type'] === 'cashbox' && table_exists($pdo, 'cashbox_transactions')) {
                cashbox_add_txn(
                    $pdo,
                    (int)$receipt['cashbox_id'],
                    $dt,
                    'in',
                    (float)$receipt['amount'],
                    'customer_receipt',
                    $id,
                    $receipt['notes'] !== '' ? $receipt['notes'] : null,
                    $userId
                );
            } elseif ($receipt['account_source_type'] === 'bank_account') {
                $stBank = $pdo->prepare("INSERT INTO bank_transactions (bank_account_id, trx_date, trx_type, amount, source_type, source_id, ref_no, notes, user_id) VALUES (?,?,?,?,?,?,?,?,?)");
                $stBank->execute([
                    (int)$receipt['bank_account_id'],
                    $dt,
                    'in',
                    (float)$receipt['amount'],
                    'customer_receipt',
                    $id,
                    $receipt['ref_no'] !== '' ? $receipt['ref_no'] : null,
                    $receipt['notes'] !== '' ? $receipt['notes'] : null,
                    $userId,
                ]);
            }

            $docDate = date('Y-m-d', strtotime((string)$receipt['receipt_date']));
            party_add_ledger(
                $pdo,
                (int)$receipt['party_id'],
                'RCPT',
                $id,
                $receiptNo,
                $docDate,
                0,
                (float)$receipt['amount'],
                $receipt['notes'] !== '' ? $receipt['notes'] : null
            );

            audit_log('CREATE_CUST_RCPT', 'customer_receipts', $id, [
                'receipt_no' => $receiptNo,
                'amount' => $receipt['amount'],
                'method' => $receipt['method'],
                'pay_method' => $receipt['pay_method'],
                'account_source_type' => $receipt['account_source_type'],
                'cashbox_id' => $receipt['cashbox_id'],
                'bank_account_id' => $receipt['bank_account_id'],
                'party_id' => $receipt['party_id'],
            ]);

            $pdo->commit();

            return [
                'ok' => true,
                'id' => $id,
                'redirect' => ($routeContext['view'] ?? [SalesReceiptsRouteContext::class, 'view'])($id),
            ];
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }

            return [
                'ok' => false,
                'errors' => ['حدث خطأ أثناء الحفظ: ' . $e->getMessage()],
                'receipt' => $receipt,
                'party' => $this->findParty($pdo, (int)$receipt['party_id']),
            ];
        }
    }

    public function getViewReceipt(PDO $pdo, int $id): ?array
    {
        $st = $pdo->prepare("SELECT cr.*, p.name party_name, COALESCE(p.code, CONCAT('P',LPAD(p.id,5,'0'))) party_code,
                                     cb.name cashbox_name, ba.name bank_account_name
                              FROM customer_receipts cr
                              JOIN parties p ON p.id = COALESCE(cr.party_id, cr.customer_id)
                              LEFT JOIN cashboxes cb ON cb.id=cr.cashbox_id
                              LEFT JOIN bank_accounts ba ON ba.id=cr.bank_account_id
                              WHERE cr.id=? LIMIT 1");
        $st->execute([$id]);

        $receipt = $st->fetch();

        return $receipt ?: null;
    }

    public function getIndexData(PDO $pdo, array $query): array
    {
        $q = trim((string)($query['q'] ?? ''));
        $dateFrom = trim((string)($query['from'] ?? ''));
        $dateTo = trim((string)($query['to'] ?? ''));

        if ($dateFrom === '') {
            $dateFrom = date('Y-m-01');
        }
        if ($dateTo === '') {
            $dateTo = date('Y-m-d');
        }

        $params = [$dateFrom . ' 00:00:00', date('Y-m-d 00:00:00', strtotime($dateTo . ' +1 day'))];
        $where = ["cr.receipt_date >= ?", "cr.receipt_date < ?"];

        if ($q !== '') {
            $where[] = "(cr.receipt_no LIKE ? OR p.name LIKE ? OR COALESCE(p.code,'') LIKE ?)";
            $like = '%' . $q . '%';
            $params[] = $like;
            $params[] = $like;
            $params[] = $like;
        }

        $sql = "SELECT cr.id, cr.receipt_no, cr.receipt_date, cr.amount, cr.method, cr.pay_method, cr.cashbox_id, cr.status,
                       p.name party_name,
                       cb.name cashbox_name
                FROM customer_receipts cr
                JOIN parties p ON p.id = COALESCE(cr.party_id, cr.customer_id)
                LEFT JOIN cashboxes cb ON cb.id = cr.cashbox_id
                WHERE " . implode(' AND ', $where) . "
                ORDER BY cr.id DESC
                LIMIT 500";

        $st = $pdo->prepare($sql);
        $st->execute($params);
        $allRows = $st->fetchAll() ?: [];
        $page = max(1, (int)($query['page'] ?? 1));
        $pagination = paginate_rows($allRows, $page, 25);

        return [
            'receipts' => $pagination['items'],
            'page' => $pagination['page'],
            'per_page' => $pagination['per_page'],
            'total_pages' => $pagination['total_pages'],
            'total_rows' => $pagination['total_rows'],
            'from_row' => $pagination['from'],
            'to_row' => $pagination['to'],
            'from' => $dateFrom,
            'to' => $dateTo,
            'q' => $q,
        ];
    }

    private function normalizeReceiptInput(array $post): array
    {
        $payMethod = strtolower(trim((string)($post['pay_method'] ?? ($post['method'] ?? 'cash'))));
        if (!in_array($payMethod, ['cash', 'bank_transfer', 'cheque'], true)) {
            $payMethod = 'cash';
        }

        $accountSourceType = trim((string)($post['account_source_type'] ?? 'cashbox'));
        if (!in_array($accountSourceType, ['cashbox', 'bank_account'], true)) {
            $accountSourceType = 'cashbox';
        }

        return [
            'receipt_date' => trim((string)($post['receipt_date'] ?? date('Y-m-d'))),
            'party_id' => (int)($post['party_id'] ?? $post['customer_id'] ?? 0),
            'account_source_type' => $accountSourceType,
            'cashbox_id' => (int)($post['cashbox_id'] ?? 0),
            'bank_account_id' => (int)($post['bank_account_id'] ?? 0),
            'amount' => (float)($post['amount'] ?? 0),
            'method' => $payMethod === 'cheque' ? 'CHEQUE' : ($payMethod === 'bank_transfer' ? 'BANK' : 'CASH'),
            'pay_method' => $payMethod,
            'ref_no' => trim((string)($post['ref_no'] ?? '')),
            'cheque_no' => trim((string)($post['cheque_no'] ?? '')),
            'cheque_due_date' => trim((string)($post['cheque_due_date'] ?? '')),
            'beneficiary_name' => trim((string)($post['beneficiary_name'] ?? '')),
            'bank_name' => trim((string)($post['bank_name'] ?? '')),
            'transfer_ref' => trim((string)($post['transfer_ref'] ?? '')),
            'transfer_date' => trim((string)($post['transfer_date'] ?? '')),
            'sender_name' => trim((string)($post['sender_name'] ?? '')),
            'notes' => trim((string)($post['notes'] ?? '')),
        ];
    }

    private function validateReceipt(array $receipt): array
    {
        $errors = [];

        if ((int)$receipt['party_id'] <= 0) {
            $errors[] = 'اختر طرفًا صحيحًا.';
        }

        if ((float)$receipt['amount'] <= 0) {
            $errors[] = 'قيمة السند يجب أن تكون أكبر من صفر.';
        }

        if ($receipt['account_source_type'] === 'cashbox' && (int)$receipt['cashbox_id'] <= 0) {
            $errors[] = 'اختر خزنة.';
        }

        if ($receipt['account_source_type'] === 'bank_account' && (int)$receipt['bank_account_id'] <= 0) {
            $errors[] = 'اختر حسابًا بنكيًا.';
        }

        if ($receipt['pay_method'] === 'cheque' && ($receipt['cheque_no'] === '' || $receipt['cheque_due_date'] === '' || $receipt['beneficiary_name'] === '')) {
            $errors[] = 'بيانات الشيك غير مكتملة: رقم الشيك وتاريخ الاستحقاق واسم المستفيد مطلوبة.';
        }

        if ($receipt['pay_method'] === 'bank_transfer' && ($receipt['bank_name'] === '' || $receipt['transfer_ref'] === '' || $receipt['transfer_date'] === '')) {
            $errors[] = 'بيانات التحويل غير مكتملة: اسم البنك ورقم المرجع وتاريخ التحويل مطلوبة.';
        }

        return $errors;
    }

    private function findParty(PDO $pdo, int $partyId): ?array
    {
        if ($partyId <= 0) {
            return null;
        }

        $st = $pdo->prepare("SELECT id, name, COALESCE(code, CONCAT('P',LPAD(id,5,'0'))) code FROM parties WHERE id=? LIMIT 1");
        $st->execute([$partyId]);
        $party = $st->fetch();

        return $party ?: null;
    }
}
