<?php

declare(strict_types=1);

class DashboardController
{
    private static function collectDownlines(int $parentId, int $level = 1, int $maxDepth = 6, array &$visited = []): array
    {
        if ($level > $maxDepth || isset($visited[$parentId]) || !table_exists('users')) {
            return [];
        }

        $visited[$parentId] = true;

        $usernameCol = column_exists('users', 'username') ? 'username' : 'id';
        $stmt = db()->prepare("SELECT id, name, {$usernameCol} AS username, status, created_at, ref_by FROM users WHERE ref_by = :pid ORDER BY id DESC");
        $stmt->execute(['pid' => $parentId]);
        $children = $stmt->fetchAll();

        $rows = [];
        foreach ($children as $child) {
            $parentName = '';
            if (!empty($child['ref_by'])) {
                $pstmt = db()->prepare("SELECT {$usernameCol} AS username, name FROM users WHERE id = :id LIMIT 1");
                $pstmt->execute(['id' => (int) $child['ref_by']]);
                $parent = $pstmt->fetch();
                $parentName = (string) ($parent['username'] ?? $parent['name'] ?? '');
            }

            $rows[] = [
                'id' => (int) $child['id'],
                'name' => (string) ($child['name'] ?? ''),
                'username' => (string) ($child['username'] ?? ''),
                'status' => (string) ($child['status'] ?? ''),
                'created_at' => (string) ($child['created_at'] ?? ''),
                'level' => $level,
                'parent_name' => $parentName,
            ];

            $nested = self::collectDownlines((int) $child['id'], $level + 1, $maxDepth, $visited);
            foreach ($nested as $item) {
                $rows[] = $item;
            }
        }

        return $rows;
    }

    private static function expirationDate(string $expiration): string
    {
        $expiration = trim($expiration);
        if ($expiration === '') {
            return date('Y-m-d H:i:s', strtotime('+30 days'));
        }

        $parts = preg_split('/\s+/', $expiration);
        if (!$parts || count($parts) < 2) {
            return date('Y-m-d H:i:s', strtotime('+30 days'));
        }

        $digit = (int) ($parts[0] ?? 30);
        $unit = strtolower((string) ($parts[1] ?? 'days'));

        if ($digit <= 0) {
            $digit = 30;
        }

        $normalizedUnit = match (true) {
            str_contains($unit, 'day') => 'days',
            str_contains($unit, 'week') => 'weeks',
            str_contains($unit, 'month') => 'months',
            str_contains($unit, 'year') => 'years',
            str_contains($unit, 'hour') => 'hours',
            default => 'days',
        };

        return date('Y-m-d H:i:s', strtotime('+' . $digit . ' ' . $normalizedUnit));
    }

    public static function dashboard(): void
    {
        $user = require_auth();
        $uid = (int) $user['id'];

        $deposited = 0.0;
        if (table_exists('deposits')) {
            $stmt = db()->prepare("SELECT COALESCE(SUM(amount), 0) FROM deposits WHERE user = :uid AND status = 'Processed'");
            $stmt->execute(['uid' => $uid]);
            $deposited = to_float($stmt->fetchColumn());
        }

        $withdrawn = 0.0;
        if (table_exists('withdrawals')) {
            $stmt = db()->prepare("SELECT COALESCE(SUM(amount), 0) FROM withdrawals WHERE user = :uid AND status = 'Processed'");
            $stmt->execute(['uid' => $uid]);
            $withdrawn = to_float($stmt->fetchColumn());
        }

        $totalPlans = 0;
        $activePlans = 0;
        if (table_exists('user_plans')) {
            $stmt = db()->prepare('SELECT COUNT(*) FROM user_plans WHERE user = :uid');
            $stmt->execute(['uid' => $uid]);
            $totalPlans = (int) $stmt->fetchColumn();

            $activeColumn = user_active_column();
            if (column_exists('user_plans', $activeColumn)) {
                $stmt = db()->prepare("SELECT COUNT(*) FROM user_plans WHERE user = :uid AND {$activeColumn} = 'yes'");
                $stmt->execute(['uid' => $uid]);
                $activePlans = (int) $stmt->fetchColumn();
            }
        }

        render('pages.dashboard', [
            'title' => 'Dashboard',
            'user' => $user,
            'deposited' => $deposited,
            'withdrawn' => $withdrawn,
            'totalPlans' => $totalPlans,
            'activePlans' => $activePlans,
        ]);
    }

