<?php

declare(strict_types=1);

namespace App\Modules\Budget;

use App\Core\BaseController;
use App\Core\Request;
use App\Core\Session;

/**
 * BudgetController — HTTP controller handler for the Couple Wedding Budget Planner.
 *
 * @package Inveetaire
 * @ref     RC-017
 */
class BudgetController extends BaseController
{
    protected string $layout = 'couple';
    private BudgetModel $model;

    public function __construct()
    {
        parent::__construct();
        $this->model = new BudgetModel();
    }

    /**
     * Display the main Budget Dashboard.
     * Route: GET /app/couple/budget
     */
    public function index(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        
        // Ensure default categories are seeded for this workspace
        $this->model->ensureDefaultCategoriesExist($workspaceId);

        // Fetch statistics and lists
        $summary = $this->model->getSummaryStats($workspaceId);
        $categoryStats = $this->model->getCategorySummaryStats($workspaceId);
        $statusStats = $this->model->getExpenseStatusStats($workspaceId);
        $recentPayments = $this->model->getRecentPayments($workspaceId, 5);
        $upcomingDues = $this->model->getUpcomingDueDates($workspaceId, 5);
        $categories = $this->model->getCategories($workspaceId);

        $this->render('budget/dashboard', [
            'title' => 'Budget Planner',
            'summary' => $summary,
            'category_stats' => $categoryStats,
            'status_stats' => $statusStats,
            'recent_payments' => $recentPayments,
            'upcoming_dues' => $upcomingDues,
            'categories' => $categories,
            'success' => Session::get('success') ?? null,
            'error' => Session::get('error') ?? null
        ]);

        Session::set('success', null);
        Session::set('error', null);
    }

    /**
     * Update workspace total budget and currency settings.
     * Route: POST /app/couple/budget/settings
     */
    public function updateSettings(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $postData = $request->allPost();

        $rawBudget = $postData['total_budget'] ?? 0.0;
        if (is_string($rawBudget)) {
            $rawBudget = str_replace(['.', ','], ['', '.'], $rawBudget);
        }
        $totalBudget = (float) $rawBudget;
        $currency = trim((string) ($postData['currency'] ?? 'IDR'));
        $activeCategories = $postData['active_categories'] ?? [];

        if ($totalBudget < 0.0) {
            $this->flash('error', 'Total Budget limit cannot be negative.');
            $this->redirect('/app/couple/budget/settings');
            return;
        }

        try {
            $this->model->saveSettings($workspaceId, $totalBudget, $currency);
            $this->model->updateActiveCategories($workspaceId, array_map('intval', $activeCategories));
            $this->flash('success', 'Planning Settings updated successfully.');
        } catch (\Throwable $e) {
            $this->flash('error', 'Failed to update Planning Settings: ' . $e->getMessage());
            $this->redirect('/app/couple/budget/settings');
            return;
        }

        $this->redirect('/app/couple/budget');
    }

    /**
     * Render the categories management page.
     * Route: GET /app/couple/budget/categories
     */
    public function categories(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $categories = $this->model->getCategories($workspaceId);

        $this->render('budget/categories', [
            'title' => 'Category Management',
            'categories' => $categories,
            'success' => Session::get('success') ?? null,
            'error' => Session::get('error') ?? null
        ]);

        Session::set('success', null);
        Session::set('error', null);
    }

    /**
     * Create a new category.
     * Route: POST /app/couple/budget/categories/create
     */
    public function createCategory(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $name = trim((string) ($request->allPost()['name'] ?? ''));

        if ($name === '') {
            $this->flash('error', 'Category name cannot be empty.');
        } else {
            $this->model->createCategory($workspaceId, $name);
            $this->flash('success', "Category '{$name}' created successfully.");
        }

        $this->redirect('/app/couple/budget/categories');
    }

    /**
     * Rename an existing category.
     * Route: POST /app/couple/budget/categories/rename/{id}
     */
    public function renameCategory(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $id = (int) $request->param('id');
        $name = trim((string) ($request->allPost()['name'] ?? ''));

        if ($name === '') {
            $this->flash('error', 'Category name cannot be empty.');
        } else {
            $this->model->renameCategory($workspaceId, $id, $name);
            $this->flash('success', 'Category renamed successfully.');
        }

        $this->redirect('/app/couple/budget/categories');
    }

