<?php

namespace {
    if (!function_exists('base_url')) {
        function base_url($uri = '')
        {
            return 'https://test.alrahmaisgl.org/' . ltrim((string)$uri, '/');
        }
    }

    if (!function_exists('site_url')) {
        function site_url($uri = '')
        {
            return 'https://test.alrahmaisgl.org/' . ltrim((string)$uri, '/');
        }
    }

    if (!function_exists('view')) {
        function view($name, $data = [], $options = [])
        {
            return ['view' => $name, 'data' => $data, 'options' => $options];
        }
    }
}

namespace Tests\App\Controllers\View {

    use App\Controllers\View\ReimbursementController;
    use CodeIgniter\Database\BaseBuilder;
    use CodeIgniter\Database\BaseConnection;
    use CodeIgniter\Database\BaseResult;
    use CodeIgniter\Test\CIUnitTestCase;
    use Config\Services;
    use App\Models\UserModel;

    class TestableReimbursementController extends ReimbursementController
    {
        public function __construct()
        {
            // Skip the real parent constructor so we can inject mocks.
        }

        public function setDb(BaseConnection $db): self
        {
            $this->db = $db;
            return $this;
        }

        public function setUserModel(UserModel $model): self
        {
            $this->userModel = $model;
            return $this;
        }
    }

    class ReimbursementFakeRenderer
    {
        private array $data = [];
        private array $lastRender = [];

        public function setData(?array $data = null): self
        {
            $this->data = $data ?? [];
            return $this;
        }

        public function render(string $view, array $data = [], $options = null)
        {
            $this->lastRender = [
                'view' => $view,
                'data' => $data ?: $this->data,
                'options' => $options,
            ];

            return 'fake-rendered:' . $view;
        }

        public function getLastRender(): array
        {
            return $this->lastRender;
        }
    }

    class ReimbursementControllerTest extends CIUnitTestCase
    {
        private TestableReimbursementController $controller;
        private ReimbursementFakeRenderer $renderer;

        protected function setUp(): void
        {
            parent::setUp();

            $this->renderer = new ReimbursementFakeRenderer();
            Services::injectMock('renderer', $this->renderer);

            $result = $this->createMock(BaseResult::class);
            $result->method('getResultArray')->willReturn([
                [
                    'id' => 5,
                    'expense_id' => 5,
                    'expense_amount' => 15.5,
                    'description' => 'Markers',
                    'expense_receipt' => 'uploads/path/expense_receipt.pdf',
                    'retailor' => 'Office Store',
                    'purchased_by' => 11,
                    'purchaser_firstname' => 'Amina',
                    'purchaser_lastname' => 'Teacher',
                ],
                [
                    'id' => 6,
                    'expense_id' => 6,
                    'expense_amount' => 22.0,
                    'description' => 'Paper',
                    'expense_receipt' => 'uploads/receipt_path.pdf',
                    'retailor' => 'Supply Shop',
                    'purchased_by' => 12,
                    'purchaser_firstname' => 'Omar',
                    'purchaser_lastname' => 'Admin',
                ],
            ]);

            $builder = $this->createMock(BaseBuilder::class);
            $builder->method('select')->willReturnSelf();
            $builder->method('join')->willReturnSelf();
            $builder->method('where')->willReturnSelf();
            $builder->method('orWhere')->willReturnSelf();
            $builder->method('groupStart')->willReturnSelf();
            $builder->method('groupEnd')->willReturnSelf();
            $builder->method('orderBy')->willReturnSelf();
            $builder->method('get')->willReturn($result);

            $db = $this->createMock(BaseConnection::class);
            $db->method('table')->willReturn($builder);

            $this->controller = new TestableReimbursementController();
            $this->controller->setDb($db);
            $userModel = $this->getMockBuilder(UserModel::class)
                ->disableOriginalConstructor()
                ->addMethods(['select', 'join', 'where'])
                ->onlyMethods(['findAll'])
                ->getMock();
            $userModel->method('select')->willReturnSelf();
            $userModel->method('join')->willReturnSelf();
            $userModel->method('where')->willReturnSelf();
            $userModel->method('findAll')->willReturn([]);
            $this->controller->setUserModel($userModel);
        }

        protected function tearDown(): void
        {
            Services::resetSingle('renderer');
            parent::tearDown();
        }

        public function testUnderProcessingBuildsViewWithExpenses()
        {
            $result = $this->controller->underProcessing();
            if (is_array($result)) {
                $rendered = $result;
            } else {
                $this->assertSame('fake-rendered:reimbursements/under_processing', $result);
                $rendered = $this->renderer->getLastRender();
            }
            $this->assertSame('reimbursements/under_processing', $rendered['view']);
            $pendingItems = $rendered['data']['pendingItems'];
            $this->assertCount(2, $pendingItems);

            $first = $pendingItems[0];
            $this->assertSame(site_url('receipts/expense_receipt.pdf'), $first['receipt_url']);
            $this->assertSame('Amina Teacher', $first['purchased_by']);

            $second = $pendingItems[1];
            $this->assertSame(site_url('receipts/receipt_path.pdf'), $second['receipt_url']);
            $this->assertSame('Omar Admin', $second['purchased_by']);
        }
    }
}
