<?php

namespace App\Controllers\View;
use App\Controllers\BaseController;
use App\Models\ExpenseModel;
use App\Models\UserModel;
use App\Models\ConfigurationModel;
use CodeIgniter\Exceptions\PageNotFoundException;

class ExpenseController extends BaseController
{
    protected $expenseModel;
    protected $userModel;
    protected $configModel;
    protected $schoolYear;
    protected $semester;
    protected $retailors;

    public function __construct()
    {
        $this->expenseModel = new ExpenseModel();
        $this->userModel    = new UserModel();
        $this->configModel  = new ConfigurationModel();

        $this->schoolYear = $this->configModel->getConfig('school_year');
        $this->semester   = $this->configModel->getConfig('semester');

        // Default list of common retailors; adjust as needed
        $this->retailors = [
            'Amazon',
            'Walmart',
            'Costco',
            'BJ\'s',
            'Market Basket',
            'Aldi',
            'Hannaford',
            'Sam\'s Club',
            'HomeGoods',
            'Hostinger',
            'Wicked Cheesy',
            'Shatila',
            'Brothers Pizzeria',
            'Paradise Biryani Pointe',
            'Emad Leiman',
            'Nova Trampoline Park',
            'Lubin\'s Awards',
            'Dollar Tree',
            'Stop & Shop',
            'Dunkin\' Donuts',
            'Giovanni\'s Pizza',
            'Trader Joes'
        ];
    }

    /**
     * Return staff users (admins/teachers/etc.) excluding parents/guests.
     */
    private function staffUsers(): array
    {
        $rows = $this->userModel
            ->select('users.id, users.firstname, users.lastname, roles.name AS role_name')
            ->join('user_roles', 'user_roles.user_id = users.id', 'left')
            ->join('roles', 'roles.id = user_roles.role_id', 'left')
            ->where('roles.name IS NOT NULL', null, false)
            ->findAll();

        $excludedRoles = array_map('strtolower', ['parent', 'student', 'guest']);
        $staff = [];

        foreach ($rows as $row) {
            $roleName = strtolower((string) ($row['role_name'] ?? ''));
            $id       = (int) ($row['id'] ?? 0);
            if ($id <= 0 || in_array($roleName, $excludedRoles, true)) {
                continue;
            }
            if (!isset($staff[$id])) {
                $staff[$id] = [
                    'id'        => $id,
                    'firstname' => $row['firstname'] ?? '',
                    'lastname'  => $row['lastname'] ?? '',
                ];
            }
        }

        uasort($staff, static function ($a, $b) {
            $nameA = trim(($a['firstname'] ?? '') . ' ' . ($a['lastname'] ?? ''));
            $nameB = trim(($b['firstname'] ?? '') . ' ' . ($b['lastname'] ?? ''));
            return strcasecmp($nameA, $nameB);
        });

        return array_values($staff);
    }