    public static function plans(): void
    {
        $user = require_auth();

        $plans = [];
        try {
            $plans = db()->query('SELECT * FROM plans ORDER BY id ASC')->fetchAll();
        } catch (Throwable) {
            try {
                // Fallback for installs where the table name differs.
                $plans = db()->query('SELECT * FROM investment_plans ORDER BY id ASC')->fetchAll();
            } catch (Throwable) {
                $plans = [];
            }
        }

        render('pages.buy-plan', [
            'title' => 'Buy Plan',
            'user' => $user,
            'plans' => $plans,
        ]);
    }

    public static function joinPlan(): void
    {
        csrf_verify();
        $user = require_auth();
        $uid = (int) $user['id'];

        $planId = (int) ($_POST['id'] ?? 0);
        $investAmount = to_float($_POST['iamount'] ?? 0);

        if ($planId <= 0 || $investAmount <= 0) {
            set_flash('message', 'Invalid plan or amount.');
            redirect('/dashboard/buy-plan');
        }

        $stmt = db()->prepare('SELECT * FROM plans WHERE id = :id LIMIT 1');
        $stmt->execute(['id' => $planId]);
        $plan = $stmt->fetch();

        if (!$plan) {
            set_flash('message', 'Selected plan not found.');
            redirect('/dashboard/buy-plan');
        }

        $min = to_float($plan['min_price'] ?? 0);
        $max = to_float($plan['max_price'] ?? 0);
        if ($min > 0 && $investAmount < $min) {
            set_flash('message', 'Amount is below this plan minimum.');
            redirect('/dashboard/buy-plan');
        }
        if ($max > 0 && $investAmount > $max) {
            set_flash('message', 'Amount exceeds this plan maximum.');
            redirect('/dashboard/buy-plan');
        }

        $balance = to_float($user['account_bal'] ?? 0);
        if ($balance < $investAmount) {
            set_flash('message', 'Insufficient account balance. Please fund your account.');
            redirect('/dashboard/deposits');
        }

        $now = now_sql();
        $endAt = self::expirationDate((string) ($plan['expiration'] ?? '30 Days'));
        $activeColumn = user_active_column();
        $upUserColumn = first_existing_column('user_plans', ['user', 'user_id', 'uid', 'user_table'], 'user');
        $upPlanColumn = first_existing_column('user_plans', ['plan', 'plan_id', 'pid'], 'plan');

        if (!column_exists('user_plans', $upUserColumn) || !column_exists('user_plans', $upPlanColumn)) {
            set_flash('message', 'user_plans table is missing required columns (user/plan mapping).');
            redirect('/dashboard/buy-plan');
        }

        db()->beginTransaction();
        try {
            update_row('users', [
                'account_bal' => $balance - $investAmount,
                'plan' => $planId,
                'entered_at' => $now,
                'updated_at' => $now,
            ], 'id = :id', ['id' => $uid]);

            $userPlanId = insert_row('user_plans', [
                $upPlanColumn => $planId,
                $upUserColumn => $uid,
                'amount' => $investAmount,
                $activeColumn => 'yes',
                'inv_duration' => (string) ($plan['expiration'] ?? '30 Days'),
                'expire_date' => $endAt,
                'activated_at' => $now,
                'last_growth' => $now,
                'created_at' => $now,
                'updated_at' => $now,
            ]);

            if ($userPlanId > 0 && column_exists('users', 'user_plan')) {
                update_row('users', ['user_plan' => $userPlanId], 'id = :id', ['id' => $uid]);
            }

            insert_row(transactions_table(), [
                'user' => $uid,
                'plan' => $plan['name'] ?? 'Plan',
                'amount' => $investAmount,
                'type' => 'Plan purchase',
                'created_at' => $now,
                'updated_at' => $now,
            ]);

            $gift = to_float($plan['gift'] ?? 0);
            if ($gift > 0) {
                $updatedUserStmt = db()->prepare('SELECT account_bal, bonus FROM users WHERE id = :id LIMIT 1');
                $updatedUserStmt->execute(['id' => $uid]);
                $updatedUser = $updatedUserStmt->fetch() ?: $user;

                update_row('users', [
                    'account_bal' => to_float($updatedUser['account_bal'] ?? 0) + $gift,
                    'bonus' => to_float($updatedUser['bonus'] ?? 0) + $gift,
                    'updated_at' => $now,
                ], 'id = :id', ['id' => $uid]);

                insert_row(transactions_table(), [
                    'user' => $uid,
                    'plan' => $plan['name'] ?? 'Plan',
                    'amount' => $gift,
                    'type' => 'Gift Bonus',
                    'created_at' => $now,
                    'updated_at' => $now,
                ]);
            }

            db()->commit();
        } catch (Throwable $e) {
            if (db()->inTransaction()) {
                db()->rollBack();
            }
            set_flash('message', 'Unable to complete plan purchase: ' . $e->getMessage());
            redirect('/dashboard/buy-plan');
        }

        set_flash('success', 'Plan purchased successfully.');
        redirect('/dashboard/myplans');
    }