    /**
     * Soft delete an existing category.
     * Route: POST /app/couple/budget/categories/delete/{id}
     */
    public function deleteCategory(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $id = (int) $request->param('id');

        try {
            $this->model->deleteCategory($workspaceId, $id);
            $this->flash('success', 'Category and its related expense items deleted successfully.');
        } catch (\Throwable $e) {
            $this->flash('error', 'Failed to delete category: ' . $e->getMessage());
        }

        $this->redirect('/app/couple/budget/categories');
    }

    /**
     * Save categories sorting sort_order reordering via AJAX/POST.
     * Route: POST /app/couple/budget/categories/reorder
     */
    public function reorderCategories(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $postData = $request->allPost();
        
        $categoryIds = $postData['category_ids'] ?? [];
        if (!is_array($categoryIds) && is_string($categoryIds)) {
            $categoryIds = json_decode($categoryIds, true) ?? [];
        }

        if (!empty($categoryIds)) {
            $this->model->reorderCategories($workspaceId, $categoryIds);
            $this->json(['success' => true, 'message' => 'Categories reordered successfully.']);
        } else {
            $this->json([], 400, 'Invalid category IDs provided.');
        }
    }

    // ─────────────────────────────────────────────────────────────────────────
    // EXPENSES CRUD
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Show expense lists.
     * Route: GET /app/couple/budget/expenses
     */
    public function expenses(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');

        // Capture filter inputs
        $filters = [
            'category_id' => $request->get('category_id'),
            'status' => $request->get('status'),
            'search' => $request->get('search')
        ];

        $page = max(1, (int) $request->get('page', 1));
        $limit = 20;
        $offset = ($page - 1) * $limit;

        $expenses = $this->model->getExpenses($workspaceId, $filters, $limit, $offset);
        $totalExpenses = $this->model->getExpensesCount($workspaceId, $filters);
        $totalPages = (int) ceil($totalExpenses / $limit);

        $categories = $this->model->getCategories($workspaceId);
        $summary = $this->model->getSummaryStats($workspaceId);

        $this->render('budget/expenses', [
            'title' => 'Wedding Expenses',
            'expenses' => $expenses,
            'categories' => $categories,
            'filters' => $filters,
            'page' => $page,
            'total_pages' => $totalPages,
            'total_expenses' => $totalExpenses,
            'summary' => $summary,
            'success' => Session::get('success') ?? null,
            'error' => Session::get('error') ?? null
        ]);

        Session::set('success', null);
        Session::set('error', null);
    }

    /**
     * Render add expense view form.
     * Route: GET /app/couple/budget/expenses/create
     */
    public function createExpense(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $categories = $this->model->getCategories($workspaceId);

        $this->render('budget/create_expense', [
            'title' => 'Add Expense Item',
            'categories' => $categories,
            'errors' => Session::get('errors') ?? [],
            'old' => Session::get('old_input') ?? []
        ]);

        Session::set('errors', null);
        Session::set('old_input', null);
    }

    /**
     * Store new expense item.
     * Route: POST /app/couple/budget/expenses/create
     */
    public function storeExpense(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $postData = $request->allPost();

        $errors = [];
        if (empty($postData['name'])) {
            $errors['name'] = 'Expense description is required.';
        }
        if (empty($postData['category_id'])) {
            $errors['category_id'] = 'Expense category is required.';
        }

        if (!empty($errors)) {
            Session::set('errors', $errors);
            Session::set('old_input', $postData);
            $this->flash('error', 'Please resolve the validation errors.');
            $this->redirect('/app/couple/budget/expenses/create');
            return;
        }

        try {
            $this->model->createExpense($workspaceId, $postData);
            $this->flash('success', 'Expense item added successfully.');
            $this->redirect('/app/couple/budget/expenses');
        } catch (\Throwable $e) {
            $this->flash('error', 'Failed to add expense item: ' . $e->getMessage());
            Session::set('old_input', $postData);
            $this->redirect('/app/couple/budget/expenses/create');
        }
    }