    public function index()
    {
        $expenses = $this->expenseModel
            ->select("
                expenses.*,
                u.firstname AS purchaser_firstname, u.lastname AS purchaser_lastname,
                approver.firstname AS approver_firstname, approver.lastname AS approver_lastname
            ")
            ->join('users u', 'u.id = expenses.purchased_by', 'left')
            ->join('users approver', 'approver.id = expenses.approved_by', 'left')
            ->orderBy('expenses.created_at', 'DESC')
            ->findAll();

        // Enrich each row with a URL that goes through Files::receipt($name)
        // We store only the filename in 'receipt_path' (e.g., "1759...f62.png")
        $expenses = array_map(function ($row) {
            $name = $row['receipt_path'] ?? null;
            $row['receipt_url'] = $this->receiptUrl($name);
            return $row;
        }, $expenses);

        return view('expenses/index', ['expenses' => $expenses]);
    }

    public function create()
    {
        $users = $this->staffUsers();

        return view('expenses/create', [
            'users'     => $users,
            'retailors' => $this->retailors,
        ]);
    }

    public function store()
    {
        $rules = [
            'category'     => 'required|in_list[Expense,Purchase,Reimbursement,Donation]',
            'amount'       => 'required|decimal|greater_than[0]',
            // Frontend sends purchased_by as "id|Full Name"
            'purchased_by' => 'required',
            // Optional extra fields
            'retailor'         => 'permit_empty|max_length[255]',
            'date_of_purchase' => 'permit_empty',
            // allow JPG/JPEG/PNG/WEBP/GIF and PDF up to 2MB
            'receipt'      => 'uploaded[receipt]'
                . '|max_size[receipt,2048]'
                . '|ext_in[receipt,jpg,jpeg,png,webp,gif,pdf]'
                . '|mime_in[receipt,image/jpg,image/jpeg,image/png,image/webp,image/gif,application/pdf]',
        ];

        $messages = [
            'receipt' => [
                'uploaded' => 'Receipt file is required.',
                'max_size' => 'Maximum file size is 2MB.',
                'ext_in'   => 'Allowed formats: JPG, JPEG, PNG, WEBP, GIF, or PDF.',
                'mime_in'  => 'Allowed formats: JPG, JPEG, PNG, WEBP, GIF, or PDF.',
            ]
        ];

        if (!$this->validate($rules, $messages)) {
            return redirect()->back()->withInput()->with('error', $this->validator->listErrors());
        }

        // Safe values
        $category    = (string) $this->request->getPost('category');
        $amount      = (string) $this->request->getPost('amount');
        $description = (string) $this->request->getPost('description');
        $retailor    = trim((string) $this->request->getPost('retailor'));
        $datePurchase = (string) $this->request->getPost('date_of_purchase');
        $userId      = (int) (session()->get('user_id') ?? 0);
        $isDonation  = ($category === 'Donation');

        // Parse "purchased_by" as "7|John Doe"
        $purchasedInfo = (string) $this->request->getPost('purchased_by');
        [$purchasedById, $purchasedByName] = array_pad(explode('|', $purchasedInfo, 2), 2, null);
        $purchasedById = (int) $purchasedById;

        // School context
        $schoolYear = $this->schoolYear ?: date('Y');
        $semester   = $this->semester ?: 'Fall';

        // Handle upload: store under writable/uploads/receipts and save only the filename
        $receiptName = null;
        $file = $this->request->getFile('receipt');
        if ($file && $file->isValid() && !$file->hasMoved()) {
            $stored      = $file->store('receipts'); // -> writable/uploads/receipts/<randomname>.ext
            $receiptName = basename($stored);
        }

        $status       = $isDonation ? 'approved' : 'pending';
        $statusReason = $isDonation ? 'Marked as Donation (non-reimbursable).' : null;

        $this->expenseModel->insert([
            'category'     => $category,
            'amount'       => $amount,
            'receipt_path' => $receiptName, // filename only
            'description'  => $description,
            'retailor'     => ($retailor !== '') ? $retailor : null,
            'date_of_purchase' => ($datePurchase !== '') ? $datePurchase : null,
            'purchased_by' => $purchasedById,
            'added_by'     => $userId,
            'status'       => $status,
            'status_reason'=> $statusReason,
            'approved_by'  => $isDonation ? $userId : null,
            'school_year'  => $schoolYear,
            'semester'     => $semester,
        ]);

        return redirect()->to('/expenses/index')->with('success', 'Record added successfully!');
    }


    public function updateStatus()
    {
        $data   = $this->request->getJSON(true);
        $id     = isset($data['id']) ? (int)$data['id'] : null;
        $status = $data['status'] ?? null;
        $reason = $data['reason'] ?? '';
        $userId = (int) (session()->get('user_id') ?? 0);

        if (!$id || !in_array($status, ['approved', 'denied'], true)) {
            log_message('error', 'Invalid status or ID');
            return $this->response->setJSON(['error' => 'Invalid data']);
        }

        $expense = $this->expenseModel->find($id);
        if (!$expense) {
            log_message('error', 'Expense not found for ID ' . $id);
            return $this->response->setJSON(['error' => 'Expense not found']);
        }

        $success = $this->expenseModel->update($id, [
            'status'        => $status,
            'status_reason' => $reason,
            'approved_by'   => $userId,
            'updated_by'    => $userId
        ]);

        if (!$success) {
            log_message('error', 'Expense update failed for ID ' . $id);
            return $this->response->setJSON(['error' => 'Update failed']);
        }

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

    /**
     * Build a public URL for a receipt filename through Files::receipt($name).
     * Expects just the filename (e.g., "1759113425_1c443e607e1900f92f62.png").
     */
    private function receiptUrl(?string $filename): ?string
    {
        if (!$filename) {
            return null;
        }
        // Route should be defined as: $routes->get('receipts/(:any)', 'Files::receipt/$1');
        return site_url('receipts/' . $filename);
    }



    public function edit(int $id)
    {
        $expense = $this->expenseModel->find($id);
        if (!$expense) {
            throw PageNotFoundException::forPageNotFound("Expense #$id not found");
        }

        // same user list you use in create()
        $users = $this->staffUsers();

        return view('expenses/edit', [
            'expense'     => $expense,
            'users'       => $users,
            'retailors'   => $this->retailors,
            'receipt_url' => $expense['receipt_path'] ? site_url('receipts/' . basename($expense['receipt_path'])) : null,
        ]);
    }

    public function update(int $id)
    {
        helper(['form']);

        $expense = $this->expenseModel->find($id);
        if (!$expense) {
            throw PageNotFoundException::forPageNotFound("Expense #$id not found");
        }

        // Base rules
        $rules = [
            'category'     => 'required|in_list[Expense,Purchase,Reimbursement,Donation]',
            'amount'       => 'required|decimal|greater_than[0]',
            'purchased_by' => 'required', // still "id|Full Name"
            'retailor'         => 'permit_empty|max_length[255]',
            'date_of_purchase' => 'permit_empty',
        ];

        // Optional new receipt validation (only if provided)
        $file = $this->request->getFile('receipt');
        if ($file && $file->isValid() && ($file->getSize() ?? 0) > 0) {
            $rules['receipt'] = 'max_size[receipt,2048]'
                . '|ext_in[receipt,jpg,jpeg,png,webp,gif,pdf]'
                . '|mime_in[receipt,image/jpg,image/jpeg,image/png,image/webp,image/gif,application/pdf]';
        }

        if (!$this->validate($rules)) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        // Parse "id|Name"
        [$purchasedById] = array_pad(explode('|', (string) $this->request->getPost('purchased_by'), 2), 2, null);
        $purchasedById = (int) $purchasedById;
        $category   = (string) $this->request->getPost('category');
        $isDonation = ($category === 'Donation');
        $userId     = (int) (session()->get('user_id') ?? 0);

        // Keep old receipt unless replaced or removed
        $receiptName = $expense['receipt_path'];
        if ($file && $file->isValid() && !$file->hasMoved() && ($file->getSize() ?? 0) > 0) {
            $stored      = $file->store('receipts');
            $receiptName = basename($stored);
        }
        if ($this->request->getPost('remove_receipt') === '1') {
            $receiptName = null;
        }

        $updateData = [
            'category'     => $category,
            'amount'       => (string) $this->request->getPost('amount'),
            'description'  => (string) $this->request->getPost('description'),
            'retailor'     => trim((string) $this->request->getPost('retailor')) ?: null,
            'date_of_purchase' => (string) $this->request->getPost('date_of_purchase') ?: null,
            'purchased_by' => $purchasedById,
            'receipt_path' => $receiptName,
            'updated_by'   => $userId,
        ];

        if ($isDonation) {
            $updateData['status']           = 'approved';
            $updateData['status_reason']    = 'Marked as Donation (non-reimbursable).';
            $updateData['approved_by']      = $userId ?: null;
            $updateData['reimbursement_id'] = null;
        } elseif (($expense['category'] ?? '') === 'Donation') {
            // Moving a donation back to a reimbursable category: clear the marker.
            $updateData['status_reason'] = null;
            $updateData['approved_by']   = $expense['approved_by'] ?? null;
            $updateData['status']        = $expense['status'] ?? 'pending';
        }

        $this->expenseModel->update($id, $updateData);

        return redirect()->to('/expenses/index')->with('success', 'Expense updated.');
    }
}