    public static function deposits(): void
    {
        $user = require_auth();
        $uid = (int) $user['id'];

        if (is_post()) {
            csrf_verify();

            $amount = to_float($_POST['amount'] ?? 0);
            $method = trim((string) ($_POST['payment_method'] ?? ''));

            if ($amount <= 0 || $method === '') {
                set_flash('message', 'Amount and payment method are required.');
                redirect('/dashboard/deposits');
            }

            $now = now_sql();
            insert_row('deposits', [
                'txn_id' => 'DP' . strtoupper(bin2hex(random_bytes(4))),
                'user' => $uid,
                'uname' => $user['name'] ?? 'User',
                'amount' => $amount,
                'payment_mode' => $method,
                'plan' => 0,
                'status' => 'Pending',
                'created_at' => $now,
                'updated_at' => $now,
            ]);

            set_flash('success', 'Deposit request submitted successfully.');
            redirect('/dashboard/deposits');
        }

        $methods = [];
        if (table_exists('wdmethods')) {
            $query = "SELECT * FROM wdmethods WHERE (type = 'deposit' OR type = 'both' OR type IS NULL)";
            if (column_exists('wdmethods', 'status')) {
                $query .= " AND (status = 'enabled' OR status = 'Enabled' OR status IS NULL)";
            }
            $query .= ' ORDER BY id ASC';
            $methods = db()->query($query)->fetchAll();
        }

        $deposits = [];
        if (table_exists('deposits')) {
            $stmt = db()->prepare('SELECT * FROM deposits WHERE user = :uid ORDER BY id DESC');
            $stmt->execute(['uid' => $uid]);
            $deposits = $stmt->fetchAll();
        }

        render('pages.deposits', [
            'title' => 'Fund Account',
            'user' => $user,
            'methods' => $methods,
            'deposits' => $deposits,
        ]);
    }