    /**
     * Render edit form.
     * Route: GET /app/couple/budget/expenses/edit/{id}
     */
    public function editExpense(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $id = (int) $request->param('id');

        $expense = $this->model->getExpense($workspaceId, $id);
        if (!$expense) {
            $this->flash('error', 'Expense item not found.');
            $this->redirect('/app/couple/budget/expenses');
            return;
        }

        $categories = $this->model->getCategories($workspaceId);

        $this->render('budget/edit_expense', [
            'title' => 'Edit Expense Item',
            'expense' => $expense,
            'categories' => $categories,
            'errors' => Session::get('errors') ?? [],
            'old' => Session::get('old_input') ?? []
        ]);

        Session::set('errors', null);
        Session::set('old_input', null);
    }

    /**
     * Process edit updates.
     * Route: POST /app/couple/budget/expenses/edit/{id}
     */
    public function updateExpense(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $id = (int) $request->param('id');
        $postData = $request->allPost();

        $errors = [];
        if (empty($postData['name'])) {
            $errors['name'] = 'Expense description is required.';
        }
        if (empty($postData['category_id'])) {
            $errors['category_id'] = 'Expense category is required.';
        }

        if (!empty($errors)) {
            Session::set('errors', $errors);
            Session::set('old_input', $postData);
            $this->flash('error', 'Please resolve the validation errors.');
            $this->redirect("/app/couple/budget/expenses/edit/{$id}");
            return;
        }

        try {
            $this->model->updateExpense($workspaceId, $id, $postData);
            $this->flash('success', 'Expense item updated successfully.');
            $this->redirect("/app/couple/budget/expenses/{$id}");
        } catch (\Throwable $e) {
            $this->flash('error', 'Failed to update expense item: ' . $e->getMessage());
            Session::set('old_input', $postData);
            $this->redirect("/app/couple/budget/expenses/edit/{$id}");
        }
    }

    /**
     * Soft delete an expense item.
     * Route: POST /app/couple/budget/expenses/delete/{id}
     */
    public function deleteExpense(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $id = (int) $request->param('id');

        try {
            $this->model->deleteExpense($workspaceId, $id);
            $this->flash('success', 'Expense item deleted successfully.');
        } catch (\Throwable $e) {
            $this->flash('error', 'Failed to delete expense item: ' . $e->getMessage());
        }

        $this->redirect('/app/couple/budget/expenses');
    }

    /**
     * View expense detailed items and payments.
     * Route: GET /app/couple/budget/expenses/{id}
     */
    public function showExpense(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $id = (int) $request->param('id');

        $expense = $this->model->getExpense($workspaceId, $id);
        if (!$expense) {
            $this->flash('error', 'Expense item not found.');
            $this->redirect('/app/couple/budget/expenses');
            return;
        }

        $payments = $this->model->getPayments($workspaceId, $id, 100, 0);
        
        // Sum total paid
        $totalPaid = 0.0;
        foreach ($payments as $pay) {
            $totalPaid += (float) $pay['amount'];
        }
        $remainingBalance = max(0.0, ((float)$expense['actual_cost'] > 0 ? (float)$expense['actual_cost'] : (float)$expense['estimated_cost']) - $totalPaid);
        $summary = $this->model->getSummaryStats($workspaceId);

        $this->render('budget/detail', [
            'title' => 'Expense Details',
            'expense' => $expense,
            'payments' => $payments,
            'total_paid' => $totalPaid,
            'remaining_balance' => $remainingBalance,
            'summary' => $summary,
            'success' => Session::get('success') ?? null,
            'error' => Session::get('error') ?? null,
            'errors' => Session::get('errors') ?? [],
            'old' => Session::get('old_input') ?? []
        ]);

        Session::set('success', null);
        Session::set('error', null);
        Session::set('errors', null);
        Session::set('old_input', null);
    }

