<?php
/**
 * Login Endpoint
 * Handles user authentication for Ton Sui Mining
 */

// Define TONSUI_LOADED constant to prevent direct access to session_init.php
define('TONSUI_LOADED', true);
// Allow guest access for this public endpoint
if (!defined('TONSUI_ALLOW_GUEST')) {
    define('TONSUI_ALLOW_GUEST', true);
}

// Dev-friendly: Return JSON for fatal errors (even if they happen during includes)
if (!function_exists('tonsui_register_fatal_json')) {
    function tonsui_register_fatal_json() {
        @ini_set('display_errors', '0');
        @error_reporting(E_ALL);
        register_shutdown_function(function () {
            $e = error_get_last();
            if ($e && in_array($e['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
                // Determine debug flag without assuming autoload
                $debug = $_ENV['APP_DEBUG'] ?? $_SERVER['APP_DEBUG'] ?? getenv('APP_DEBUG') ?? '0';
                $envPath = __DIR__ . '/includes/Env.php';
                if (is_file($envPath)) {
                    require_once $envPath;
                    if (class_exists('TonEnv')) { $debug = TonEnv::get('APP_DEBUG', $debug); }
                }
                if (!headers_sent()) {
                    header('Content-Type: application/json');
                    http_response_code(500);
                }
                $payload = [
                    'success' => false,
                    'message' => 'An internal error occurred. Please try again.'
                ];
                if ($debug === '1' || strtolower((string)$debug) === 'true') {
                    $payload['error'] = $e['message'];
                    $payload['file'] = $e['file'];
                    $payload['line'] = $e['line'];
                }
                echo json_encode($payload);
            }
        });
    }
    tonsui_register_fatal_json();
}

// Include required files
require_once __DIR__ . '/session_init.php';
require_once __DIR__ . '/auth.php';
require_once __DIR__ . '/db.php';
require_once __DIR__ . '/csrf.php';
require_once __DIR__ . '/includes/Mailer.php';
require_once __DIR__ . '/includes/Env.php';
require_once __DIR__ . '/email_helper.php';

// Set headers
header('Content-Type: application/json');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
header('Referrer-Policy: same-origin');
// Diagnostic headers to fingerprint endpoint and build
header('X-Ton-Endpoint: login');
header('X-Ton-Version: ' . (@filemtime(__FILE__) ?: 'unknown'));

// CORS: Reflect exact origin when credentials are used
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (!empty($origin)) {
    header('Access-Control-Allow-Origin: ' . $origin);
} else {
    $scheme = $_SERVER['REQUEST_SCHEME'] ?? 'https';
    $host = $_SERVER['HTTP_HOST'] ?? '';
    if (!empty($host)) {
        header('Access-Control-Allow-Origin: ' . $scheme . '://' . $host);
    }
}
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');

// Handle preflight
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}

// Function to log security events
function logSecurityEvent($event, $userId = null, $ip = null, $userAgent = null) {
    global $pdo;
    
    $ip = $ip ?? $_SERVER['REMOTE_ADDR'] ?? 'unknown';
    $userAgent = $userAgent ?? $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
    
    error_log("Security event: $event, User: $userId, IP: $ip");
    
    // Log to database if table exists
    try {
        $stmt = $pdo->prepare("SHOW TABLES LIKE 'security_logs'");
        $stmt->execute();
        if ($stmt->rowCount() > 0) {
            $pdo->prepare("
                INSERT INTO security_logs (user_id, action, ip_address, user_agent, created_at) 
                VALUES (?, ?, ?, ?, NOW())
            ")->execute([$userId, $event, $ip, $userAgent]);
        }
    } catch (PDOException $e) {
        error_log('Error writing to security log: ' . $e->getMessage());
    }
}

// Initialize database connection
initAuth();

// Function to respond with JSON (guarded to prevent redeclaration)
if (!function_exists('respond')) {
    function respond($success, $message = '', $data = []) {
        $response = ['success' => $success];
        if ($message) $response['message'] = $message;
        if (!empty($data)) $response = array_merge($response, $data);
        echo json_encode($response);
        exit;
    }
}

// Handle login request
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Get JSON input
    $input = json_decode(file_get_contents('php://input'), true);
    $identity = isset($input['identity']) ? trim($input['identity']) : (isset($input['username']) ? trim($input['username']) : '');
    $password = $input['password'] ?? '';
    
    // Validate input
    if (empty($identity) || empty($password)) {
        http_response_code(400);
        respond(false, 'Username/email and password are required.');
    }
    
    try {
        // Detect optional account_number column
        $hasAccountNumber = false;
        try {
            $colStmt = $pdo->query("SHOW COLUMNS FROM users LIKE 'account_number'");
            $hasAccountNumber = $colStmt && $colStmt->rowCount() > 0;
        } catch (PDOException $e) { $hasAccountNumber = false; }

        // Find user by username, email, or account_number (when available)
        if ($hasAccountNumber) {
            $stmt = $pdo->prepare("\n                SELECT * FROM users \n                WHERE username = :identity1 OR email = :identity2 OR account_number = :identity3 \n                LIMIT 1\n            ");
            $stmt->execute([':identity1' => $identity, ':identity2' => $identity, ':identity3' => $identity]);
        } else {
            // Use two distinct placeholders when emulate prepares is off
            $stmt = $pdo->prepare("\n                SELECT * FROM users \n                WHERE username = :identity1 OR email = :identity2 \n                LIMIT 1\n            ");
            $stmt->execute([':identity1' => $identity, ':identity2' => $identity]);
        }
        
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$user) {
            logSecurityEvent('login_failed_nonexistent', null, null, $identity);
            respond(false, 'Invalid username or password');
        }
        
        // Verify password
        if (!password_verify($password, $user['password'])) {
            logSecurityEvent('login_failed_invalid_password', $user['id']);
            respond(false, 'Invalid username or password');
        }
        
        // Check if account is active flag
        if (isset($user['is_active']) && !$user['is_active']) {
            logSecurityEvent('login_failed_inactive', $user['id']);
            respond(false, 'Account is inactive. Please contact support.');
        }

        // Enforce status gating (pending users cannot login until approved)
        $isAdminFlag = !empty($user['is_admin']) && (int)$user['is_admin'] === 1;
        try {
            $hasStatusCol = false;
            $colS = $pdo->query("SHOW COLUMNS FROM users LIKE 'status'");
            $hasStatusCol = $colS && $colS->rowCount() > 0;
            if ($hasStatusCol && !$isAdminFlag) {
                $ustatus = strtolower(trim((string)($user['status'] ?? '')));
                if ($ustatus !== '' && $ustatus !== 'active') {
                    logSecurityEvent('login_blocked_user_status_'.$ustatus, $user['id']);
                    respond(false, 'Your application is under review. You will be notified once approved.', ['error_code' => 'APPLICATION_SUBMITTED']);
                }
            }
        } catch (PDOException $e) { /* ignore */ }
        
        // Block login until application is approved (non-admin users)
        if (!$isAdminFlag) {
            try {
                $appStmt = $pdo->prepare("SELECT status FROM user_applications WHERE user_id = ? ORDER BY created_at DESC LIMIT 1");
                $appStmt->execute([$user['id']]);
                $app = $appStmt->fetch(PDO::FETCH_ASSOC);
                if ($app) {
                    $status = strtolower($app['status']);
                    if ($status !== 'approved') {
                        $msg = $status === 'rejected' ? 'Your application was declined. Please contact support.' : 'Your application is under review. You will be notified once approved.';
                        logSecurityEvent('login_blocked_application_'.$status, $user['id']);
                        respond(false, $msg, ['error_code' => 'APPLICATION_'.strtoupper($status)]);
                    }
                }
            } catch (Exception $e) { /* ignore */ }
        }

        // Block login if account is suspended (schema-aware)
        $hasSusp = false;
        try {
            $c = $pdo->query("SHOW COLUMNS FROM users LIKE 'account_suspended'");
            $hasSusp = $c && $c->rowCount() > 0;
        } catch (PDOException $e) { $hasSusp = false; }
        if ($hasSusp && intval($user['account_suspended'] ?? 0) === 1) {
            logSecurityEvent('login_failed_suspended', $user['id']);
            respond(false, 'Account is suspended. Please contact support.', ['error_code' => 'ACCOUNT_SUSPENDED']);
        }
        
        if (!$isAdminFlag) {
            try {
                $otp = (string)random_int(100000, 999999);
            } catch (Exception $e) {
                $otp = (string)mt_rand(100000, 999999);
            }
            $_SESSION['pending_login'] = [
                'user_id' => (int)$user['id'],
                'username' => (string)$user['username'],
                'email' => (string)$user['email'],
                'otp' => $otp,
                'expires' => time() + 3600
            ];
            // Prefer centralized template engine for branding and templates
            $sent = false;
            try {
                $res = @sendEmail($pdo, $user['email'], 'loginOtp', [
                    'user_name' => ($user['name'] ?? $user['username']),
                    'otp_code' => $otp
                ]);
                if (is_array($res) && !empty($res['success'])) { $sent = true; }
            } catch (Exception $e) { $sent = false; }
            // Fallback to direct PHPMailer if helper fails
            if (!$sent) {
                $envHost = TonEnv::get('MAILER_HOST', '');
                $envUser = TonEnv::get('MAILER_USERNAME', '');
                $envPass = TonEnv::get('MAILER_PASSWORD', '');
                $envPort = (int)TonEnv::get('MAILER_PORT', '587');
                $envFrom = TonEnv::get('MAILER_FROM', $envUser);
                $envFromName = TonEnv::get('MAILER_FROM_NAME', 'First De France Bank');
                $envEnc = strtolower((string)TonEnv::get('MAILER_ENCRYPTION', ''));
                if ($envHost && $envUser && $envPass) {
                    $m = new PHPMailer\PHPMailer\PHPMailer(true);
                    $m->isSMTP();
                    $m->Host = $envHost;
                    $m->SMTPAuth = true;
                    $m->Username = $envUser;
                    $m->Password = $envPass;
                    if ($envEnc === 'ssl' || $envPort === 465) {
                        $m->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS;
                    } else {
                        $m->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
                    }
                    $m->Port = $envPort ?: 587;
                    $m->setFrom($envFrom ?: $envUser, $envFromName);
                    $m->addAddress($user['email']);
                    $m->isHTML(true);
                    $m->Subject = 'Your login verification code';
                    $m->Body = '<p>Your one-time login code is:</p><h2 style="letter-spacing:2px;">' . htmlspecialchars($otp) . '</h2><p>This code expires in 60 minutes.</p>';
                    $m->AltBody = 'Your one-time login code: ' . $otp . '\nThis code expires in 60 minutes.';
                    $m->send();
                    $sent = true;
                }
            }
            if (!$sent) {
                $cfg = getEmailConfig($pdo);
                if ($cfg && !empty($cfg['smtp']) && !empty($cfg['smtp']['enabled'])) {
                    try {
                        $smtp = $cfg['smtp'];
                        $m = new PHPMailer\PHPMailer\PHPMailer(true);
                        $m->isSMTP();
                        $m->Host = $smtp['host'];
                        $m->SMTPAuth = true;
                        $m->Username = $smtp['username'];
                        $m->Password = $smtp['password'];
                        $m->SMTPSecure = $smtp['encryption'] === 'ssl' ? PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS : PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
                        $m->Port = $smtp['port'];
                        $m->setFrom($smtp['fromEmail'], $smtp['fromName']);
                        $m->addAddress($user['email']);
                        $m->isHTML(true);
                        $m->Subject = 'Your login verification code';
                        $m->Body = '<p>Your one-time login code is:</p><h2 style="letter-spacing:2px;">' . htmlspecialchars($otp) . '</h2><p>This code expires in 60 minutes.</p>';
                        $m->AltBody = 'Your one-time login code: ' . $otp . '\nThis code expires in 60 minutes.';
                        $m->send();
                        $sent = true;
                    } catch (Exception $e) { $sent = false; }
                }
            }
            if (!$sent) {
                $mailer = new Mailer();
                @$mailer->sendEmail($user['email'], 'Your login verification code', '<p>Your one-time login code is:</p><h2 style="letter-spacing:2px;">' . htmlspecialchars($otp) . '</h2><p>This code expires in 60 minutes.</p>', 'Your one-time login code: ' . $otp);
            }
            respond(true, 'Verification code sent.', ['otp_required' => true]);
        }
        $_SESSION['user_id'] = $user['id'];
        $_SESSION['username'] = $user['username'];
        $_SESSION['email'] = $user['email'];
        $_SESSION['is_admin'] = !empty($user['is_admin']);
        $_SESSION['logged_in'] = true;
        $_SESSION['last_activity'] = time();
        session_regenerate_id(true);
        logSecurityEvent('login_success', $user['id']);

        // Ensure admin session flags are set when applicable
        $isAdmin = false;
        if (!empty($user['is_admin']) && (int)$user['is_admin'] === 1) {
            try {
                // Detect whether admins table has a user_id column
                $hasUserIdCol = false;
                try {
                    $colStmt = $pdo->query("SHOW COLUMNS FROM admins LIKE 'user_id'");
                    $hasUserIdCol = $colStmt && $colStmt->rowCount() > 0;
                } catch (PDOException $e) {
                    $hasUserIdCol = false;
                }

                // Try to find existing admin record
                if ($hasUserIdCol) {
                    $adminStmt = $pdo->prepare('SELECT * FROM admins WHERE user_id = ? LIMIT 1');
                    $adminStmt->execute([$user['id']]);
                } else {
                    $adminStmt = $pdo->prepare('SELECT * FROM admins WHERE username = ? OR email = ? LIMIT 1');
                    $adminStmt->execute([$user['username'], $user['email']]);
                }
                $adminData = $adminStmt->fetch(PDO::FETCH_ASSOC);

                // Create admin record if missing
                if (!$adminData) {
                    if ($hasUserIdCol) {
                        $createSql = 'INSERT INTO admins (user_id, username, name, email, status, created_at) VALUES (?, ?, ?, ?, ?, NOW())';
                        $createParams = [$user['id'], $user['username'], $user['name'] ?? $user['username'], $user['email'], 'active'];
                    } else {
                        $createSql = 'INSERT INTO admins (name, username, email, password, is_admin, status, created_at) VALUES (?, ?, ?, ?, 1, ?, NOW())';
                        $createParams = [$user['name'] ?? $user['username'], $user['username'], $user['email'], $user['password'], 'active'];
                    }

                    try {
                        $createAdminStmt = $pdo->prepare($createSql);
                        $createAdminStmt->execute($createParams);
                        $adminId = (int)$pdo->lastInsertId();
                        $adminData = [
                            'id' => $adminId,
                            'username' => $user['username'],
                            'email' => $user['email'],
                            'name' => $user['name'] ?? $user['username'],
                        ];
                        logSecurityEvent('admin_account_created', $user['id']);
                    } catch (PDOException $e) {
                        error_log("Failed to create admin record: " . $e->getMessage());
                        $adminData = null;
                    }
                }

                if ($adminData) {
                    $_SESSION['admin_logged_in'] = true;
                    $_SESSION['admin_id'] = (int)$adminData['id'];
                    $_SESSION['admin_username'] = $adminData['username'] ?? $user['username'];
                    $_SESSION['admin_email'] = $adminData['email'] ?? $user['email'];
                    $_SESSION['admin_name'] = $adminData['name'] ?? ($user['name'] ?? $user['username']);
                    $_SESSION['last_activity'] = time();
                    $isAdmin = true;
                    logSecurityEvent('admin_login', $user['id']);
                }
            } catch (PDOException $e) {
                error_log("Admin check error: " . $e->getMessage());
            }
        }

        // Return user data (without sensitive info)
        $userData = [
            'id' => $user['id'],
            'username' => $user['username'],
            'email' => $user['email'],
            'is_admin' => $isAdmin,
            'session_timeout' => 1800 // 30 minutes
        ];

        respond(true, 'Login successful', ['user' => $userData]);
        
    } catch (PDOException $e) {
        error_log('Database error during login: ' . $e->getMessage());
        $debug = class_exists('TonEnv') ? TonEnv::get('APP_DEBUG', '0') : ($_ENV['APP_DEBUG'] ?? '0');
        if ($debug === '1' || strtolower((string)$debug) === 'true') {
            respond(false, 'A database error occurred. Please try again later.', ['error' => $e->getMessage()]);
        } else {
            respond(false, 'A database error occurred. Please try again later.');
        }
    } catch (Exception $e) {
        error_log('Error during login: ' . $e->getMessage());
        $debug = class_exists('TonEnv') ? TonEnv::get('APP_DEBUG', '0') : ($_ENV['APP_DEBUG'] ?? '0');
        if ($debug === '1' || strtolower((string)$debug) === 'true') {
            respond(false, 'An error occurred. Please try again.', ['error' => $e->getMessage()]);
        } else {
            respond(false, 'An error occurred. Please try again.');
        }
    }
} else {
    // Not a POST request
    http_response_code(405);
    header('Allow: POST');
    respond(false, 'Method not allowed');
}