    public static function withdrawals(): void
    {
        $user = require_auth();
        $uid = (int) $user['id'];

        if (is_post()) {
            csrf_verify();

            $amount = to_float($_POST['amount'] ?? 0);
            $method = trim((string) ($_POST['method'] ?? ''));
            $wallet = trim((string) ($_POST['wallet'] ?? 'account_bal'));
            $details = trim((string) ($_POST['details'] ?? ''));

            if ($amount <= 0 || $method === '') {
                set_flash('message', 'Amount and withdrawal method are required.');
                redirect('/dashboard/withdrawals');
            }

            $methodRow = null;
            if (table_exists('wdmethods')) {
                $stmt = db()->prepare('SELECT * FROM wdmethods WHERE name = :name LIMIT 1');
                $stmt->execute(['name' => $method]);
                $methodRow = $stmt->fetch();
            }

            if ($methodRow) {
                $minimum = to_float($methodRow['minimum'] ?? 0);
                if ($minimum > 0 && $amount < $minimum) {
                    set_flash('message', 'Amount is lower than this method minimum.');
                    redirect('/dashboard/withdrawals');
                }
            }

            $walletColumn = match ($wallet) {
                'roi' => column_exists('users', 'roi') ? 'roi' : 'account_bal',
                'ref_bonus' => column_exists('users', 'ref_bonus') ? 'ref_bonus' : 'account_bal',
                default => 'account_bal',
            };

            $sourceBalance = to_float($user[$walletColumn] ?? 0);

            $charges = 0.0;
            if ($methodRow) {
                $charges += to_float($methodRow['charges_fixed'] ?? 0);
                $charges += $amount * (to_float($methodRow['charges_percentage'] ?? 0) / 100);
            }

            $toDeduct = $amount + $charges;
            if ($sourceBalance < $toDeduct) {
                set_flash('message', 'Insufficient balance for this withdrawal request.');
                redirect('/dashboard/withdrawals');
            }

            $now = now_sql();

            db()->beginTransaction();
            try {
                update_row('users', [
                    $walletColumn => $sourceBalance - $toDeduct,
                    'updated_at' => $now,
                ], 'id = :id', ['id' => $uid]);

                insert_row('withdrawals', [
                    'txn_id' => 'WD' . strtoupper(bin2hex(random_bytes(4))),
                    'user' => $uid,
                    'uname' => $user['name'] ?? 'User',
                    'amount' => $amount,
                    'to_deduct' => $toDeduct,
                    'status' => 'Pending',
                    'payment_mode' => $method,
                    'paydetails' => $details,
                    'created_at' => $now,
                    'updated_at' => $now,
                ]);

                insert_row(transactions_table(), [
                    'user' => $uid,
                    'plan' => 'Withdrawal',
                    'amount' => $amount,
                    'type' => 'Withdrawal request',
                    'created_at' => $now,
                    'updated_at' => $now,
                ]);

                db()->commit();
            } catch (Throwable $e) {
                if (db()->inTransaction()) {
                    db()->rollBack();
                }
                set_flash('message', 'Unable to submit withdrawal: ' . $e->getMessage());
                redirect('/dashboard/withdrawals');
            }

            set_flash('success', 'Withdrawal request submitted successfully.');
            redirect('/dashboard/withdrawals');
        }

        $methods = [];
        if (table_exists('wdmethods')) {
            $query = "SELECT * FROM wdmethods WHERE (type = 'withdrawal' OR type = 'both' OR type IS NULL)";
            if (column_exists('wdmethods', 'status')) {
                $query .= " AND (status = 'enabled' OR status = 'Enabled' OR status IS NULL)";
            }
            $query .= ' ORDER BY id ASC';
            $methods = db()->query($query)->fetchAll();
        }

        $withdrawals = [];
        if (table_exists('withdrawals')) {
            $stmt = db()->prepare('SELECT * FROM withdrawals WHERE user = :uid ORDER BY id DESC');
            $stmt->execute(['uid' => $uid]);
            $withdrawals = $stmt->fetchAll();
        }

        render('pages.withdrawals', [
            'title' => 'Withdraw Funds',
            'user' => $user,
            'methods' => $methods,
            'withdrawals' => $withdrawals,
        ]);
    }