    // ─────────────────────────────────────────────────────────────────────────
    // PAYMENTS TRACKING
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Log a payment transaction.
     * Route: POST /app/couple/budget/expenses/{id}/payments
     */
    public function storePayment(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $expenseId = (int) $request->param('id');
        $postData = $request->allPost();

        $errors = [];
        $amount = (float) ($postData['amount'] ?? 0.0);
        $paymentDate = trim((string) ($postData['payment_date'] ?? ''));

        if ($amount <= 0.0) {
            $errors['amount'] = 'Payment amount must be greater than zero.';
        }
        if ($paymentDate === '') {
            $errors['payment_date'] = 'Payment date is required.';
        }

        if (!empty($errors)) {
            Session::set('errors', $errors);
            Session::set('old_input', $postData);
            $this->flash('error', 'Please resolve the payment validation errors.');
            $this->redirect("/app/couple/budget/expenses/{$expenseId}");
            return;
        }

        try {
            $this->model->createPayment(
                $workspaceId,
                $expenseId,
                $amount,
                $paymentDate,
                !empty($postData['payment_method']) ? trim($postData['payment_method']) : null,
                !empty($postData['reference_no']) ? trim($postData['reference_no']) : null,
                !empty($postData['notes']) ? trim($postData['notes']) : null
            );
            $this->flash('success', 'Payment transaction logged successfully.');
        } catch (\Throwable $e) {
            $this->flash('error', 'Failed to log payment transaction: ' . $e->getMessage());
            Session::set('old_input', $postData);
        }

        $this->redirect("/app/couple/budget/expenses/{$expenseId}");
    }

    /**
     * Delete a logged payment transaction.
     * Route: POST /app/couple/budget/payments/delete/{id}
     */
    public function deletePayment(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $id = (int) $request->param('id');

        try {
            // Find parent expense before delete
            $db = \App\Core\Database::getConnection();
            $stmt = $db->prepare('SELECT expense_id FROM budget_payments WHERE id = ? AND workspace_id = ? AND is_deleted = 0');
            $stmt->execute([$id, $workspaceId]);
            $pay = $stmt->fetch();
            
            if ($pay) {
                $expenseId = (int) $pay['expense_id'];
                $this->model->deletePayment($workspaceId, $id);
                $this->flash('success', 'Payment transaction removed successfully.');
                $this->redirect("/app/couple/budget/expenses/{$expenseId}");
                return;
            }
        } catch (\Throwable $e) {
            $this->flash('error', 'Failed to remove payment: ' . $e->getMessage());
        }

        $this->redirect('/app/couple/budget/expenses');
    }

    /**
     * Show list of all recent payments.
     * Route: GET /app/couple/budget/payments
     */
    public function payments(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');

        $page = max(1, (int) $request->get('page', 1));
        $limit = 25;
        $offset = ($page - 1) * $limit;

        $payments = $this->model->getPayments($workspaceId, null, $limit, $offset);
        $totalPayments = $this->model->getPaymentsCount($workspaceId, null);
        $totalPages = (int) ceil($totalPayments / $limit);
        $summary = $this->model->getSummaryStats($workspaceId);

        $this->render('budget/payments', [
            'title' => 'Payment History',
            'payments' => $payments,
            'page' => $page,
            'total_pages' => $totalPages,
            'total_payments' => $totalPayments,
            'summary' => $summary,
            'success' => Session::get('success') ?? null,
            'error' => Session::get('error') ?? null
        ]);

        Session::set('success', null);
        Session::set('error', null);
    }

    /**
     * Display Planning Settings.
     * Route: GET /app/couple/budget/settings
     */
    public function settings(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $settings = $this->model->getSettings($workspaceId);
        $categories = $this->model->getAllCategories($workspaceId);

        $this->render('budget/settings', [
            'title' => 'Planning Settings',
            'settings' => $settings,
            'categories' => $categories,
            'success' => Session::get('success') ?? null,
            'error' => Session::get('error') ?? null
        ]);

        Session::set('success', null);
        Session::set('error', null);
    }

    /**
     * Redirect to the category detail expense page.
     * Route: GET /app/couple/budget/category/{category_id}
     */
    public function categoryDetail(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $categoryId = (int) $request->param('category_id');

        $expense = $this->model->getCategoryExpense($workspaceId, $categoryId);
        if (!$expense) {
            $this->flash('error', 'Category details could not be loaded.');
            $this->redirect('/app/couple/budget');
            return;
        }

        $this->redirect('/app/couple/budget/expenses/' . $expense['id']);
    }
}