    public static function transactions(): void
    {
        $user = require_auth();
        $uid = (int) $user['id'];

        $deposits = [];
        if (table_exists('deposits')) {
            $stmt = db()->prepare('SELECT * FROM deposits WHERE user = :uid ORDER BY id DESC');
            $stmt->execute(['uid' => $uid]);
            $deposits = $stmt->fetchAll();
        }

        $withdrawals = [];
        if (table_exists('withdrawals')) {
            $stmt = db()->prepare('SELECT * FROM withdrawals WHERE user = :uid ORDER BY id DESC');
            $stmt->execute(['uid' => $uid]);
            $withdrawals = $stmt->fetchAll();
        }

        $transactions = [];
        $txTable = transactions_table();
        if (table_exists($txTable)) {
            $stmt = db()->prepare("SELECT * FROM {$txTable} WHERE user = :uid ORDER BY id DESC");
            $stmt->execute(['uid' => $uid]);
            $transactions = $stmt->fetchAll();
        }

        render('pages.transactions', [
            'title' => 'Transactions',
            'user' => $user,
            'deposits' => $deposits,
            'withdrawals' => $withdrawals,
            'transactions' => $transactions,
        ]);
    }

    public static function profitHistory(): void
    {
        $user = require_auth();
        $uid = (int) $user['id'];

        $records = [];
        $txTable = transactions_table();
        if (table_exists($txTable)) {
            $stmt = db()->prepare("SELECT * FROM {$txTable} WHERE user = :uid AND UPPER(type) = 'ROI' ORDER BY id DESC");
            $stmt->execute(['uid' => $uid]);
            $records = $stmt->fetchAll();
        }

        render('pages.profit-history', [
            'title' => 'Profit History',
            'user' => $user,
            'records' => $records,
        ]);
    }

    public static function referrals(): void
    {
        $user = require_auth();
        $uid = (int) $user['id'];

        $settings = settings();
        $siteAddress = trim((string) ($settings['site_address'] ?? ''), '/');

        $username = (string) ($user['username'] ?? '');
        if ($username === '') {
            $username = (string) $uid;
        }

        $refLink = (string) ($user['ref_link'] ?? '');
        if ($refLink === '') {
            if ($siteAddress !== '') {
                $refLink = 'https://' . $siteAddress . '/ref/' . $username;
            } else {
                $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
                $host = (string) ($_SERVER['HTTP_HOST'] ?? 'localhost');
                $refLink = $scheme . '://' . $host . '/ref/' . $username;
            }
        }

        $parentName = 'None';
        if (!empty($user['ref_by']) && table_exists('users')) {
            $usernameCol = column_exists('users', 'username') ? 'username' : 'name';
            $stmt = db()->prepare("SELECT {$usernameCol} AS pname FROM users WHERE id = :id LIMIT 1");
            $stmt->execute(['id' => (int) $user['ref_by']]);
            $parent = $stmt->fetch();
            if ($parent && !empty($parent['pname'])) {
                $parentName = (string) $parent['pname'];
            }
        }

        $visited = [];
        $downlines = self::collectDownlines($uid, 1, 6, $visited);

        render('pages.referrals', [
            'title' => 'Referrals',
            'user' => $user,
            'refLink' => $refLink,
            'parentName' => $parentName,
            'downlines' => $downlines,
        ]);
    }

    public static function accountSettings(): void
    {
        $user = require_auth();
        $uid = (int) $user['id'];

        if (is_post()) {
            csrf_verify();

            $formType = trim((string) ($_POST['form_type'] ?? 'profile'));
            $now = now_sql();

            if ($formType === 'profile') {
                update_row('users', [
                    'name' => trim((string) ($_POST['name'] ?? '')),
                    'phone' => trim((string) ($_POST['phone'] ?? '')),
                    'country' => trim((string) ($_POST['country'] ?? '')),
                    'address' => trim((string) ($_POST['address'] ?? '')),
                    'dob' => trim((string) ($_POST['dob'] ?? '')),
                    'updated_at' => $now,
                ], 'id = :id', ['id' => $uid]);

                set_flash('success', 'Profile updated successfully.');
                redirect('/dashboard/account-settings');
            }

            if ($formType === 'withdrawal') {
                update_row('users', [
                    'bank_name' => trim((string) ($_POST['bank_name'] ?? '')),
                    'account_name' => trim((string) ($_POST['account_name'] ?? '')),
                    'account_number' => trim((string) ($_POST['account_number'] ?? '')),
                    'swift_code' => trim((string) ($_POST['swift_code'] ?? '')),
                    'btc_address' => trim((string) ($_POST['btc_address'] ?? '')),
                    'eth_address' => trim((string) ($_POST['eth_address'] ?? '')),
                    'ltc_address' => trim((string) ($_POST['ltc_address'] ?? '')),
                    'updated_at' => $now,
                ], 'id = :id', ['id' => $uid]);

                set_flash('success', 'Withdrawal settings updated successfully.');
                redirect('/dashboard/account-settings');
            }

            if ($formType === 'password') {
                $current = (string) ($_POST['current_password'] ?? '');
                $new = (string) ($_POST['new_password'] ?? '');
                $confirm = (string) ($_POST['confirm_password'] ?? '');

                if ($new === '' || strlen($new) < 6) {
                    set_flash('message', 'New password must be at least 6 characters.');
                    redirect('/dashboard/account-settings');
                }

                if ($new !== $confirm) {
                    set_flash('message', 'Password confirmation does not match.');
                    redirect('/dashboard/account-settings');
                }

                if (!password_verify($current, (string) ($user['password'] ?? ''))) {
                    set_flash('message', 'Current password is incorrect.');
                    redirect('/dashboard/account-settings');
                }

                update_row('users', [
                    'password' => password_hash($new, PASSWORD_DEFAULT),
                    'updated_at' => $now,
                ], 'id = :id', ['id' => $uid]);

                set_flash('success', 'Password updated successfully.');
                redirect('/dashboard/account-settings');
            }
        }

        $freshUser = auth_user();
        render('pages.account-settings', [
            'title' => 'Account Settings',
            'user' => $freshUser ?? $user,
        ]);
    }

    public static function myPlans(): void
    {
        $user = require_auth();
        $uid = (int) $user['id'];

        $rows = [];
        if (table_exists('user_plans')) {
            $upUserColumn = first_existing_column('user_plans', ['user', 'user_id', 'uid', 'user_table'], 'user');
            $upPlanColumn = first_existing_column('user_plans', ['plan', 'plan_id', 'pid'], 'plan');

            if (column_exists('user_plans', $upUserColumn)) {
                if (table_exists('plans') && column_exists('user_plans', $upPlanColumn)) {
                    $sql = "SELECT up.*, p.name AS plan_name, p.increment_amount, p.increment_type, p.increment_interval, p.minr, p.maxr
                            FROM user_plans up
                            LEFT JOIN plans p ON p.id = up.`{$upPlanColumn}`
                            WHERE up.`{$upUserColumn}` = :uid
                            ORDER BY up.id DESC";
                    $stmt = db()->prepare($sql);
                    $stmt->execute(['uid' => $uid]);
                    $rows = $stmt->fetchAll();
                } else {
                    $sql = "SELECT * FROM user_plans WHERE `{$upUserColumn}` = :uid ORDER BY id DESC";
                    $stmt = db()->prepare($sql);
                    $stmt->execute(['uid' => $uid]);
                    $rows = $stmt->fetchAll();
                }
            }
        }

        render('pages.myplans', [
            'title' => 'My Plans',
            'user' => $user,
            'plans' => $rows,
            'activeColumn' => user_active_column(),
        ]);
    }
}


